diff --git a/src/Netscript/APIWrapper.ts b/src/Netscript/APIWrapper.ts index cb644b49b..ee7fd9d75 100644 --- a/src/Netscript/APIWrapper.ts +++ b/src/Netscript/APIWrapper.ts @@ -9,15 +9,16 @@ import { Settings } from "../Settings/Settings"; import { CONSTANTS } from "../Constants"; type ExternalFunction = (...args: any[]) => any; -type ExternalAPI = { +export type ExternalAPI = { [string: string]: ExternalAPI | ExternalFunction; }; type InternalFunction unknown> = (ctx: NetscriptContext) => F; + export type InternalAPI = { [Property in keyof API]: API[Property] extends ExternalFunction ? InternalFunction - : API[Property] extends ExternalAPI + : API[Property] extends object ? InternalAPI : never; }; @@ -42,9 +43,14 @@ type NetscriptHelpers = { number: (funcName: string, argName: string, v: unknown) => number; city: (funcName: string, argName: string, v: unknown) => CityName; boolean: (v: unknown) => boolean; - getServer: (hostname: string, callingFnName: string) => BaseServer; + getServer: (hostname: string, ctx: NetscriptContext) => BaseServer; checkSingularityAccess: (func: string) => void; - hack: (hostname: any, manual: any, { threads: requestedThreads, stock }?: any) => Promise; + hack: ( + ctx: NetscriptContext, + hostname: any, + manual: any, + { threads: requestedThreads, stock }?: any, + ) => Promise; getValidPort: (funcName: string, port: any) => IPort; }; @@ -87,7 +93,7 @@ function wrapFunction( number: (argName: string, v: unknown) => helpers.number(functionPath, argName, v), city: (argName: string, v: unknown) => helpers.city(functionPath, argName, v), boolean: helpers.boolean, - getServer: (hostname: string) => helpers.getServer(hostname, functionPath), + getServer: (hostname: string) => helpers.getServer(hostname, ctx), checkSingularityAccess: () => helpers.checkSingularityAccess(functionName), hack: helpers.hack, getValidPort: (port: any) => helpers.getValidPort(functionPath, port), diff --git a/src/Netscript/RamCostGenerator.ts b/src/Netscript/RamCostGenerator.ts index 620d67453..f14e1e80d 100644 --- a/src/Netscript/RamCostGenerator.ts +++ b/src/Netscript/RamCostGenerator.ts @@ -1,6 +1,18 @@ import { IPlayer } from "src/PersonObjects/IPlayer"; import { IMap } from "../types"; +import { NS as INS } from "../ScriptEditor/NetscriptDefinitions"; + +import { INetscriptExtra } from "../NetscriptFunctions/Extra"; + +type RamCostTree = { + [Property in keyof API]: API[Property] extends () => void + ? number | ((p: IPlayer) => void) + : API[Property] extends object + ? RamCostTree + : never; +}; + // TODO remember to update RamCalculations.js and WorkerScript.js // RAM costs for Netscript functions @@ -89,7 +101,7 @@ function SF4Cost(cost: number): (player: IPlayer) => number { } // Hacknet API -const hacknet: IMap = { +const hacknet = { numNodes: 0, purchaseNode: 0, getPurchaseNodeCost: 0, @@ -106,10 +118,15 @@ const hacknet: IMap = { hashCost: 0, spendHashes: 0, maxNumNodes: 0, + hashCapacity: 0, + getHashUpgrades: 0, + getHashUpgradeLevel: 0, + getStudyMult: 0, + getTrainingMult: 0, }; // Stock API -const stock: IMap = { +const stock = { getSymbols: RamCostConstants.ScriptGetStockRamCost, getPrice: RamCostConstants.ScriptGetStockRamCost, getAskPrice: RamCostConstants.ScriptGetStockRamCost, @@ -134,7 +151,7 @@ const stock: IMap = { }; // Singularity API -const singularity: IMap = { +const singularity = { universityCourse: SF4Cost(RamCostConstants.ScriptSingularityFn1RamCost), gymWorkout: SF4Cost(RamCostConstants.ScriptSingularityFn1RamCost), travelToCity: SF4Cost(RamCostConstants.ScriptSingularityFn1RamCost), @@ -190,7 +207,7 @@ const singularity: IMap = { }; // Gang API -const gang: IMap = { +const gang = { createGang: RamCostConstants.ScriptGangApiBaseRamCost / 4, inGang: RamCostConstants.ScriptGangApiBaseRamCost / 4, getMemberNames: RamCostConstants.ScriptGangApiBaseRamCost / 4, @@ -215,7 +232,7 @@ const gang: IMap = { }; // Bladeburner API -const bladeburner: IMap = { +const bladeburner = { getContractNames: RamCostConstants.ScriptBladeburnerApiBaseRamCost / 10, getOperationNames: RamCostConstants.ScriptBladeburnerApiBaseRamCost / 10, getBlackOpNames: RamCostConstants.ScriptBladeburnerApiBaseRamCost / 10, @@ -253,15 +270,13 @@ const bladeburner: IMap = { getBonusTime: 0, }; -const infiltration: IMap = { - calculateDifficulty: RamCostConstants.ScriptInfiltrationCalculateDifficulty, - calculateRewards: RamCostConstants.ScriptInfiltrationCalculateRewards, - calculateGetLocations: RamCostConstants.ScriptInfiltrationGetLocations, - calculateGetInfiltrations: RamCostConstants.ScriptInfiltrationGetInfiltrations, +const infiltration = { + getPossibleLocations: RamCostConstants.ScriptInfiltrationGetLocations, + getInfiltration: RamCostConstants.ScriptInfiltrationGetInfiltrations, }; // Coding Contract API -const codingcontract: IMap = { +const codingcontract = { attempt: RamCostConstants.ScriptCodingContractBaseRamCost, getContractType: RamCostConstants.ScriptCodingContractBaseRamCost / 2, getData: RamCostConstants.ScriptCodingContractBaseRamCost / 2, @@ -270,7 +285,7 @@ const codingcontract: IMap = { }; // Duplicate Sleeve API -const sleeve: IMap = { +const sleeve = { getNumSleeves: RamCostConstants.ScriptSleeveBaseRamCost, setToShockRecovery: RamCostConstants.ScriptSleeveBaseRamCost, setToSynchronize: RamCostConstants.ScriptSleeveBaseRamCost, @@ -290,7 +305,7 @@ const sleeve: IMap = { }; // Stanek API -const stanek: IMap = { +const stanek = { giftWidth: RamCostConstants.ScriptStanekWidth, giftHeight: RamCostConstants.ScriptStanekHeight, chargeFragment: RamCostConstants.ScriptStanekCharge, @@ -305,7 +320,7 @@ const stanek: IMap = { }; // UI API -const ui: IMap = { +const ui = { getTheme: 0, setTheme: 0, resetTheme: 0, @@ -313,17 +328,84 @@ const ui: IMap = { setStyles: 0, resetStyles: 0, getGameInfo: 0, + clearTerminal: 0, }; // Grafting API -const grafting: IMap = { +const grafting = { getAugmentationGraftPrice: 3.75, getAugmentationGraftTime: 3.75, getGraftableAugmentations: 5, graftAugmentation: 7.5, }; -export const RamCosts: IMap = { +const corporation = { + createCorporation: 0, + hasUnlockUpgrade: 0, + getUnlockUpgradeCost: 0, + getUpgradeLevel: 0, + getUpgradeLevelCost: 0, + getExpandIndustryCost: 0, + getExpandCityCost: 0, + getInvestmentOffer: 0, + acceptInvestmentOffer: 0, + goPublic: 0, + bribe: 0, + getCorporation: 0, + getDivision: 0, + expandIndustry: 0, + expandCity: 0, + unlockUpgrade: 0, + levelUpgrade: 0, + issueDividends: 0, + buyBackShares: 0, + sellShares: 0, + getBonusTime: 0, + sellMaterial: 0, + sellProduct: 0, + discontinueProduct: 0, + setSmartSupply: 0, + setSmartSupplyUseLeftovers: 0, + buyMaterial: 0, + bulkPurchase: 0, + getWarehouse: 0, + getProduct: 0, + getMaterial: 0, + setMaterialMarketTA1: 0, + setMaterialMarketTA2: 0, + setProductMarketTA1: 0, + setProductMarketTA2: 0, + exportMaterial: 0, + cancelExportMaterial: 0, + purchaseWarehouse: 0, + upgradeWarehouse: 0, + makeProduct: 0, + limitMaterialProduction: 0, + limitProductProduction: 0, + getPurchaseWarehouseCost: 0, + getUpgradeWarehouseCost: 0, + hasWarehouse: 0, + assignJob: 0, + hireEmployee: 0, + upgradeOfficeSize: 0, + throwParty: 0, + buyCoffee: 0, + hireAdVert: 0, + research: 0, + getOffice: 0, + getEmployee: 0, + getHireAdVertCost: 0, + getHireAdVertCount: 0, + getResearchCost: 0, + hasResearched: 0, + setAutoJobAssignment: 0, + getOfficeSizeUpgradeCost: 0, +}; + +const SourceRamCosts = { + args: undefined as unknown as never[], // special use case + enums: undefined as unknown as never, + corporation, hacknet, stock, singularity, @@ -363,7 +445,6 @@ export const RamCosts: IMap = { enableLog: 0, isLogEnabled: 0, getScriptLogs: 0, - clearTerminal: RamCostConstants.ScriptClearTerminalCost, nuke: RamCostConstants.ScriptPortProgramRamCost, brutessh: RamCostConstants.ScriptPortProgramRamCost, ftpcrack: RamCostConstants.ScriptPortProgramRamCost, @@ -382,7 +463,6 @@ export const RamCosts: IMap = { ps: RamCostConstants.ScriptScanRamCost, getRecentScripts: RamCostConstants.ScriptRecentScriptsRamCost, hasRootAccess: RamCostConstants.ScriptHasRootAccessRamCost, - getIp: RamCostConstants.ScriptGetHostnameRamCost, getHostname: RamCostConstants.ScriptGetHostnameRamCost, getHackingLevel: RamCostConstants.ScriptGetHackingLevelRamCost, getHackingMultipliers: RamCostConstants.ScriptGetMultipliersRamCost, @@ -439,12 +519,74 @@ export const RamCosts: IMap = { getOwnedSourceFiles: RamCostConstants.ScriptGetOwnedSourceFiles, tail: 0, toast: 0, + closeTail: 0, + clearPort: 0, + openDevMenu: 0, + alert: 0, + flags: 0, + exploit: 0, + bypass: 0, + alterReality: 0, + rainbow: 0, heart: { // Easter egg function break: 0, }, + + formulas: { + reputation: { + calculateFavorToRep: 0, + calculateRepToFavor: 0, + repFromDonation: 0, + }, + skills: { + calculateSkill: 0, + calculateExp: 0, + }, + hacking: { + hackChance: 0, + hackExp: 0, + hackPercent: 0, + growPercent: 0, + hackTime: 0, + growTime: 0, + weakenTime: 0, + }, + hacknetNodes: { + moneyGainRate: 0, + levelUpgradeCost: 0, + ramUpgradeCost: 0, + coreUpgradeCost: 0, + hacknetNodeCost: 0, + constants: 0, + }, + hacknetServers: { + hashGainRate: 0, + levelUpgradeCost: 0, + ramUpgradeCost: 0, + coreUpgradeCost: 0, + cacheUpgradeCost: 0, + hashUpgradeCost: 0, + hacknetServerCost: 0, + constants: 0, + }, + gang: { + wantedPenalty: 0, + respectGain: 0, + wantedLevelGain: 0, + moneyGain: 0, + ascensionPointsGain: 0, + ascensionMultiplier: 0, + }, + }, }; +export const RamCosts: IMap = SourceRamCosts; + +// This line in particular is there so typescript typechecks that we are not missing any static ram cost. +// eslint-disable-next-line @typescript-eslint/no-unused-vars +const _typecheck: RamCostTree = SourceRamCosts; + export function getRamCost(player: IPlayer, ...args: string[]): number { if (args.length === 0) { console.warn(`No arguments passed to getRamCost()`); diff --git a/src/NetscriptEvaluator.ts b/src/NetscriptEvaluator.ts index a1f1e96ad..96aceb460 100644 --- a/src/NetscriptEvaluator.ts +++ b/src/NetscriptEvaluator.ts @@ -2,6 +2,7 @@ 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 @@ -36,26 +37,22 @@ export function makeRuntimeRejectMsg(workerScript: WorkerScript, msg: string): s return "|DELIMITER|" + server.hostname + "|DELIMITER|" + workerScript.name + "|DELIMITER|" + msg; } -export function resolveNetscriptRequestedThreads( - workerScript: WorkerScript, - functionName: string, - requestedThreads: number, -): number { - const threads = workerScript.scriptRef.threads; +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( - workerScript, - `Invalid thread count passed to ${functionName}: ${requestedThreads}. Threads must be a positive number.`, + ctx.workerScript, + `Invalid thread count passed to ${ctx.function}: ${requestedThreads}. Threads must be a positive number.`, ); } if (requestedThreadsAsInt > threads) { throw makeRuntimeRejectMsg( - workerScript, - `Too many threads requested by ${functionName}. Requested: ${requestedThreads}. Has: ${threads}.`, + ctx.workerScript, + `Too many threads requested by ${ctx.function}. Requested: ${requestedThreads}. Has: ${threads}.`, ); } return requestedThreadsAsInt; diff --git a/src/NetscriptFunctions.ts b/src/NetscriptFunctions.ts index 6fbb948fe..b78f8eb6e 100644 --- a/src/NetscriptFunctions.ts +++ b/src/NetscriptFunctions.ts @@ -1,7 +1,6 @@ import $ from "jquery"; import { vsprintf, sprintf } from "sprintf-js"; -import { getRamCost } from "./Netscript/RamCostGenerator"; import { WorkerScriptStartStopEventEmitter } from "./Netscript/WorkerScriptStartStopEventEmitter"; import { BitNodeMultipliers } from "./BitNode/BitNodeMultipliers"; @@ -77,12 +76,10 @@ import { IPort } from "./NetscriptPort"; import { NS as INS, - Singularity as ISingularity, Player as INetscriptPlayer, Gang as IGang, Bladeburner as IBladeburner, Stanek as IStanek, - Sleeve as ISleeve, Infiltration as IInfiltration, RunningScript as IRunningScript, RecentScript as IRecentScript, @@ -94,12 +91,6 @@ import { BitNodeMultipliers as IBNMults, Server as IServerDef, RunningScript as IRunningScriptDef, - Grafting as IGrafting, - UserInterface as IUserInterface, - TIX as ITIX, - Corporation as ICorporation, - CodingContract as ICodingContract, - Hacknet as IHacknet, // ToastVariant, } from "./ScriptEditor/NetscriptDefinitions"; import { NetscriptSingularity } from "./NetscriptFunctions/Singularity"; @@ -115,7 +106,8 @@ import { calculateIntelligenceBonus } from "./PersonObjects/formulas/intelligenc import { CalculateShareMult, StartSharing } from "./NetworkShare/Share"; import { recentScripts } from "./Netscript/RecentScripts"; import { CityName } from "./Locations/data/CityNames"; -import { wrapAPI } from "./Netscript/APIWrapper"; +import { InternalAPI, NetscriptContext, wrapAPI } from "./Netscript/APIWrapper"; +import { INetscriptHelper } from "./NetscriptFunctions/INetscriptHelper"; interface NS extends INS { [key: string]: any; @@ -167,10 +159,10 @@ export function NetscriptFunctions(workerScript: WorkerScript): NS { * @param {string} callingFnName - Name of calling function. For logging purposes * @returns {BaseServer} The specified server as a BaseServer */ - const safeGetServer = function (hostname: string, callingFnName: string): BaseServer { + const safeGetServer = function (hostname: string, ctx: NetscriptContext): BaseServer { const server = GetServer(hostname); if (server == null) { - throw makeRuntimeErrorMsg(callingFnName, `Invalid hostname: ${hostname}`); + throw ctx.makeRuntimeErrorMsg(`Invalid hostname: ${hostname}`); } return server; }; @@ -187,19 +179,15 @@ export function NetscriptFunctions(workerScript: WorkerScript): NS { * is not specified. */ const getRunningScript = function ( + ctx: NetscriptContext, fn: string, hostname: string, - callingFnName: string, scriptArgs: any, ): RunningScript | null { - if (typeof callingFnName !== "string" || callingFnName === "") { - callingFnName = "getRunningScript"; - } - if (!Array.isArray(scriptArgs)) { throw makeRuntimeRejectMsg( workerScript, - `Invalid scriptArgs argument passed into getRunningScript() from ${callingFnName}(). ` + + `Invalid scriptArgs argument passed into getRunningScript() from ${ctx.function}(). ` + `This is probably a bug. Please report to game developer`, ); } @@ -209,7 +197,7 @@ export function NetscriptFunctions(workerScript: WorkerScript): NS { if (hostname == null) { hostname = workerScript.hostname; } - const server = safeGetServer(hostname, callingFnName); + const server = safeGetServer(hostname, ctx); return findRunningScript(fn, scriptArgs, server); } @@ -218,11 +206,7 @@ export function NetscriptFunctions(workerScript: WorkerScript): NS { return workerScript.scriptRef; }; - const getRunningScriptByPid = function (pid: number, callingFnName: string): RunningScript | null { - if (typeof callingFnName !== "string" || callingFnName === "") { - callingFnName = "getRunningScriptgetRunningScriptByPid"; - } - + const getRunningScriptByPid = function (pid: number): RunningScript | null { for (const server of GetAllServers()) { const runningScript = findRunningScriptByPid(pid, server); if (runningScript) return runningScript; @@ -368,17 +352,18 @@ export function NetscriptFunctions(workerScript: WorkerScript): NS { }; const hack = async function ( + ctx: NetscriptContext, hostname: string, manual: boolean, { threads: requestedThreads, stock }: any = {}, ): Promise { if (hostname === undefined) { - throw makeRuntimeErrorMsg("hack", "Takes 1 argument."); + throw ctx.makeRuntimeErrorMsg("Takes 1 argument."); } - const threads = resolveNetscriptRequestedThreads(workerScript, "hack", requestedThreads); - const server = safeGetServer(hostname, "hack"); + const threads = resolveNetscriptRequestedThreads(ctx, requestedThreads); + const server = safeGetServer(hostname, ctx); if (!(server instanceof Server)) { - throw makeRuntimeErrorMsg("hack", "Cannot be executed on this server."); + throw ctx.makeRuntimeErrorMsg("Cannot be executed on this server."); } // Calculate the hacking time @@ -387,11 +372,10 @@ export function NetscriptFunctions(workerScript: WorkerScript): NS { // No root access or skill level too low const canHack = netscriptCanHack(server, Player); if (!canHack.res) { - throw makeRuntimeErrorMsg("hack", canHack.msg || ""); + throw ctx.makeRuntimeErrorMsg(canHack.msg || ""); } - workerScript.log( - "hack", + ctx.log( () => `Executing on '${server.hostname}' in ${convertTimeMsToTimeElapsedString( hackingTime * 1000, @@ -437,8 +421,7 @@ export function NetscriptFunctions(workerScript: WorkerScript): NS { Player.gainHackingExp(expGainedOnSuccess); if (manual) Player.gainIntelligenceExp(0.005); workerScript.scriptRef.onlineExpGained += expGainedOnSuccess; - workerScript.log( - "hack", + ctx.log( () => `Successfully hacked '${server.hostname}' for ${numeralWrapper.formatMoney( moneyGained, @@ -456,8 +439,7 @@ export function NetscriptFunctions(workerScript: WorkerScript): NS { // Player only gains 25% exp for failure? Player.gainHackingExp(expGainedOnFailure); workerScript.scriptRef.onlineExpGained += expGainedOnFailure; - workerScript.log( - "hack", + ctx.log( () => `Failed to hack '${server.hostname}'. Gained ${numeralWrapper.formatExp( expGainedOnFailure, @@ -478,7 +460,7 @@ export function NetscriptFunctions(workerScript: WorkerScript): NS { return out; }; - const helper = { + const helper: INetscriptHelper = { updateDynamicRam: updateDynamicRam, makeRuntimeErrorMsg: makeRuntimeErrorMsg, string: (funcName: string, argName: string, v: unknown): string => { @@ -531,1040 +513,1003 @@ export function NetscriptFunctions(workerScript: WorkerScript): NS { }, }; - const extra = NetscriptExtra(Player, workerScript, helper); - const formulas = NetscriptFormulas(Player, workerScript, helper); + const singularity = NetscriptSingularity(Player, workerScript); - const gang = wrapAPI(helper, {}, workerScript, NetscriptGang(Player, workerScript), "gang").gang as unknown as IGang; - const sleeve = wrapAPI(helper, {}, workerScript, NetscriptSleeve(Player), "sleeve").sleeve as unknown as ISleeve; - const hacknet = wrapAPI(helper, {}, workerScript, NetscriptHacknet(Player, workerScript), "hacknet") - .hacknet as unknown as IHacknet; - const bladeburner = wrapAPI(helper, {}, workerScript, NetscriptBladeburner(Player, workerScript), "bladeburner") - .bladeburner as unknown as IBladeburner; - const codingcontract = wrapAPI( - helper, - {}, - workerScript, - NetscriptCodingContract(Player, workerScript), - "codingcontract", - ).codingcontract as unknown as ICodingContract; - const infiltration = wrapAPI(helper, {}, workerScript, NetscriptInfiltration(Player), "infiltration") - .infiltration as unknown as IInfiltration; - const stanek = wrapAPI(helper, {}, workerScript, NetscriptStanek(Player, workerScript, helper), "stanek") - .stanek as unknown as IStanek; - const corporation = wrapAPI(helper, {}, workerScript, NetscriptCorporation(Player, workerScript), "corporation") - .corporation as unknown as ICorporation; - const singularity = wrapAPI(helper, {}, workerScript, NetscriptSingularity(Player, workerScript), "singularity") - .singularity as unknown as ISingularity; - const stockmarket = wrapAPI(helper, {}, workerScript, NetscriptStockMarket(Player, workerScript), "stock") - .stock as unknown as ITIX; - const ui = wrapAPI(helper, {}, workerScript, NetscriptUserInterface(), "ui").ui as unknown as IUserInterface; - const grafting = wrapAPI(helper, {}, workerScript, NetscriptGrafting(Player), "grafting") - .grafting as unknown as IGrafting; - - const base: INS = { + const base: InternalAPI = { ...singularity, + args: workerScript.args as unknown as any, + enums: { + toast: ToastVariant, + } as unknown as any, + singularity: singularity, - gang: gang, - bladeburner: bladeburner, - codingcontract: codingcontract, - sleeve: sleeve, - corporation: corporation, - stanek: stanek, - infiltration: infiltration, - ui: ui, - formulas: formulas, - stock: stockmarket, - grafting: grafting, - args: workerScript.args, - hacknet: hacknet, + gang: NetscriptGang(Player, workerScript), + bladeburner: NetscriptBladeburner(Player, workerScript), + codingcontract: NetscriptCodingContract(Player, workerScript), + sleeve: NetscriptSleeve(Player), + corporation: NetscriptCorporation(Player, workerScript), + 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: function (_hostname: unknown = workerScript.hostname): string[] { - updateDynamicRam("scan", getRamCost(Player, "scan")); - const hostname = helper.string("scan", "hostname", _hostname); - const server = safeGetServer(hostname, "scan"); - 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); - } - workerScript.log("scan", () => `returned ${server.serversOnNetwork.length} connections for ${server.hostname}`); - return out; - }, - hack: function (_hostname: unknown, { threads: requestedThreads, stock }: BasicHGWOptions = {}): Promise { - updateDynamicRam("hack", getRamCost(Player, "hack")); - const hostname = helper.string("hack", "hostname", _hostname); - return hack(hostname, false, { threads: requestedThreads, stock: stock }); - }, - hackAnalyzeThreads: function (_hostname: unknown, _hackAmount: unknown): number { - updateDynamicRam("hackAnalyzeThreads", getRamCost(Player, "hackAnalyzeThreads")); - const hostname = helper.string("hackAnalyzeThreads", "hostname", _hostname); - const hackAmount = helper.number("hackAnalyzeThreads", "hackAmount", _hackAmount); + 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, "hackAnalyzeThreads"); - if (!(server instanceof Server)) { - workerScript.log("hackAnalyzeThreads", () => "Cannot be executed on this server."); - return -1; - } - if (isNaN(hackAmount)) { - throw makeRuntimeErrorMsg( - "hackAnalyzeThreads", - `Invalid hackAmount argument passed into hackAnalyzeThreads: ${hackAmount}. Must be numeric.`, - ); - } - - if (hackAmount < 0 || hackAmount > server.moneyAvailable) { - return -1; - } else if (hackAmount === 0) { - return 0; - } - - const percentHacked = calculatePercentMoneyHacked(server, Player); - - if (percentHacked === 0 || server.moneyAvailable === 0) { - return 0; // To prevent returning infinity below - } - - return hackAmount / Math.floor(server.moneyAvailable * percentHacked); - }, - hackAnalyze: function (_hostname: unknown): number { - updateDynamicRam("hackAnalyze", getRamCost(Player, "hackAnalyze")); - const hostname = helper.string("hackAnalyze", "hostname", _hostname); - - const server = safeGetServer(hostname, "hackAnalyze"); - if (!(server instanceof Server)) { - workerScript.log("hackAnalyze", () => "Cannot be executed on this server."); - return 0; - } - - return calculatePercentMoneyHacked(server, Player); - }, - hackAnalyzeSecurity: function (_threads: unknown, _hostname?: unknown): number { - updateDynamicRam("hackAnalyzeSecurity", getRamCost(Player, "hackAnalyzeSecurity")); - let threads = helper.number("hackAnalyzeSecurity", "threads", _threads); - if (_hostname) { - const hostname = helper.string("hackAnalyzeSecurity", "hostname", _hostname); - const server = safeGetServer(hostname, "hackAnalyze"); + // Check argument validity + const server = safeGetServer(hostname, ctx); if (!(server instanceof Server)) { - workerScript.log("hackAnalyzeSecurity", () => "Cannot be executed on this 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) { 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)); + if (percentHacked === 0 || server.moneyAvailable === 0) { + return 0; // To prevent returning infinity below } - } - return CONSTANTS.ServerFortifyAmount * threads; - }, - hackAnalyzeChance: function (_hostname: unknown): number { - updateDynamicRam("hackAnalyzeChance", getRamCost(Player, "hackAnalyzeChance")); - const hostname = helper.string("hackAnalyzeChance", "hostname", _hostname); - - const server = safeGetServer(hostname, "hackAnalyzeChance"); - if (!(server instanceof Server)) { - workerScript.log("hackAnalyzeChance", () => "Cannot be executed on this server."); - return 0; - } - - return calculateHackingChance(server, Player); - }, - sleep: async function (_time: unknown = 0): Promise { - updateDynamicRam("sleep", getRamCost(Player, "sleep")); - const time = helper.number("sleep", "time", _time); - if (time === undefined) { - throw makeRuntimeErrorMsg("sleep", "Takes 1 argument."); - } - workerScript.log("sleep", () => `Sleeping for ${time} milliseconds`); - return netscriptDelay(time, workerScript).then(function () { - return Promise.resolve(true); - }); - }, - asleep: function (_time: unknown = 0): Promise { - updateDynamicRam("asleep", getRamCost(Player, "asleep")); - const time = helper.number("asleep", "time", _time); - if (time === undefined) { - throw makeRuntimeErrorMsg("asleep", "Takes 1 argument."); - } - workerScript.log("asleep", () => `Sleeping for ${time} milliseconds`); - return new Promise((resolve) => setTimeout(() => resolve(true), time)); - }, - grow: async function ( - _hostname: unknown, - { threads: requestedThreads, stock }: BasicHGWOptions = {}, - ): Promise { - updateDynamicRam("grow", getRamCost(Player, "grow")); - const hostname = helper.string("grow", "hostname", _hostname); - const threads = resolveNetscriptRequestedThreads( - workerScript, - "grow", - requestedThreads ?? workerScript.scriptRef.threads, - ); - if (hostname === undefined) { - throw makeRuntimeErrorMsg("grow", "Takes 1 argument."); - } - const server = safeGetServer(hostname, "grow"); - if (!(server instanceof Server)) { - workerScript.log("grow", () => "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 makeRuntimeErrorMsg("grow", canHack.msg || ""); - } - - const growTime = calculateGrowTime(server, Player); - workerScript.log( - "grow", - () => - `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; - workerScript.log( - "grow", - () => - `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: function (_hostname: unknown, _growth: unknown, _cores: unknown = 1): number { - updateDynamicRam("growthAnalyze", getRamCost(Player, "growthAnalyze")); - const hostname = helper.string("growthAnalyze", "hostname", _hostname); - const growth = helper.number("growthAnalyze", "growth", _growth); - const cores = helper.number("growthAnalyze", "cores", _cores); - - // Check argument validity - const server = safeGetServer(hostname, "growthAnalyze"); - if (!(server instanceof Server)) { - workerScript.log("growthAnalyze", () => "Cannot be executed on this server."); - return 0; - } - if (typeof growth !== "number" || isNaN(growth) || growth < 1 || !isFinite(growth)) { - throw makeRuntimeErrorMsg("growthAnalyze", `Invalid argument: growth must be numeric and >= 1, is ${growth}.`); - } - - return numCycleForGrowth(server, Number(growth), Player, cores); - }, - growthAnalyzeSecurity: function (_threads: unknown, _hostname?: unknown, _cores?: unknown): number { - updateDynamicRam("growthAnalyzeSecurity", getRamCost(Player, "growthAnalyzeSecurity")); - let threads = helper.number("growthAnalyzeSecurity", "threads", _threads); - if (_hostname) { - const cores = helper.number("growthAnalyzeSecurity", "cores", _cores) || 1; - const hostname = helper.string("growthAnalyzeSecurity", "hostname", _hostname); - const server = safeGetServer(hostname, "growthAnalyzeSecurity"); + return hackAmount / Math.floor(server.moneyAvailable * percentHacked); + }, + hackAnalyze: + (ctx: NetscriptContext) => + (_hostname: unknown): number => { + const hostname = ctx.helper.string("hostname", _hostname); + const server = safeGetServer(hostname, ctx); if (!(server instanceof Server)) { - workerScript.log("growthAnalyzeSecurity", () => "Cannot be executed on this server."); + ctx.log(() => "Cannot be executed on this server."); return 0; } - const maxThreadsNeeded = Math.ceil( - numCycleForGrowthCorrected(server, server.moneyMax, server.moneyAvailable, Player, cores), - ); + 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; + } - threads = Math.min(threads, maxThreadsNeeded); - } + const percentHacked = calculatePercentMoneyHacked(server, Player); - return 2 * CONSTANTS.ServerFortifyAmount * threads; - }, - weaken: async function (_hostname: unknown, { threads: requestedThreads }: BasicHGWOptions = {}): Promise { - updateDynamicRam("weaken", getRamCost(Player, "weaken")); - const hostname = helper.string("weaken", "hostname", _hostname); - const threads = resolveNetscriptRequestedThreads( - workerScript, - "weaken", - requestedThreads ?? workerScript.scriptRef.threads, - ); - if (hostname === undefined) { - throw makeRuntimeErrorMsg("weaken", "Takes 1 argument."); - } - const server = safeGetServer(hostname, "weaken"); - if (!(server instanceof Server)) { - workerScript.log("weaken", () => "Cannot be executed on this server."); - return Promise.resolve(0); - } + if (percentHacked > 0) { + // thread count is limited to the maximum number of threads needed + threads = Math.min(threads, Math.ceil(1 / percentHacked)); + } + } - // No root access or skill level too low - const canHack = netscriptCanWeaken(server); - if (!canHack.res) { - throw makeRuntimeErrorMsg("weaken", canHack.msg || ""); - } + return CONSTANTS.ServerFortifyAmount * threads; + }, + hackAnalyzeChance: + (ctx: NetscriptContext) => + (_hostname: unknown): number => { + const hostname = ctx.helper.string("hostname", _hostname); - const weakenTime = calculateWeakenTime(server, Player); - workerScript.log( - "weaken", - () => - `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) { - workerScript.log("weaken", () => "Server is null, did it die?"); + 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 coreBonus = 1 + (host.cpuCores - 1) / 16; - server.weaken(CONSTANTS.ServerWeakenAmount * threads * coreBonus); - workerScript.scriptRef.recordWeaken(server.hostname, threads); - const expGain = calculateHackingExpGain(server, Player) * threads; - workerScript.log( - "weaken", + + 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( () => - `'${server.hostname}' security level weakened to ${ - server.hackDifficulty - }. Gained ${numeralWrapper.formatExp(expGain)} hacking exp (t=${numeralWrapper.formatThreads(threads)})`, + `Executing on '${server.hostname}' in ${convertTimeMsToTimeElapsedString( + growTime * 1000, + true, + )} (t=${numeralWrapper.formatThreads(threads)}).`, ); - workerScript.scriptRef.onlineExpGained += expGain; - Player.gainHackingExp(expGain); - return Promise.resolve(CONSTANTS.ServerWeakenAmount * threads * coreBonus); - }); - }, - weakenAnalyze: function (_threads: unknown, _cores: unknown = 1): number { - updateDynamicRam("weakenAnalyze", getRamCost(Player, "weakenAnalyze")); - const threads = helper.number("weakenAnalyze", "threads", _threads); - const cores = helper.number("weakenAnalyze", "cores", _cores); - const coreBonus = 1 + (cores - 1) / 16; - return CONSTANTS.ServerWeakenAmount * threads * coreBonus * BitNodeMultipliers.ServerWeakenRate; - }, - share: async function (): Promise { - updateDynamicRam("share", getRamCost(Player, "share")); - workerScript.log("share", () => "Sharing this computer."); + 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.intelligence, 2)); return netscriptDelay(10000, workerScript).finally(function () { - workerScript.log("share", () => "Finished sharing this computer."); + ctx.log(() => "Finished sharing this computer."); end(); }); }, - getSharePower: function (): number { - updateDynamicRam("getSharePower", getRamCost(Player, "getSharePower")); + getSharePower: () => (): number => { return CalculateShareMult(); }, - print: function (...args: any[]): void { - updateDynamicRam("print", getRamCost(Player, "print")); - if (args.length === 0) { - throw makeRuntimeErrorMsg("print", "Takes at least 1 argument."); - } - workerScript.print(argsToString(args)); - }, - printf: function (_format: unknown, ...args: any[]): void { - updateDynamicRam("printf", getRamCost(Player, "printf")); - const format = helper.string("printf", "format", _format); - if (typeof format !== "string") { - throw makeRuntimeErrorMsg("printf", "First argument must be string for the format."); - } - workerScript.print(vsprintf(format, args)); - }, - tprint: function (...args: any[]): void { - updateDynamicRam("tprint", getRamCost(Player, "tprint")); - if (args.length === 0) { - throw makeRuntimeErrorMsg("tprint", "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: function (_format: unknown, ...args: any[]): void { - updateDynamicRam("tprintf", getRamCost(Player, "tprintf")); - const format = helper.string("printf", "format", _format); - if (typeof format !== "string") { - throw makeRuntimeErrorMsg("tprintf", "First argument must be string for the format."); - } - const str = vsprintf(format, args); + print: + (ctx: NetscriptContext) => + (...args: any[]): void => { + if (args.length === 0) { + throw ctx.makeRuntimeErrorMsg("Takes at least 1 argument."); + } + workerScript.print(argsToString(args)); + }, + printf: + (ctx: NetscriptContext) => + (_format: unknown, ...args: any[]): 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: any[]): 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: any[]): void => { + const format = ctx.helper.string("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: function (): void { - updateDynamicRam("clearLog", getRamCost(Player, "clearLog")); + if (str.startsWith("ERROR") || str.startsWith("FAIL")) { + Terminal.error(`${str}`); + return; + } + if (str.startsWith("SUCCESS")) { + Terminal.success(`${str}`); + return; + } + if (str.startsWith("WARN")) { + Terminal.warn(`${str}`); + return; + } + if (str.startsWith("INFO")) { + Terminal.info(`${str}`); + return; + } + Terminal.print(`${str}`); + }, + clearLog: () => (): void => { workerScript.scriptRef.clearLog(); }, - disableLog: function (_fn: unknown): void { - updateDynamicRam("disableLog", getRamCost(Player, "disableLog")); - const fn = helper.string("disableLog", "fn", _fn); - if (fn === "ALL") { - for (const fn of Object.keys(possibleLogs)) { + 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}`); } - workerScript.log("disableLog", () => `Disabled logging for all functions`); - } else if (possibleLogs[fn] === undefined) { - throw makeRuntimeErrorMsg("disableLog", `Invalid argument: ${fn}.`); - } else { - workerScript.disableLogs[fn] = true; - workerScript.log("disableLog", () => `Disabled logging for ${fn}`); - } - }, - enableLog: function (_fn: unknown): void { - updateDynamicRam("enableLog", getRamCost(Player, "enableLog")); - const fn = helper.string("enableLog", "fn", _fn); - if (fn === "ALL") { - for (const fn of Object.keys(possibleLogs)) { - delete workerScript.disableLogs[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: any, hostname: any, ...scriptArgs: any[]): string[] => { + const runningScriptObj = getRunningScript(ctx, fn, hostname, scriptArgs); + if (runningScriptObj == null) { + ctx.log(() => getCannotFindRunningScriptErrorMessage(fn, hostname, scriptArgs)); + return []; } - workerScript.log("enableLog", () => `Enabled logging for all functions`); - } else if (possibleLogs[fn] === undefined) { - throw makeRuntimeErrorMsg("enableLog", `Invalid argument: ${fn}.`); - } - delete workerScript.disableLogs[fn]; - workerScript.log("enableLog", () => `Enabled logging for ${fn}`); - }, - isLogEnabled: function (_fn: unknown): boolean { - updateDynamicRam("isLogEnabled", getRamCost(Player, "isLogEnabled")); - const fn = helper.string("isLogEnabled", "fn", _fn); - if (possibleLogs[fn] === undefined) { - throw makeRuntimeErrorMsg("isLogEnabled", `Invalid argument: ${fn}.`); - } - return !workerScript.disableLogs[fn]; - }, - getScriptLogs: function (fn: any, hostname: any, ...scriptArgs: any[]): string[] { - updateDynamicRam("getScriptLogs", getRamCost(Player, "getScriptLogs")); - const runningScriptObj = getRunningScript(fn, hostname, "getScriptLogs", scriptArgs); - if (runningScriptObj == null) { - workerScript.log("getScriptLogs", () => getCannotFindRunningScriptErrorMessage(fn, hostname, scriptArgs)); - return []; - } - return runningScriptObj.logs.slice(); - }, - tail: function (fn: any, hostname: any = workerScript.hostname, ...scriptArgs: any[]): void { - updateDynamicRam("tail", getRamCost(Player, "tail")); - let runningScriptObj; - if (arguments.length === 0) { - runningScriptObj = workerScript.scriptRef; - } else if (typeof fn === "number") { - runningScriptObj = getRunningScriptByPid(fn, "tail"); - } else { - runningScriptObj = getRunningScript(fn, hostname, "tail", scriptArgs); - } - if (runningScriptObj == null) { - workerScript.log("tail", () => getCannotFindRunningScriptErrorMessage(fn, hostname, scriptArgs)); - return; - } + return runningScriptObj.logs.slice(); + }, + tail: + (ctx: NetscriptContext) => + (fn: any, hostname: any = workerScript.hostname, ...scriptArgs: any[]): void => { + let runningScriptObj; + if (arguments.length === 0) { + runningScriptObj = workerScript.scriptRef; + } else if (typeof fn === "number") { + runningScriptObj = getRunningScriptByPid(fn); + } else { + runningScriptObj = getRunningScript(ctx, fn, hostname, scriptArgs); + } + if (runningScriptObj == null) { + ctx.log(() => getCannotFindRunningScriptErrorMessage(fn, hostname, scriptArgs)); + return; + } - LogBoxEvents.emit(runningScriptObj); - }, - closeTail: function (_pid: unknown = workerScript.scriptRef.pid): void { - updateDynamicRam("closeTail", getRamCost(Player, "closeTail")); - const pid = helper.number("closeTail", "pid", _pid); - //Emit an event to tell the game to close the tail window if it exists - LogBoxCloserEvents.emit(pid); - }, - nuke: function (_hostname: unknown): boolean { - updateDynamicRam("nuke", getRamCost(Player, "nuke")); - const hostname = helper.string("tail", "hostname", _hostname); - if (hostname === undefined) { - throw makeRuntimeErrorMsg("nuke", "Takes 1 argument."); - } - const server = safeGetServer(hostname, "nuke"); - if (!(server instanceof Server)) { - workerScript.log("nuke", () => "Cannot be executed on this server."); - return false; - } - if (server.hasAdminRights) { - workerScript.log("nuke", () => `Already have root access to '${server.hostname}'.`); + LogBoxEvents.emit(runningScriptObj); + }, + 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; - } - if (!Player.hasProgram(Programs.NukeProgram.name)) { - throw makeRuntimeErrorMsg("nuke", "You do not have the NUKE.exe virus!"); - } - if (server.openPortCount < server.numOpenPortsRequired) { - throw makeRuntimeErrorMsg("nuke", "Not enough ports opened to use NUKE.exe virus."); - } - server.hasAdminRights = true; - workerScript.log("nuke", () => `Executed NUKE.exe virus on '${server.hostname}' to gain root access.`); - return true; - }, - brutessh: function (_hostname: unknown): boolean { - updateDynamicRam("brutessh", getRamCost(Player, "brutessh")); - const hostname = helper.string("brutessh", "hostname", _hostname); - if (hostname === undefined) { - throw makeRuntimeErrorMsg("brutessh", "Takes 1 argument."); - } - const server = safeGetServer(hostname, "brutessh"); - if (!(server instanceof Server)) { - workerScript.log("brutessh", () => "Cannot be executed on this server."); - return false; - } - if (!Player.hasProgram(Programs.BruteSSHProgram.name)) { - throw makeRuntimeErrorMsg("brutessh", "You do not have the BruteSSH.exe program!"); - } - if (!server.sshPortOpen) { - workerScript.log("brutessh", () => `Executed BruteSSH.exe on '${server.hostname}' to open SSH port (22).`); - server.sshPortOpen = true; - ++server.openPortCount; - } else { - workerScript.log("brutessh", () => `SSH Port (22) already opened on '${server.hostname}'.`); - } - return true; - }, - ftpcrack: function (_hostname: unknown): boolean { - updateDynamicRam("ftpcrack", getRamCost(Player, "ftpcrack")); - const hostname = helper.string("ftpcrack", "hostname", _hostname); - if (hostname === undefined) { - throw makeRuntimeErrorMsg("ftpcrack", "Takes 1 argument."); - } - const server = safeGetServer(hostname, "ftpcrack"); - if (!(server instanceof Server)) { - workerScript.log("ftpcrack", () => "Cannot be executed on this server."); - return false; - } - if (!Player.hasProgram(Programs.FTPCrackProgram.name)) { - throw makeRuntimeErrorMsg("ftpcrack", "You do not have the FTPCrack.exe program!"); - } - if (!server.ftpPortOpen) { - workerScript.log("ftpcrack", () => `Executed FTPCrack.exe on '${server.hostname}' to open FTP port (21).`); - server.ftpPortOpen = true; - ++server.openPortCount; - } else { - workerScript.log("ftpcrack", () => `FTP Port (21) already opened on '${server.hostname}'.`); - } - return true; - }, - relaysmtp: function (_hostname: unknown): boolean { - updateDynamicRam("relaysmtp", getRamCost(Player, "relaysmtp")); - const hostname = helper.string("relaysmtp", "hostname", _hostname); - if (hostname === undefined) { - throw makeRuntimeErrorMsg("relaysmtp", "Takes 1 argument."); - } - const server = safeGetServer(hostname, "relaysmtp"); - if (!(server instanceof Server)) { - workerScript.log("relaysmtp", () => "Cannot be executed on this server."); - return false; - } - if (!Player.hasProgram(Programs.RelaySMTPProgram.name)) { - throw makeRuntimeErrorMsg("relaysmtp", "You do not have the relaySMTP.exe program!"); - } - if (!server.smtpPortOpen) { - workerScript.log("relaysmtp", () => `Executed relaySMTP.exe on '${server.hostname}' to open SMTP port (25).`); - server.smtpPortOpen = true; - ++server.openPortCount; - } else { - workerScript.log("relaysmtp", () => `SMTP Port (25) already opened on '${server.hostname}'.`); - } - return true; - }, - httpworm: function (_hostname: unknown): boolean { - updateDynamicRam("httpworm", getRamCost(Player, "httpworm")); - const hostname = helper.string("httpworm", "hostname", _hostname); - if (hostname === undefined) { - throw makeRuntimeErrorMsg("httpworm", "Takes 1 argument"); - } - const server = safeGetServer(hostname, "httpworm"); - if (!(server instanceof Server)) { - workerScript.log("httpworm", () => "Cannot be executed on this server."); - return false; - } - if (!Player.hasProgram(Programs.HTTPWormProgram.name)) { - throw makeRuntimeErrorMsg("httpworm", "You do not have the HTTPWorm.exe program!"); - } - if (!server.httpPortOpen) { - workerScript.log("httpworm", () => `Executed HTTPWorm.exe on '${server.hostname}' to open HTTP port (80).`); - server.httpPortOpen = true; - ++server.openPortCount; - } else { - workerScript.log("httpworm", () => `HTTP Port (80) already opened on '${server.hostname}'.`); - } - return true; - }, - sqlinject: function (_hostname: unknown): boolean { - updateDynamicRam("sqlinject", getRamCost(Player, "sqlinject")); - const hostname = helper.string("sqlinject", "hostname", _hostname); - if (hostname === undefined) { - throw makeRuntimeErrorMsg("sqlinject", "Takes 1 argument."); - } - const server = safeGetServer(hostname, "sqlinject"); - if (!(server instanceof Server)) { - workerScript.log("sqlinject", () => "Cannot be executed on this server."); - return false; - } - if (!Player.hasProgram(Programs.SQLInjectProgram.name)) { - throw makeRuntimeErrorMsg("sqlinject", "You do not have the SQLInject.exe program!"); - } - if (!server.sqlPortOpen) { - workerScript.log("sqlinject", () => `Executed SQLInject.exe on '${server.hostname}' to open SQL port (1433).`); - server.sqlPortOpen = true; - ++server.openPortCount; - } else { - workerScript.log("sqlinject", () => `SQL Port (1433) already opened on '${server.hostname}'.`); - } - return true; - }, - run: function (_scriptname: unknown, _threads: unknown = 1, ...args: any[]): number { - updateDynamicRam("run", getRamCost(Player, "run")); - const scriptname = helper.string("run", "scriptname", _scriptname); - const threads = helper.number("run", "threads", _threads); - if (scriptname === undefined) { - throw makeRuntimeErrorMsg("run", "Usage: run(scriptname, [numThreads], [arg1], [arg2]...)"); - } - if (isNaN(threads) || threads <= 0) { - throw makeRuntimeErrorMsg("run", `Invalid thread count. Must be numeric and > 0, is ${threads}`); - } - const scriptServer = GetServer(workerScript.hostname); - if (scriptServer == null) { - throw makeRuntimeErrorMsg("run", "Could not find server. This is a bug. Report to dev."); - } - - return runScriptFromScript(Player, "run", scriptServer, scriptname, args, workerScript, threads); - }, - exec: function (_scriptname: unknown, _hostname: unknown, _threads: unknown = 1, ...args: any[]): number { - updateDynamicRam("exec", getRamCost(Player, "exec")); - const scriptname = helper.string("exec", "scriptname", _scriptname); - const hostname = helper.string("exec", "hostname", _hostname); - const threads = helper.number("exec", "threads", _threads); - if (scriptname === undefined || hostname === undefined) { - throw makeRuntimeErrorMsg("exec", "Usage: exec(scriptname, server, [numThreads], [arg1], [arg2]...)"); - } - if (isNaN(threads) || threads <= 0) { - throw makeRuntimeErrorMsg("exec", `Invalid thread count. Must be numeric and > 0, is ${threads}`); - } - const server = safeGetServer(hostname, "exec"); - return runScriptFromScript(Player, "exec", server, scriptname, args, workerScript, threads); - }, - spawn: function (_scriptname: unknown, _threads: unknown = 1, ...args: any[]): void { - updateDynamicRam("spawn", getRamCost(Player, "spawn")); - const scriptname = helper.string("spawn", "scriptname", _scriptname); - const threads = helper.number("spawn", "threads", _threads); - if (!scriptname || !threads) { - throw makeRuntimeErrorMsg("spawn", "Usage: spawn(scriptname, threads)"); - } - - const spawnDelay = 10; - setTimeout(() => { + }, + 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: any[]): number => { + const scriptname = ctx.helper.string("scriptname", _scriptname); + const threads = ctx.helper.number("threads", _threads); + if (scriptname === undefined) { + throw ctx.makeRuntimeErrorMsg("Usage: run(scriptname, [numThreads], [arg1], [arg2]...)"); + } if (isNaN(threads) || threads <= 0) { - throw makeRuntimeErrorMsg("spawn", `Invalid thread count. Must be numeric and > 0, is ${threads}`); + throw ctx.makeRuntimeErrorMsg(`Invalid thread count. Must be numeric and > 0, is ${threads}`); } const scriptServer = GetServer(workerScript.hostname); if (scriptServer == null) { - throw makeRuntimeErrorMsg("spawn", "Could not find server. This is a bug. Report to dev"); + 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); - - workerScript.log("spawn", () => `Will execute '${scriptname}' in ${spawnDelay} seconds`); - - workerScript.running = false; // Prevent workerScript from "finishing execution naturally" - if (killWorkerScript(workerScript)) { - workerScript.log("spawn", () => "Exiting..."); - } - }, - kill: function (filename: any, hostname?: any, ...scriptArgs: any[]): boolean { - updateDynamicRam("kill", getRamCost(Player, "kill")); - - let res; - const killByPid = typeof filename === "number"; - if (killByPid) { - // Kill by pid - res = killWorkerScript(filename); - } else { - // Kill by filename/hostname - if (filename === undefined || hostname === undefined) { - throw makeRuntimeErrorMsg("kill", "Usage: kill(scriptname, server, [arg1], [arg2]...)"); + return runScriptFromScript(Player, "run", scriptServer, scriptname, args, workerScript, threads); + }, + exec: + (ctx: NetscriptContext) => + (_scriptname: unknown, _hostname: unknown, _threads: unknown = 1, ...args: any[]): number => { + const scriptname = ctx.helper.string("scriptname", _scriptname); + const hostname = ctx.helper.string("hostname", _hostname); + const threads = ctx.helper.number("threads", _threads); + if (scriptname === undefined || hostname === undefined) { + throw ctx.makeRuntimeErrorMsg("Usage: exec(scriptname, server, [numThreads], [arg1], [arg2]...)"); + } + if (isNaN(threads) || threads <= 0) { + throw ctx.makeRuntimeErrorMsg(`Invalid thread count. Must be numeric and > 0, is ${threads}`); + } + const server = safeGetServer(hostname, ctx); + return runScriptFromScript(Player, "exec", server, scriptname, args, workerScript, threads); + }, + spawn: + (ctx: NetscriptContext) => + (_scriptname: unknown, _threads: unknown = 1, ...args: any[]): void => { + const scriptname = ctx.helper.string("scriptname", _scriptname); + const threads = ctx.helper.number("threads", _threads); + if (!scriptname || !threads) { + throw ctx.makeRuntimeErrorMsg("Usage: spawn(scriptname, threads)"); } - const server = safeGetServer(hostname, "kill"); - const runningScriptObj = getRunningScript(filename, hostname, "kill", scriptArgs); - if (runningScriptObj == null) { - workerScript.log("kill", () => getCannotFindRunningScriptErrorMessage(filename, hostname, scriptArgs)); + 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: any, hostname?: any, ...scriptArgs: any[]): boolean => { + let res; + const killByPid = typeof filename === "number"; + if (killByPid) { + // Kill by pid + res = killWorkerScript(filename); + } else { + // Kill by filename/hostname + if (filename === undefined || hostname === undefined) { + throw ctx.makeRuntimeErrorMsg("Usage: kill(scriptname, server, [arg1], [arg2]...)"); + } + + const server = safeGetServer(hostname, ctx); + const runningScriptObj = getRunningScript(ctx, filename, hostname, scriptArgs); + if (runningScriptObj == null) { + ctx.log(() => getCannotFindRunningScriptErrorMessage(filename, hostname, scriptArgs)); + return false; + } + + res = killWorkerScript(runningScriptObj, server.hostname); + } + + if (res) { + if (killByPid) { + ctx.log(() => `Killing script with PID ${filename}`); + } else { + ctx.log(() => `Killing '${filename}' on '${hostname}' with args: ${arrayToString(scriptArgs)}.`); + } + return true; + } else { + if (killByPid) { + ctx.log(() => `No script with PID ${filename}`); + } else { + ctx.log(() => `No such script '${filename}' on '${hostname}' with args: ${arrayToString(scriptArgs)}`); + } return false; } - - res = killWorkerScript(runningScriptObj, server.hostname); - } - - if (res) { - if (killByPid) { - workerScript.log("kill", () => `Killing script with PID ${filename}`); - } else { - workerScript.log( - "kill", - () => `Killing '${filename}' on '${hostname}' with args: ${arrayToString(scriptArgs)}.`, - ); + }, + 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])"); } - return true; - } else { - if (killByPid) { - workerScript.log("kill", () => `No script with PID ${filename}`); - } else { - workerScript.log( - "kill", - () => `No such script '${filename}' on '${hostname}' with args: ${arrayToString(scriptArgs)}`, - ); + 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(server.runningScripts[i], server.hostname, false); + ++scriptsKilled; } - return false; - } - }, - killall: function (_hostname: unknown = workerScript.hostname, _safetyguard: unknown = true): boolean { - updateDynamicRam("killall", getRamCost(Player, "killall")); - const hostname = helper.string("killall", "hostname", _hostname); - const safetyguard = helper.boolean(_safetyguard); - if (hostname === undefined) { - throw makeRuntimeErrorMsg("killall", "Usage: killall(hostname, [safetyguard boolean])"); - } - const server = safeGetServer(hostname, "killall"); + WorkerScriptStartStopEventEmitter.emit(); + ctx.log(() => `Killing all scripts on '${server.hostname}'. May take a few minutes for the scripts to die.`); - let scriptsKilled = 0; - - for (let i = server.runningScripts.length - 1; i >= 0; --i) { - if (safetyguard === true && server.runningScripts[i].pid == workerScript.pid) continue; - killWorkerScript(server.runningScripts[i], server.hostname, false); - ++scriptsKilled; - } - WorkerScriptStartStopEventEmitter.emit(); - workerScript.log( - "killall", - () => `Killing all scripts on '${server.hostname}'. May take a few minutes for the scripts to die.`, - ); - - return scriptsKilled > 0; - }, - exit: function (): void { - updateDynamicRam("exit", getRamCost(Player, "exit")); + return scriptsKilled > 0; + }, + exit: (ctx: NetscriptContext) => (): void => { workerScript.running = false; // Prevent workerScript from "finishing execution naturally" if (killWorkerScript(workerScript)) { - workerScript.log("exit", () => "Exiting..."); + ctx.log(() => "Exiting..."); } else { - workerScript.log("exit", () => "Failed. This is a bug. Report to dev."); + ctx.log(() => "Failed. This is a bug. Report to dev."); } }, - scp: async function (scriptname: any, _hostname1: unknown, hostname2?: any): Promise { - updateDynamicRam("scp", getRamCost(Player, "scp")); - const hostname1 = helper.string("scp", "hostname1", _hostname1); - if (arguments.length !== 2 && arguments.length !== 3) { - throw makeRuntimeErrorMsg("scp", "Takes 2 or 3 arguments"); - } - if (scriptname && scriptname.constructor === Array) { - // Recursively call scp on all elements of array - const scripts: Array = scriptname; - if (scripts.length === 0) { - throw makeRuntimeErrorMsg("scp", "No scripts to copy"); + scp: + (ctx: NetscriptContext) => + async (scriptname: any, _hostname1: unknown, hostname2?: any): Promise => { + const hostname1 = ctx.helper.string("hostname1", _hostname1); + if (arguments.length !== 2 && arguments.length !== 3) { + throw ctx.makeRuntimeErrorMsg("Takes 2 or 3 arguments"); } - let res = true; - await Promise.all( - scripts.map(async function (script) { - if (!(await NetscriptFunctions(workerScript).scp(script, hostname1, hostname2))) { - res = false; - } - }), - ); - return Promise.resolve(res); - } - - // Invalid file type - if (!isValidFilePath(scriptname)) { - throw makeRuntimeErrorMsg("scp", `Invalid filename: '${scriptname}'`); - } - - // Invalid file name - if (!scriptname.endsWith(".lit") && !isScriptFilename(scriptname) && !scriptname.endsWith("txt")) { - throw makeRuntimeErrorMsg("scp", "Only works for scripts, .lit and .txt files"); - } - - let destServer: BaseServer | null; - let currServ: BaseServer | null; - - if (hostname2 != null) { - // 3 Argument version: scriptname, source, destination - if (scriptname === undefined || hostname1 === undefined || hostname2 === undefined) { - throw makeRuntimeErrorMsg("scp", "Takes 2 or 3 arguments"); - } - destServer = safeGetServer(hostname2, "scp"); - currServ = safeGetServer(hostname1, "scp"); - } else if (hostname1 != null) { - // 2 Argument version: scriptname, destination - if (scriptname === undefined || hostname1 === undefined) { - throw makeRuntimeErrorMsg("scp", "Takes 2 or 3 arguments"); - } - destServer = safeGetServer(hostname1, "scp"); - currServ = GetServer(workerScript.hostname); - if (currServ == null) { - throw makeRuntimeErrorMsg( - "scp", - "Could not find server hostname for this script. This is a bug. Report to dev.", + if (scriptname && scriptname.constructor === Array) { + // Recursively call scp on all elements of array + const scripts: Array = 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, hostname1, hostname2))) { + res = false; + } + }), ); + return Promise.resolve(res); } - } else { - throw makeRuntimeErrorMsg("scp", "Takes 2 or 3 arguments"); - } - // 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; + // 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"); + } + + let destServer: BaseServer | null; + let currServ: BaseServer | null; + + if (hostname2 != null) { + // 3 Argument version: scriptname, source, destination + if (scriptname === undefined || hostname1 === undefined || hostname2 === undefined) { + throw ctx.makeRuntimeErrorMsg("Takes 2 or 3 arguments"); + } + destServer = safeGetServer(hostname2, ctx); + currServ = safeGetServer(hostname1, ctx); + } else if (hostname1 != null) { + // 2 Argument version: scriptname, destination + if (scriptname === undefined || hostname1 === undefined) { + throw ctx.makeRuntimeErrorMsg("Takes 2 or 3 arguments"); + } + destServer = safeGetServer(hostname1, ctx); + currServ = GetServer(workerScript.hostname); + if (currServ == null) { + throw ctx.makeRuntimeErrorMsg( + "Could not find server hostname for this script. This is a bug. Report to dev.", + ); + } + } else { + throw ctx.makeRuntimeErrorMsg("Takes 2 or 3 arguments"); + } + + // 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 (!found) { - workerScript.log("scp", () => `File '${scriptname}' does not exist.`); + if (sourceScript == null) { + 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) { - workerScript.log("scp", () => `File '${scriptname}' copied over to '${destServer?.hostname}'.`); - return Promise.resolve(true); // Already exists - } - } - destServer.messages.push(scriptname); - workerScript.log("scp", () => `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) { - workerScript.log("scp", () => `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; - workerScript.log("scp", () => `File '${scriptname}' copied over to '${destServer?.hostname}'.`); + // 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); } } - const newFile = new TextFile(txtFile.fn, txtFile.text); - destServer.textFiles.push(newFile); - workerScript.log("scp", () => `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; + // 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 (arguments.length >= 2) { + filter = grep.toString(); } - } - if (sourceScript == null) { - workerScript.log("scp", () => `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) { - workerScript.log("scp", () => `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); - workerScript.log("scp", () => `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: function (_hostname: unknown, _grep: unknown = ""): string[] { - updateDynamicRam("ls", getRamCost(Player, "ls")); - const hostname = helper.string("ls", "hostname", _hostname); - const grep = helper.string("ls", "grep", _grep); - if (hostname === undefined) { - throw makeRuntimeErrorMsg("ls", "Usage: ls(hostname/ip, [grep filter])"); - } - const server = safeGetServer(hostname, "ls"); - - // Get the grep filter, if one exists - let filter = ""; - if (arguments.length >= 2) { - filter = grep.toString(); - } - - const allFiles = []; - for (let i = 0; i < server.programs.length; i++) { - if (filter) { - if (server.programs[i].includes(filter)) { + 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]); } - } else { - allFiles.push(server.programs[i]); } - } - for (let i = 0; i < server.scripts.length; i++) { - if (filter) { - if (server.scripts[i].filename.includes(filter)) { + 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); } - } 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); + 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]); } - } else { - allFiles.push(server.messages[i]); } - } - for (let i = 0; i < server.textFiles.length; i++) { - if (filter) { - if (server.textFiles[i].fn.includes(filter)) { + 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); } - } 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)) { + 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); } - } else { - allFiles.push(server.contracts[i].fn); } - } - // Sort the files alphabetically then print each - allFiles.sort(); - return allFiles; - }, - getRecentScripts: function (): IRecentScript[] { - updateDynamicRam("getRecentScripts", getRamCost(Player, "getRecentScripts")); + // Sort the files alphabetically then print each + allFiles.sort(); + return allFiles; + }, + getRecentScripts: () => (): IRecentScript[] => { return recentScripts.map((rs) => ({ timeOfDeath: rs.timeOfDeath, ...createPublicRunningScript(rs.runningScript), })); }, - ps: function (_hostname: unknown = workerScript.hostname): ProcessInfo[] { - updateDynamicRam("ps", getRamCost(Player, "ps")); - const hostname = helper.string("ps", "hostname", _hostname); - const server = safeGetServer(hostname, "ps"); - 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: function (_hostname: unknown): boolean { - updateDynamicRam("hasRootAccess", getRamCost(Player, "hasRootAccess")); - const hostname = helper.string("hasRootAccess", "hostname", _hostname); - if (hostname === undefined) { - throw makeRuntimeErrorMsg("hasRootAccess", "Takes 1 argument"); - } - const server = safeGetServer(hostname, "hasRootAccess"); - return server.hasAdminRights; - }, - getHostname: function (): string { - updateDynamicRam("getHostname", getRamCost(Player, "getHostname")); + 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); + + const server = safeGetServer(hostname, ctx); + return server.hasAdminRights; + }, + getHostname: (ctx: NetscriptContext) => (): string => { const scriptServer = GetServer(workerScript.hostname); if (scriptServer == null) { - throw makeRuntimeErrorMsg("getHostname", "Could not find server. This is a bug. Report to dev."); + throw ctx.makeRuntimeErrorMsg("Could not find server. This is a bug. Report to dev."); } return scriptServer.hostname; }, - getHackingLevel: function (): number { - updateDynamicRam("getHackingLevel", getRamCost(Player, "getHackingLevel")); + getHackingLevel: (ctx: NetscriptContext) => (): number => { Player.updateSkillLevels(); - workerScript.log("getHackingLevel", () => `returned ${Player.hacking}`); + ctx.log(() => `returned ${Player.hacking}`); return Player.hacking; }, - getHackingMultipliers: function (): HackingMultipliers { - updateDynamicRam("getHackingMultipliers", getRamCost(Player, "getHackingMultipliers")); + getHackingMultipliers: () => (): HackingMultipliers => { return { chance: Player.hacking_chance_mult, speed: Player.hacking_speed_mult, @@ -1572,8 +1517,7 @@ export function NetscriptFunctions(workerScript: WorkerScript): NS { growth: Player.hacking_grow_mult, }; }, - getHacknetMultipliers: function (): HacknetMultipliers { - updateDynamicRam("getHacknetMultipliers", getRamCost(Player, "getHacknetMultipliers")); + getHacknetMultipliers: () => (): HacknetMultipliers => { return { production: Player.hacknet_node_money_mult, purchaseCost: Player.hacknet_node_purchase_cost_mult, @@ -1582,867 +1526,837 @@ export function NetscriptFunctions(workerScript: WorkerScript): NS { levelCost: Player.hacknet_node_level_cost_mult, }; }, - getBitNodeMultipliers: function (): IBNMults { - updateDynamicRam("getBitNodeMultipliers", getRamCost(Player, "getBitNodeMultipliers")); + getBitNodeMultipliers: (ctx: NetscriptContext) => (): IBNMults => { if (Player.sourceFileLvl(5) <= 0 && Player.bitNodeN !== 5) { - throw makeRuntimeErrorMsg("getBitNodeMultipliers", "Requires Source-File 5 to run."); + throw ctx.makeRuntimeErrorMsg("Requires Source-File 5 to run."); } const copy = Object.assign({}, BitNodeMultipliers); return copy; }, - getServer: function (_hostname: unknown = workerScript.hostname): IServerDef { - updateDynamicRam("getServer", getRamCost(Player, "getServer")); - const hostname = helper.string("getServer", "hostname", _hostname); - const server = safeGetServer(hostname, "getServer"); - const copy = Object.assign({}, server) as any; - // These fields should be hidden. - copy.contracts = undefined; - copy.messages = undefined; - copy.runningScripts = undefined; - copy.scripts = undefined; - copy.textFiles = undefined; - copy.programs = undefined; - copy.serversOnNetwork = undefined; - 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: function (_hostname: unknown): number { - updateDynamicRam("getServerMoneyAvailable", getRamCost(Player, "getServerMoneyAvailable")); - const hostname = helper.string("getServerMoneyAvailable", "hostname", _hostname); - const server = safeGetServer(hostname, "getServerMoneyAvailable"); - if (!(server instanceof Server)) { - workerScript.log("getServerMoneyAvailable", () => "Cannot be executed on this server."); - return 0; - } - if (failOnHacknetServer(server, "getServerMoneyAvailable")) { - return 0; - } - if (server.hostname == "home") { - // Return player's money - workerScript.log( - "getServerMoneyAvailable", - () => `returned player's money: ${numeralWrapper.formatMoney(Player.money)}`, + 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 any; + // These fields should be hidden. + copy.contracts = undefined; + copy.messages = undefined; + copy.runningScripts = undefined; + copy.scripts = undefined; + copy.textFiles = undefined; + copy.programs = undefined; + copy.serversOnNetwork = undefined; + 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 Player.money; - } - workerScript.log( - "getServerMoneyAvailable", - () => `returned ${numeralWrapper.formatMoney(server.moneyAvailable)} for '${server.hostname}'`, - ); - return server.moneyAvailable; - }, - getServerSecurityLevel: function (_hostname: unknown): number { - updateDynamicRam("getServerSecurityLevel", getRamCost(Player, "getServerSecurityLevel")); - const hostname = helper.string("getServerSecurityLevel", "hostname", _hostname); - const server = safeGetServer(hostname, "getServerSecurityLevel"); - if (!(server instanceof Server)) { - workerScript.log("getServerSecurityLevel", () => "Cannot be executed on this server."); - return 1; - } - if (failOnHacknetServer(server, "getServerSecurityLevel")) { - return 1; - } - workerScript.log( - "getServerSecurityLevel", - () => `returned ${numeralWrapper.formatServerSecurity(server.hackDifficulty)} for '${server.hostname}'`, - ); - return server.hackDifficulty; - }, - getServerBaseSecurityLevel: function (_hostname: unknown): number { - updateDynamicRam("getServerBaseSecurityLevel", getRamCost(Player, "getServerBaseSecurityLevel")); - const hostname = helper.string("getServerBaseSecurityLevel", "hostname", _hostname); - workerScript.log( - "getServerBaseSecurityLevel", - () => `getServerBaseSecurityLevel is deprecated because it's not useful.`, - ); - const server = safeGetServer(hostname, "getServerBaseSecurityLevel"); - if (!(server instanceof Server)) { - workerScript.log("getServerBaseSecurityLevel", () => "Cannot be executed on this server."); - return 1; - } - if (failOnHacknetServer(server, "getServerBaseSecurityLevel")) { - return 1; - } - workerScript.log( - "getServerBaseSecurityLevel", - () => `returned ${numeralWrapper.formatServerSecurity(server.baseDifficulty)} for '${server.hostname}'`, - ); - return server.baseDifficulty; - }, - getServerMinSecurityLevel: function (_hostname: unknown): number { - updateDynamicRam("getServerMinSecurityLevel", getRamCost(Player, "getServerMinSecurityLevel")); - const hostname = helper.string("getServerMinSecurityLevel", "hostname", _hostname); - const server = safeGetServer(hostname, "getServerMinSecurityLevel"); - if (!(server instanceof Server)) { - workerScript.log("getServerMinSecurityLevel", () => "Cannot be executed on this server."); - return 1; - } - if (failOnHacknetServer(server, "getServerMinSecurityLevel")) { - return 1; - } - workerScript.log( - "getServerMinSecurityLevel", - () => `returned ${numeralWrapper.formatServerSecurity(server.minDifficulty)} for ${server.hostname}`, - ); - return server.minDifficulty; - }, - getServerRequiredHackingLevel: function (_hostname: unknown): number { - updateDynamicRam("getServerRequiredHackingLevel", getRamCost(Player, "getServerRequiredHackingLevel")); - const hostname = helper.string("getServerRequiredHackingLevel", "hostname", _hostname); - const server = safeGetServer(hostname, "getServerRequiredHackingLevel"); - if (!(server instanceof Server)) { - workerScript.log("getServerRequiredHackingLevel", () => "Cannot be executed on this server."); - return 1; - } - if (failOnHacknetServer(server, "getServerRequiredHackingLevel")) { - return 1; - } - workerScript.log( - "getServerRequiredHackingLevel", - () => `returned ${numeralWrapper.formatSkill(server.requiredHackingSkill)} for '${server.hostname}'`, - ); - return server.requiredHackingSkill; - }, - getServerMaxMoney: function (_hostname: unknown): number { - updateDynamicRam("getServerMaxMoney", getRamCost(Player, "getServerMaxMoney")); - const hostname = helper.string("getServerMaxMoney", "hostname", _hostname); - const server = safeGetServer(hostname, "getServerMaxMoney"); - if (!(server instanceof Server)) { - workerScript.log("getServerMaxMoney", () => "Cannot be executed on this server."); - return 0; - } - if (failOnHacknetServer(server, "getServerMaxMoney")) { - return 0; - } - workerScript.log( - "getServerMaxMoney", - () => `returned ${numeralWrapper.formatMoney(server.moneyMax)} for '${server.hostname}'`, - ); - return server.moneyMax; - }, - getServerGrowth: function (_hostname: unknown): number { - updateDynamicRam("getServerGrowth", getRamCost(Player, "getServerGrowth")); - const hostname = helper.string("getServerGrowth", "hostname", _hostname); - const server = safeGetServer(hostname, "getServerGrowth"); - if (!(server instanceof Server)) { - workerScript.log("getServerGrowth", () => "Cannot be executed on this server."); - return 1; - } - if (failOnHacknetServer(server, "getServerGrowth")) { - return 1; - } - workerScript.log("getServerGrowth", () => `returned ${server.serverGrowth} for '${server.hostname}'`); - return server.serverGrowth; - }, - getServerNumPortsRequired: function (_hostname: unknown): number { - updateDynamicRam("getServerNumPortsRequired", getRamCost(Player, "getServerNumPortsRequired")); - const hostname = helper.string("getServerNumPortsRequired", "hostname", _hostname); - const server = safeGetServer(hostname, "getServerNumPortsRequired"); - if (!(server instanceof Server)) { - workerScript.log("getServerNumPortsRequired", () => "Cannot be executed on this server."); - return 5; - } - if (failOnHacknetServer(server, "getServerNumPortsRequired")) { - return 5; - } - workerScript.log( - "getServerNumPortsRequired", - () => `returned ${server.numOpenPortsRequired} for '${server.hostname}'`, - ); - return server.numOpenPortsRequired; - }, - getServerRam: function (_hostname: unknown): [number, number] { - updateDynamicRam("getServerRam", getRamCost(Player, "getServerRam")); - const hostname = helper.string("getServerRam", "hostname", _hostname); - workerScript.log( - "getServerRam", - () => `getServerRam is deprecated in favor of getServerMaxRam / getServerUsedRam`, - ); - const server = safeGetServer(hostname, "getServerRam"); - workerScript.log( - "getServerRam", - () => `returned [${numeralWrapper.formatRAM(server.maxRam)}, ${numeralWrapper.formatRAM(server.ramUsed)}]`, - ); - return [server.maxRam, server.ramUsed]; - }, - getServerMaxRam: function (_hostname: unknown): number { - updateDynamicRam("getServerMaxRam", getRamCost(Player, "getServerMaxRam")); - const hostname = helper.string("getServerMaxRam", "hostname", _hostname); - const server = safeGetServer(hostname, "getServerMaxRam"); - workerScript.log("getServerMaxRam", () => `returned ${numeralWrapper.formatRAM(server.maxRam)}`); - return server.maxRam; - }, - getServerUsedRam: function (_hostname: unknown): number { - updateDynamicRam("getServerUsedRam", getRamCost(Player, "getServerUsedRam")); - const hostname = helper.string("getServerUsedRam", "hostname", _hostname); - const server = safeGetServer(hostname, "getServerUsedRam"); - workerScript.log("getServerUsedRam", () => `returned ${numeralWrapper.formatRAM(server.ramUsed)}`); - return server.ramUsed; - }, - serverExists: function (_hostname: unknown): boolean { - updateDynamicRam("serverExists", getRamCost(Player, "serverExists")); - const hostname = helper.string("serverExists", "hostname", _hostname); - return GetServer(hostname) !== null; - }, - fileExists: function (_filename: unknown, _hostname: unknown = workerScript.hostname): boolean { - updateDynamicRam("fileExists", getRamCost(Player, "fileExists")); - const filename = helper.string("fileExists", "filename", _filename); - const hostname = helper.string("fileExists", "hostname", _hostname); - if (filename === undefined) { - throw makeRuntimeErrorMsg("fileExists", "Usage: fileExists(scriptname, [server])"); - } - const server = safeGetServer(hostname, "fileExists"); - for (let i = 0; i < server.scripts.length; ++i) { - if (filename == server.scripts[i].filename) { - return true; + 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; } - } - for (let i = 0; i < server.programs.length; ++i) { - if (filename.toLowerCase() == server.programs[i].toLowerCase()) { - return true; + if (failOnHacknetServer(server, "getServerBaseSecurityLevel")) { + return 1; } - } - for (let i = 0; i < server.messages.length; ++i) { - if (filename.toLowerCase() === server.messages[i].toLowerCase()) { - return true; + 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; } - } - const txtFile = getTextFile(filename, server); - return txtFile != null; - }, - isRunning: function (fn: any, hostname: any = workerScript.hostname, ...scriptArgs: any[]): boolean { - updateDynamicRam("isRunning", getRamCost(Player, "isRunning")); - if (fn === undefined || hostname === undefined) { - throw makeRuntimeErrorMsg("isRunning", "Usage: isRunning(scriptname, server, [arg1], [arg2]...)"); - } - if (typeof fn === "number") { - return getRunningScriptByPid(fn, "isRunning") != null; - } else { - return getRunningScript(fn, hostname, "isRunning", scriptArgs) != null; - } - }, - getPurchasedServerLimit: function (): number { - updateDynamicRam("getPurchasedServerLimit", getRamCost(Player, "getPurchasedServerLimit")); - + 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: any, hostname: any = workerScript.hostname, ...scriptArgs: any[]): boolean => { + if (fn === undefined || hostname === undefined) { + throw ctx.makeRuntimeErrorMsg("Usage: isRunning(scriptname, server, [arg1], [arg2]...)"); + } + if (typeof fn === "number") { + return getRunningScriptByPid(fn) != null; + } else { + return getRunningScript(ctx, fn, hostname, scriptArgs) != null; + } + }, + getPurchasedServerLimit: () => (): number => { return getPurchaseServerLimit(); }, - getPurchasedServerMaxRam: function (): number { - updateDynamicRam("getPurchasedServerMaxRam", getRamCost(Player, "getPurchasedServerMaxRam")); - + getPurchasedServerMaxRam: () => (): number => { return getPurchaseServerMaxRam(); }, - getPurchasedServerCost: function (_ram: unknown): number { - updateDynamicRam("getPurchasedServerCost", getRamCost(Player, "getPurchasedServerCost")); - const ram = helper.number("getPurchasedServerCost", "ram", _ram); + getPurchasedServerCost: + (ctx: NetscriptContext) => + (_ram: unknown): number => { + const ram = ctx.helper.number("ram", _ram); - const cost = getPurchaseServerCost(ram); - if (cost === Infinity) { - workerScript.log("getPurchasedServerCost", () => `Invalid argument: ram='${ram}'`); - return Infinity; - } + const cost = getPurchaseServerCost(ram); + if (cost === Infinity) { + ctx.log(() => `Invalid argument: ram='${ram}'`); + return Infinity; + } - return cost; - }, - purchaseServer: function (_name: unknown, _ram: unknown): string { - updateDynamicRam("purchaseServer", getRamCost(Player, "purchaseServer")); - const name = helper.string("purchaseServer", "name", _name); - const ram = helper.number("purchaseServer", "ram", _ram); - if (arguments.length !== 2) throw makeRuntimeErrorMsg("purchaseServer", "Takes 2 arguments"); - let hostnameStr = String(name); - hostnameStr = hostnameStr.replace(/\s+/g, ""); - if (hostnameStr == "") { - workerScript.log("purchaseServer", () => `Invalid argument: hostname='${hostnameStr}'`); - return ""; - } + return cost; + }, + purchaseServer: + (ctx: NetscriptContext) => + (_name: unknown, _ram: unknown): string => { + const name = ctx.helper.string("name", _name); + const ram = ctx.helper.number("ram", _ram); + if (arguments.length !== 2) throw ctx.makeRuntimeErrorMsg("Takes 2 arguments"); + let hostnameStr = String(name); + hostnameStr = hostnameStr.replace(/\s+/g, ""); + if (hostnameStr == "") { + ctx.log(() => `Invalid argument: hostname='${hostnameStr}'`); + return ""; + } - if (Player.purchasedServers.length >= getPurchaseServerLimit()) { - workerScript.log( - "purchaseServer", - () => - `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()) { - workerScript.log( - "purchaseServer", - () => `Invalid argument: ram='${ram}' must not be greater than getPurchaseServerMaxRam`, + if (Player.purchasedServers.length >= getPurchaseServerLimit()) { + ctx.log( + () => + `You have reached the maximum limit of ${getPurchaseServerLimit()} servers. You cannot purchase any more.`, ); - } else { - workerScript.log("purchaseServer", () => `Invalid argument: ram='${ram}' must be a positive power of 2`); + return ""; } - return ""; - } + 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`); + } - if (Player.money < cost) { - workerScript.log( - "purchaseServer", - () => `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"); - workerScript.log( - "purchaseServer", - () => `Purchased new server with hostname '${newServ.hostname}' for ${numeralWrapper.formatMoney(cost)}`, - ); - return newServ.hostname; - }, - deleteServer: function (_name: unknown): boolean { - updateDynamicRam("deleteServer", getRamCost(Player, "deleteServer")); - const name = helper.string("purchaseServer", "name", _name); - let hostnameStr = String(name); - hostnameStr = hostnameStr.replace(/\s\s+/g, ""); - const server = GetServer(hostnameStr); - if (!(server instanceof Server)) { - workerScript.log("deleteServer", () => `Invalid argument: hostname='${hostnameStr}'`); - return false; - } - - if (!server.purchasedByPlayer || server.hostname === "home") { - workerScript.log("deleteServer", () => "Cannot delete non-purchased server."); - return false; - } - - const hostname = server.hostname; - - // Can't delete server you're currently connected to - if (server.isConnectedTo) { - workerScript.log("deleteServer", () => "You are currently connected to the server you are trying to delete."); - return false; - } - - // A server cannot delete itself - if (hostname === workerScript.hostname) { - workerScript.log("deleteServer", () => "Cannot delete the server this script is running on."); - return false; - } - - // Delete all scripts running on server - if (server.runningScripts.length > 0) { - workerScript.log( - "deleteServer", - () => `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; + return ""; } - } - if (!found) { - workerScript.log( - "deleteServer", - () => `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); - workerScript.log("deleteServer", () => `Deleted server '${hostnameStr}`); - return true; + if (Player.money < cost) { + ctx.log(() => `Not enough money to purchase server. Need ${numeralWrapper.formatMoney(cost)}`); + return ""; } - } - // Wasn't found on home computer - workerScript.log( - "deleteServer", - () => `Could not find server ${hostname} as a purchased server. This is a bug. Report to dev.`, - ); - return false; - }, - getPurchasedServers: function (): string[] { - updateDynamicRam("getPurchasedServers", getRamCost(Player, "getPurchasedServers")); + 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)}`, + ); + 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; + } + + if (!server.purchasedByPlayer || server.hostname === "home") { + ctx.log(() => "Cannot delete non-purchased server."); + return false; + } + + const hostname = server.hostname; + + // 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; + } + + // A server cannot delete itself + if (hostname === workerScript.hostname) { + ctx.log(() => "Cannot delete the server this script is running on."); + return false; + } + + // 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 + 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) { + 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: function (_port: unknown, data: any = ""): Promise { - updateDynamicRam("writePort", getRamCost(Player, "writePort")); - const port = helper.number("writePort", "port", _port); - if (typeof data !== "string" && typeof data !== "number") { - throw makeRuntimeErrorMsg( - "writePort", - `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: function (_port: unknown, data: any = "", _mode: unknown = "a"): Promise { - updateDynamicRam("write", getRamCost(Player, "write")); - const port = helper.string("write", "port", _port); - const mode = helper.string("write", "mode", _mode); - if (isString(port)) { - // Write to script or text file - let fn = port; - if (!isValidFilePath(fn)) { - throw makeRuntimeErrorMsg("write", `Invalid filepath: ${fn}`); + writePort: + (ctx: NetscriptContext) => + (_port: unknown, data: any = ""): 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: any = "", _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); - } + if (fn.lastIndexOf("/") === 0) { + fn = removeLeadingSlash(fn); + } - // Coerce 'data' to be a string - try { - data = String(data); - } catch (e: any) { - throw makeRuntimeErrorMsg("write", `Invalid data (${e}). Data being written must be convertible to a string`); - } + // Coerce 'data' to be a string + try { + data = String(data); + } catch (e: any) { + throw ctx.makeRuntimeErrorMsg(`Invalid data (${e}). Data being written must be convertible to a string`); + } - const server = workerScript.getServer(); - if (server == null) { - throw makeRuntimeErrorMsg("write", "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, data, server.hostname, server.scripts); - server.scripts.push(script); + 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, data, server.hostname, server.scripts); + server.scripts.push(script); + return script.updateRamUsage(Player, server.scripts); + } + mode === "w" ? (script.code = data) : (script.code += data); return script.updateRamUsage(Player, server.scripts); - } - mode === "w" ? (script.code = 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, data, server); - return Promise.resolve(); - } - if (mode === "w") { - txtFile.write(data); } else { - txtFile.append(data); + // Write to text file + const txtFile = getTextFile(fn, server); + if (txtFile == null) { + createTextFile(fn, data, server); + return Promise.resolve(); + } + if (mode === "w") { + txtFile.write(data); + } else { + txtFile.append(data); + } } + return Promise.resolve(); + } else { + throw ctx.makeRuntimeErrorMsg(`Invalid argument: ${port}`); } - return Promise.resolve(); - } else { - throw makeRuntimeErrorMsg("write", `Invalid argument: ${port}`); - } - }, - tryWritePort: function (_port: unknown, data: any = ""): Promise { - updateDynamicRam("tryWritePort", getRamCost(Player, "tryWritePort")); - let port = helper.number("tryWritePort", "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) { + }, + tryWritePort: + (ctx: NetscriptContext) => + (_port: unknown, data: any = ""): Promise => { + let port = ctx.helper.number("port", _port); + if (typeof data !== "string" && typeof data !== "number") { throw makeRuntimeErrorMsg( "tryWritePort", - `Invalid port: ${port}. Only ports 1-${CONSTANTS.NumNetscriptPorts} are valid.`, + `Trying to write invalid data to a port: only strings and numbers are valid.`, ); } - const iport = NetscriptPorts[port - 1]; - if (iport == null || !(iport instanceof Object)) { - throw makeRuntimeErrorMsg("tryWritePort", `Could not find port: ${port}. This is a bug. Report to dev.`); - } - return Promise.resolve(iport.tryWrite(data)); - } else { - throw makeRuntimeErrorMsg("tryWritePort", `Invalid argument: ${port}`); - } - }, - readPort: function (_port: unknown): any { - updateDynamicRam("readPort", getRamCost(Player, "readPort")); - const port = helper.number("readPort", "port", _port); - // Read from port - const iport = helper.getValidPort("readPort", port); - const x = iport.read(); - return x; - }, - read: function (_port: unknown): string { - updateDynamicRam("read", getRamCost(Player, "read")); - const port = helper.string("read", "port", _port); - if (isString(port)) { - // Read from script or text file - const fn = port; - const server = GetServer(workerScript.hostname); - if (server == null) { - throw makeRuntimeErrorMsg("read", "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 ""; + 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.`, + ); } - return script.code; + 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 { - // Read from text file - const txtFile = getTextFile(fn, server); - if (txtFile !== null) { - return txtFile.text; + throw ctx.makeRuntimeErrorMsg(`Invalid argument: ${port}`); + } + }, + readPort: + (ctx: NetscriptContext) => + (_port: unknown): any => { + 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 { - return ""; + // 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): any => { + 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: any): boolean => { + const fn = ctx.helper.string("fn", _fn); + + if (hostname == null || hostname === "") { + hostname = workerScript.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; } } - } else { - throw makeRuntimeErrorMsg("read", `Invalid argument: ${port}`); - } - }, - peek: function (_port: unknown): any { - updateDynamicRam("peek", getRamCost(Player, "peek")); - const port = helper.number("peek", "port", _port); - const iport = helper.getValidPort("peek", port); - const x = iport.peek(); - return x; - }, - clear: function (_file: unknown): void { - updateDynamicRam("clear", getRamCost(Player, "clear")); - const file = helper.string("peek", "file", _file); - if (isString(file)) { - // Clear text file - const fn = file; - const server = GetServer(workerScript.hostname); - if (server == null) { - throw makeRuntimeErrorMsg("clear", "Error getting Server. This is a bug. Report to dev."); + 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(server.runningScripts[i], server.hostname); + suc = true; + i--; + } } - const txtFile = getTextFile(fn, server); - if (txtFile != null) { - txtFile.write(""); - } - } else { - throw makeRuntimeErrorMsg("clear", `Invalid argument: ${file}`); - } - }, - clearPort: function (_port: unknown): void { - updateDynamicRam("clearPort", getRamCost(Player, "clearPort")); - const port = helper.number("clearPort", "port", _port); - // Clear port - const iport = helper.getValidPort("clearPort", port); - iport.clear(); - }, - getPortHandle: function (_port: unknown): IPort { - updateDynamicRam("getPortHandle", getRamCost(Player, "getPortHandle")); - const port = helper.number("getPortHandle", "port", _port); - const iport = helper.getValidPort("getPortHandle", port); - return iport; - }, - rm: function (_fn: unknown, hostname: any): boolean { - updateDynamicRam("rm", getRamCost(Player, "rm")); - const fn = helper.string("rm", "fn", _fn); - - if (hostname == null || hostname === "") { - hostname = workerScript.hostname; - } - const s = safeGetServer(hostname, "rm"); - - const status = s.removeFile(fn); - if (!status.res) { - workerScript.log("rm", () => status.msg + ""); - } - - return status.res; - }, - scriptRunning: function (_scriptname: unknown, _hostname: unknown): boolean { - updateDynamicRam("scriptRunning", getRamCost(Player, "scriptRunning")); - const scriptname = helper.string("scriptRunning", "scriptname", _scriptname); - const hostname = helper.string("scriptRunning", "hostname", _hostname); - const server = safeGetServer(hostname, "scriptRunning"); - for (let i = 0; i < server.runningScripts.length; ++i) { - if (server.runningScripts[i].filename == scriptname) { - return true; - } - } - return false; - }, - scriptKill: function (_scriptname: unknown, _hostname: unknown): boolean { - updateDynamicRam("scriptKill", getRamCost(Player, "scriptKill")); - const scriptname = helper.string("scriptKill", "scriptname", _scriptname); - const hostname = helper.string("scriptKill", "hostname", _hostname); - const server = safeGetServer(hostname, "scriptKill"); - let suc = false; - for (let i = 0; i < server.runningScripts.length; i++) { - if (server.runningScripts[i].filename == scriptname) { - killWorkerScript(server.runningScripts[i], server.hostname); - suc = true; - i--; - } - } - return suc; - }, - getScriptName: function (): string { - updateDynamicRam("getScriptName", getRamCost(Player, "getScriptName")); + return suc; + }, + getScriptName: () => (): string => { return workerScript.name; }, - getScriptRam: function (_scriptname: unknown, _hostname: unknown = workerScript.hostname): number { - updateDynamicRam("getScriptRam", getRamCost(Player, "getScriptRam")); - const scriptname = helper.string("getScriptRam", "scriptname", _scriptname); - const hostname = helper.string("getScriptRam", "hostname", _hostname); - const server = safeGetServer(hostname, "getScriptRam"); - for (let i = 0; i < server.scripts.length; ++i) { - if (server.scripts[i].filename == scriptname) { - return server.scripts[i].ramUsage; + 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: function (fn: any, hostname: any, ...args: any[]): IRunningScriptDef | null { - updateDynamicRam("getRunningScript", getRamCost(Player, "getRunningScript")); - - let runningScript; - if (fn === undefined && hostname === undefined && args.length === 0) { - runningScript = workerScript.scriptRef; - } else if (typeof fn === "number") { - runningScript = getRunningScriptByPid(fn, "getRunningScript"); - } else { - runningScript = getRunningScript(fn, hostname, "getRunningScript", args); - } - if (runningScript === null) return null; - return createPublicRunningScript(runningScript); - }, - getHackTime: function (_hostname: unknown = workerScript.hostname): number { - updateDynamicRam("getHackTime", getRamCost(Player, "getHackTime")); - const hostname = helper.string("getHackTime", "hostname", _hostname); - const server = safeGetServer(hostname, "getHackTime"); - if (!(server instanceof Server)) { - workerScript.log("getHackTime", () => "invalid for this kind of server"); - return Infinity; - } - if (failOnHacknetServer(server, "getHackTime")) { - return Infinity; - } - - return calculateHackingTime(server, Player) * 1000; - }, - getGrowTime: function (_hostname: unknown = workerScript.hostname): number { - updateDynamicRam("getGrowTime", getRamCost(Player, "getGrowTime")); - const hostname = helper.string("getGrowTime", "hostname", _hostname); - const server = safeGetServer(hostname, "getGrowTime"); - if (!(server instanceof Server)) { - workerScript.log("getGrowTime", () => "invalid for this kind of server"); - return Infinity; - } - if (failOnHacknetServer(server, "getGrowTime")) { - return Infinity; - } - - return calculateGrowTime(server, Player) * 1000; - }, - getWeakenTime: function (_hostname: unknown = workerScript.hostname): number { - updateDynamicRam("getWeakenTime", getRamCost(Player, "getWeakenTime")); - const hostname = helper.string("getWeakenTime", "hostname", _hostname); - const server = safeGetServer(hostname, "getWeakenTime"); - if (!(server instanceof Server)) { - workerScript.log("getWeakenTime", () => "invalid for this kind of server"); - return Infinity; - } - if (failOnHacknetServer(server, "getWeakenTime")) { - return Infinity; - } - - return calculateWeakenTime(server, Player) * 1000; - }, - getScriptIncome: function (scriptname?: any, hostname?: any, ...args: any[]): any { - updateDynamicRam("getScriptIncome", getRamCost(Player, "getScriptIncome")); - if (arguments.length === 0) { - const res = []; - - // 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 0; + }, + getRunningScript: + (ctx: NetscriptContext) => + (fn: any, hostname: any, ...args: any[]): IRunningScriptDef | null => { + let runningScript; + if (fn === undefined && hostname === undefined && args.length === 0) { + runningScript = workerScript.scriptRef; + } else if (typeof fn === "number") { + runningScript = getRunningScriptByPid(fn); + } else { + runningScript = getRunningScript(ctx, fn, hostname, args); + } + 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; } - res.push(total); - // Second element is total income you've earned from scripts since you installed Augs - res.push(Player.scriptProdSinceLastAug / (Player.playtimeSinceLastAug / 1000)); - return res; - } else { - // Get income for a particular script - const server = safeGetServer(hostname, "getScriptIncome"); - const runningScriptObj = findRunningScript(scriptname, args, server); - if (runningScriptObj == null) { - workerScript.log( - "getScriptIncome", - () => `No such script '${scriptname}' on '${server.hostname}' with args: ${arrayToString(args)}`, - ); - return -1; + 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; } - return runningScriptObj.onlineMoneyMade / runningScriptObj.onlineRunningTime; - } - }, - getScriptExpGain: function (scriptname?: any, hostname?: any, ...args: any[]): number { - updateDynamicRam("getScriptExpGain", getRamCost(Player, "getScriptExpGain")); - if (arguments.length === 0) { - let total = 0; - for (const ws of workerScripts.values()) { - total += ws.scriptRef.onlineExpGained / ws.scriptRef.onlineRunningTime; + if (failOnHacknetServer(server, "getGrowTime")) { + return Infinity; } - return total; - } else { - // Get income for a particular script - const server = safeGetServer(hostname, "getScriptExpGain"); - const runningScriptObj = findRunningScript(scriptname, args, server); - if (runningScriptObj == null) { - workerScript.log( - "getScriptExpGain", - () => `No such script '${scriptname}' on '${server.hostname}' with args: ${arrayToString(args)}`, - ); - return -1; - } - return runningScriptObj.onlineExpGained / runningScriptObj.onlineRunningTime; - } - }, - nFormat: function (_n: unknown, _format: unknown): string { - updateDynamicRam("nFormat", getRamCost(Player, "nFormat")); - const n = helper.number("nFormat", "n", _n); - const format = helper.string("nFormat", "format", _format); - if (isNaN(n)) { - return ""; - } - return numeralWrapper.format(n, format); - }, - tFormat: function (_milliseconds: unknown, _milliPrecision: unknown = false): string { - updateDynamicRam("tFormat", getRamCost(Player, "tFormat")); - const milliseconds = helper.number("tFormat", "milliseconds", _milliseconds); - const milliPrecision = helper.boolean(_milliPrecision); - return convertTimeMsToTimeElapsedString(milliseconds, milliPrecision); - }, - getTimeSinceLastAug: function (): number { - updateDynamicRam("getTimeSinceLastAug", getRamCost(Player, "getTimeSinceLastAug")); + 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; + }, + getScriptIncome: + (ctx: NetscriptContext) => + (scriptname?: any, hostname?: any, ...args: any[]): any => { + if (arguments.length === 0) { + const res = []; + + // 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; + } + res.push(total); + + // Second element is total income you've earned from scripts since you installed Augs + res.push(Player.scriptProdSinceLastAug / (Player.playtimeSinceLastAug / 1000)); + return res; + } else { + // Get income for a particular script + const server = safeGetServer(hostname, ctx); + const runningScriptObj = findRunningScript(scriptname, args, server); + if (runningScriptObj == null) { + ctx.log(() => `No such script '${scriptname}' on '${server.hostname}' with args: ${arrayToString(args)}`); + return -1; + } + return runningScriptObj.onlineMoneyMade / runningScriptObj.onlineRunningTime; + } + }, + getScriptExpGain: + (ctx: NetscriptContext) => + (scriptname?: any, hostname?: any, ...args: any[]): number => { + if (arguments.length === 0) { + let total = 0; + for (const ws of workerScripts.values()) { + total += ws.scriptRef.onlineExpGained / ws.scriptRef.onlineRunningTime; + } + return total; + } else { + // Get income for a particular script + const server = safeGetServer(hostname, ctx); + const runningScriptObj = findRunningScript(scriptname, args, server); + if (runningScriptObj == null) { + ctx.log(() => `No such script '${scriptname}' on '${server.hostname}' with args: ${arrayToString(args)}`); + return -1; + } + return runningScriptObj.onlineExpGained / runningScriptObj.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: function (_message: unknown): void { - updateDynamicRam("alert", getRamCost(Player, "alert")); - const message = helper.string("alert", "message", _message); - dialogBoxCreate(message); - }, - toast: function (_message: unknown, _variant: unknown = ToastVariant.SUCCESS, duration: any = 2000): void { - updateDynamicRam("toast", getRamCost(Player, "toast")); - const message = helper.string("toast", "message", _message); - const variant = helper.string("toast", "variant", _variant); - if (!checkEnum(ToastVariant, variant)) - throw new Error(`variant must be one of ${Object.values(ToastVariant).join(", ")}`); - SnackbarEvents.emit(message, variant, duration); - }, - prompt: function (_txt: unknown, options?: { type?: string; options?: string[] }): Promise { - updateDynamicRam("prompt", getRamCost(Player, "prompt")); - const txt = helper.string("toast", "txt", _txt); + 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: any = 2000): void => { + const message = ctx.helper.string("message", _message); + const variant = ctx.helper.string("variant", _variant); + 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, + return new Promise(function (resolve) { + PromptEvent.emit({ + txt: txt, + options, + resolve: resolve, + }); }); - }); - }, - wget: async function ( - _url: unknown, - _target: unknown, - _hostname: unknown = workerScript.hostname, - ): Promise { - updateDynamicRam("wget", getRamCost(Player, "wget")); - const url = helper.string("wget", "url", _url); - const target = helper.string("wget", "target", _target); - const hostname = helper.string("wget", "hostname", _hostname); - if (!isScriptFilename(target) && !target.endsWith(".txt")) { - workerScript.log("wget", () => `Invalid target file: '${target}'. Must be a script or text file.`); - return Promise.resolve(false); - } - const s = safeGetServer(hostname, "wget"); - 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) { - workerScript.log("wget", () => "Failed."); - return resolve(false); - } - if (res.overwritten) { - workerScript.log( - "wget", - () => `Successfully retrieved content and overwrote '${target}' on '${hostname}'`, - ); + }, + 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.`); + 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); - } - workerScript.log("wget", () => `Successfully retrieved content to new file '${target}' on '${hostname}'`); - return resolve(true); - }, - "text", - ).fail(function (e) { - workerScript.log("wget", () => JSON.stringify(e)); - return resolve(false); + }, + "text", + ).fail(function (e) { + ctx.log(() => JSON.stringify(e)); + return resolve(false); + }); }); - }); - }, - getFavorToDonate: function (): number { - updateDynamicRam("getFavorToDonate", getRamCost(Player, "getFavorToDonate")); + }, + getFavorToDonate: () => (): number => { return Math.floor(CONSTANTS.BaseFavorToDonate * BitNodeMultipliers.RepToDonateToFaction); }, - getOwnedSourceFiles: function (): SourceFileLvl[] { - updateDynamicRam("getOwnedSourceFiles", getRamCost(Player, "getOwnedSourceFiles")); + getOwnedSourceFiles: () => (): SourceFileLvl[] => { const res: SourceFileLvl[] = []; for (let i = 0; i < Player.sourceFiles.length; ++i) { res.push({ n: Player.sourceFiles[i].n, lvl: Player.sourceFiles[i].lvl, - } as SourceFileLvl); + }); } return res; }, - getPlayer: function (): INetscriptPlayer { - updateDynamicRam("getPlayer", getRamCost(Player, "getPlayer")); - + getPlayer: () => (): INetscriptPlayer => { const data = { hacking: Player.hacking, hp: Player.hp, @@ -2537,87 +2451,86 @@ export function NetscriptFunctions(workerScript: WorkerScript): NS { Object.assign(data.jobs, Player.jobs); return data; }, - atExit: function (f: any): void { - updateDynamicRam("atExit", getRamCost(Player, "atExit")); - if (typeof f !== "function") { - throw makeRuntimeErrorMsg("atExit", "argument should be function"); - } - workerScript.atExit = () => { - f(); - }; // Wrap the user function to prevent WorkerScript leaking as 'this' - }, - mv: function (_host: unknown, _source: unknown, _destination: unknown): void { - updateDynamicRam("mv", getRamCost(Player, "mv")); - const host = helper.string("mv", "host", _host); - const source = helper.string("mv", "source", _source); - const destination = helper.string("mv", "destination", _destination); - - if (!isValidFilePath(source)) throw makeRuntimeErrorMsg("mv", `Invalid filename: '${source}'`); - if (!isValidFilePath(destination)) throw makeRuntimeErrorMsg("mv", `Invalid filename: '${destination}'`); - - const source_is_txt = source.endsWith(".txt"); - const dest_is_txt = destination.endsWith(".txt"); - - if (!isScriptFilename(source) && !source_is_txt) - throw makeRuntimeErrorMsg("mv", `'mv' can only be used on scripts and text files (.txt)`); - if (source_is_txt != dest_is_txt) - throw makeRuntimeErrorMsg("mv", `Source and destination files must have the same type`); - - if (source === destination) { - return; - } - - // This will throw if the server is not found, we do not need to validate result. - const destServer: BaseServer | null = safeGetServer(host, "mv"); - - if (!source_is_txt && destServer.isRunning(source)) - throw makeRuntimeErrorMsg("mv", `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; + atExit: + (ctx: NetscriptContext) => + (f: any): void => { + if (typeof f !== "function") { + throw ctx.makeRuntimeErrorMsg("argument should be function"); } - } + 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); - if (source_file == null) throw makeRuntimeErrorMsg("mv", `Source file ${source} does not exist`); + if (!isValidFilePath(source)) throw ctx.makeRuntimeErrorMsg(`Invalid filename: '${source}'`); + if (!isValidFilePath(destination)) throw ctx.makeRuntimeErrorMsg(`Invalid filename: '${destination}'`); - 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(); + const source_is_txt = source.endsWith(".txt"); + const dest_is_txt = destination.endsWith(".txt"); + + 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`); + + if (source === destination) { + return; } - destServer.removeFile(source); - } else { - source_file.filename = destination; - if (source_file instanceof Script) { - source_file.markUpdated(); + // This will throw if the server is not found, we do not need to validate result. + const destServer: BaseServer | null = safeGetServer(host, ctx); + + 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), - enums: { - toast: ToastVariant, - }, }; // add undocumented functions const ns = { ...base, - ...extra, + ...NetscriptExtra(Player), }; function getFunctionNames(obj: any, prefix: string): string[] { const functionNames: string[] = []; @@ -2635,5 +2548,6 @@ export function NetscriptFunctions(workerScript: WorkerScript): NS { const possibleLogs = Object.fromEntries([...getFunctionNames(ns, "")].map((a) => [a, true])); - return ns; + const wrappedNS = wrapAPI(helper, {}, workerScript, ns) as unknown as INS; + return wrappedNS; } diff --git a/src/NetscriptFunctions/Extra.ts b/src/NetscriptFunctions/Extra.ts index 4fc5a888a..916ad96da 100644 --- a/src/NetscriptFunctions/Extra.ts +++ b/src/NetscriptFunctions/Extra.ts @@ -1,9 +1,8 @@ -import { WorkerScript } from "../Netscript/WorkerScript"; import { IPlayer } from "../PersonObjects/IPlayer"; import { Exploit } from "../Exploits/Exploit"; import * as bcrypt from "bcryptjs"; -import { INetscriptHelper } from "./INetscriptHelper"; import { Apr1Events as devMenu } from "../ui/Apr1"; +import { InternalAPI, NetscriptContext } from "../Netscript/APIWrapper"; export interface INetscriptExtra { heart: { @@ -16,35 +15,37 @@ export interface INetscriptExtra { rainbow(guess: string): void; } -export function NetscriptExtra(player: IPlayer, workerScript: WorkerScript, helper: INetscriptHelper): INetscriptExtra { +export function NetscriptExtra(player: IPlayer): InternalAPI { return { heart: { // Easter egg function - break: function (): number { + break: () => (): number => { return player.karma; }, }, - openDevMenu: function (): void { + openDevMenu: () => (): void => { devMenu.emit(); }, - exploit: function (): void { + exploit: () => (): void => { player.giveExploit(Exploit.UndocumentedFunctionCall); }, - bypass: function (doc: unknown): void { - // reset both fields first - const d = doc as any; - d.completely_unused_field = undefined; - const real_document: any = document; - real_document.completely_unused_field = undefined; - // set one to true and check that it affected the other. - real_document.completely_unused_field = true; - if (d.completely_unused_field && workerScript.ramUsage === 1.6) { - player.giveExploit(Exploit.Bypass); - } - d.completely_unused_field = undefined; - real_document.completely_unused_field = undefined; - }, - alterReality: function (): void { + bypass: + (ctx: NetscriptContext) => + (doc: unknown): void => { + // reset both fields first + const d = doc as any; + d.completely_unused_field = undefined; + const real_document: any = document; + real_document.completely_unused_field = undefined; + // set one to true and check that it affected the other. + real_document.completely_unused_field = true; + if (d.completely_unused_field && ctx.workerScript.ramUsage === 1.6) { + player.giveExploit(Exploit.Bypass); + } + d.completely_unused_field = undefined; + real_document.completely_unused_field = undefined; + }, + alterReality: () => (): void => { // We need to trick webpack into not optimizing a variable that is guaranteed to be false (and doesn't use prototypes) let x = false; const recur = function (depth: number): void { @@ -59,20 +60,22 @@ export function NetscriptExtra(player: IPlayer, workerScript: WorkerScript, help player.giveExploit(Exploit.RealityAlteration); } }, - rainbow: function (guess: unknown): boolean { - function tryGuess(): boolean { - // eslint-disable-next-line no-sync - const verified = bcrypt.compareSync( - helper.string("rainbow", "guess", guess), - "$2a$10$aertxDEkgor8baVtQDZsLuMwwGYmkRM/ohcA6FjmmzIHQeTCsrCcO", - ); - if (verified) { - player.giveExploit(Exploit.INeedARainbow); - return true; + rainbow: + (ctx: NetscriptContext) => + (guess: unknown): boolean => { + function tryGuess(): boolean { + // eslint-disable-next-line no-sync + const verified = bcrypt.compareSync( + ctx.helper.string("guess", guess), + "$2a$10$aertxDEkgor8baVtQDZsLuMwwGYmkRM/ohcA6FjmmzIHQeTCsrCcO", + ); + if (verified) { + player.giveExploit(Exploit.INeedARainbow); + return true; + } + return false; } - return false; - } - return tryGuess(); - }, + return tryGuess(); + }, }; } diff --git a/src/NetscriptFunctions/Formulas.ts b/src/NetscriptFunctions/Formulas.ts index fb6c4cd56..2c8124ca0 100644 --- a/src/NetscriptFunctions/Formulas.ts +++ b/src/NetscriptFunctions/Formulas.ts @@ -39,212 +39,267 @@ import { } from "../Gang/formulas/formulas"; import { favorToRep as calculateFavorToRep, repToFavor as calculateRepToFavor } from "../Faction/formulas/favor"; import { repFromDonation } from "../Faction/formulas/donation"; +import { InternalAPI, NetscriptContext } from "../Netscript/APIWrapper"; -export function NetscriptFormulas(player: IPlayer, workerScript: WorkerScript, helper: INetscriptHelper): IFormulas { - const checkFormulasAccess = function (func: string): void { +export function NetscriptFormulas(player: IPlayer, helper: INetscriptHelper): InternalAPI { + const checkFormulasAccess = function (ctx: NetscriptContext): void { if (!player.hasProgram(Programs.Formulas.name)) { - throw helper.makeRuntimeErrorMsg(`formulas.${func}`, `Requires Formulas.exe to run.`); + throw helper.makeRuntimeErrorMsg(`formulas.${ctx.function}`, `Requires Formulas.exe to run.`); } }; return { reputation: { - calculateFavorToRep: function (_favor: unknown): number { - const favor = helper.number("calculateFavorToRep", "favor", _favor); - checkFormulasAccess("reputation.calculateFavorToRep"); - return calculateFavorToRep(favor); - }, - calculateRepToFavor: function (_rep: unknown): number { - const rep = helper.number("calculateRepToFavor", "rep", _rep); - checkFormulasAccess("reputation.calculateRepToFavor"); - return calculateRepToFavor(rep); - }, - repFromDonation: function (_amount: unknown, player: any): number { - const amount = helper.number("repFromDonation", "amount", _amount); - checkFormulasAccess("reputation.repFromDonation"); - return repFromDonation(amount, player); - }, + calculateFavorToRep: + (ctx: NetscriptContext) => + (_favor: unknown): number => { + const favor = ctx.helper.number("favor", _favor); + checkFormulasAccess(ctx); + return calculateFavorToRep(favor); + }, + calculateRepToFavor: + (ctx: NetscriptContext) => + (_rep: unknown): number => { + const rep = ctx.helper.number("rep", _rep); + checkFormulasAccess(ctx); + return calculateRepToFavor(rep); + }, + repFromDonation: + (ctx: NetscriptContext) => + (_amount: unknown, player: any): number => { + const amount = ctx.helper.number("amount", _amount); + checkFormulasAccess(ctx); + return repFromDonation(amount, player); + }, }, skills: { - calculateSkill: function (_exp: unknown, _mult: unknown = 1): number { - const exp = helper.number("calculateSkill", "exp", _exp); - const mult = helper.number("calculateSkill", "mult", _mult); - checkFormulasAccess("skills.calculateSkill"); - return calculateSkill(exp, mult); - }, - calculateExp: function (_skill: unknown, _mult: unknown = 1): number { - const skill = helper.number("calculateExp", "skill", _skill); - const mult = helper.number("calculateExp", "mult", _mult); - checkFormulasAccess("skills.calculateExp"); - return calculateExp(skill, mult); - }, + calculateSkill: + (ctx: NetscriptContext) => + (_exp: unknown, _mult: unknown = 1): number => { + const exp = ctx.helper.number("exp", _exp); + const mult = ctx.helper.number("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); + checkFormulasAccess(ctx); + return calculateExp(skill, mult); + }, }, hacking: { - hackChance: function (server: any, player: any): number { - checkFormulasAccess("hacking.hackChance"); - return calculateHackingChance(server, player); - }, - hackExp: function (server: any, player: any): number { - checkFormulasAccess("hacking.hackExp"); - return calculateHackingExpGain(server, player); - }, - hackPercent: function (server: any, player: any): number { - checkFormulasAccess("hacking.hackPercent"); - return calculatePercentMoneyHacked(server, player); - }, - growPercent: function (server: any, _threads: unknown, player: any, _cores: unknown = 1): number { - const threads = helper.number("growPercent", "threads", _threads); - const cores = helper.number("growPercent", "cores", _cores); - checkFormulasAccess("hacking.growPercent"); - return calculateServerGrowth(server, threads, player, cores); - }, - hackTime: function (server: any, player: any): number { - checkFormulasAccess("hacking.hackTime"); - return calculateHackingTime(server, player) * 1000; - }, - growTime: function (server: any, player: any): number { - checkFormulasAccess("hacking.growTime"); - return calculateGrowTime(server, player) * 1000; - }, - weakenTime: function (server: any, player: any): number { - checkFormulasAccess("hacking.weakenTime"); - return calculateWeakenTime(server, player) * 1000; - }, + hackChance: + (ctx: NetscriptContext) => + (server: any, player: any): number => { + checkFormulasAccess(ctx); + return calculateHackingChance(server, player); + }, + hackExp: + (ctx: NetscriptContext) => + (server: any, player: any): number => { + checkFormulasAccess(ctx); + return calculateHackingExpGain(server, player); + }, + hackPercent: + (ctx: NetscriptContext) => + (server: any, player: any): number => { + checkFormulasAccess(ctx); + return calculatePercentMoneyHacked(server, player); + }, + growPercent: + (ctx: NetscriptContext) => + (server: any, _threads: unknown, player: any, _cores: unknown = 1): number => { + const threads = ctx.helper.number("threads", _threads); + const cores = ctx.helper.number("cores", _cores); + checkFormulasAccess(ctx); + return calculateServerGrowth(server, threads, player, cores); + }, + hackTime: + (ctx: NetscriptContext) => + (server: any, player: any): number => { + checkFormulasAccess(ctx); + return calculateHackingTime(server, player) * 1000; + }, + growTime: + (ctx: NetscriptContext) => + (server: any, player: any): number => { + checkFormulasAccess(ctx); + return calculateGrowTime(server, player) * 1000; + }, + weakenTime: + (ctx: NetscriptContext) => + (server: any, player: any): number => { + checkFormulasAccess(ctx); + return calculateWeakenTime(server, player) * 1000; + }, }, hacknetNodes: { - moneyGainRate: function (_level: unknown, _ram: unknown, _cores: unknown, _mult: unknown = 1): number { - const level = helper.number("moneyGainRate", "level", _level); - const ram = helper.number("moneyGainRate", "ram", _ram); - const cores = helper.number("moneyGainRate", "cores", _cores); - const mult = helper.number("moneyGainRate", "mult", _mult); - checkFormulasAccess("hacknetNodes.moneyGainRate"); - return calculateMoneyGainRate(level, ram, cores, mult); - }, - levelUpgradeCost: function (_startingLevel: unknown, _extraLevels: unknown = 1, _costMult: unknown = 1): number { - const startingLevel = helper.number("levelUpgradeCost", "startingLevel", _startingLevel); - const extraLevels = helper.number("levelUpgradeCost", "extraLevels", _extraLevels); - const costMult = helper.number("levelUpgradeCost", "costMult", _costMult); - checkFormulasAccess("hacknetNodes.levelUpgradeCost"); - return calculateLevelUpgradeCost(startingLevel, extraLevels, costMult); - }, - ramUpgradeCost: function (_startingRam: unknown, _extraLevels: unknown = 1, _costMult: unknown = 1): number { - const startingRam = helper.number("ramUpgradeCost", "startingRam", _startingRam); - const extraLevels = helper.number("ramUpgradeCost", "extraLevels", _extraLevels); - const costMult = helper.number("ramUpgradeCost", "costMult", _costMult); - checkFormulasAccess("hacknetNodes.ramUpgradeCost"); - return calculateRamUpgradeCost(startingRam, extraLevels, costMult); - }, - coreUpgradeCost: function (_startingCore: unknown, _extraCores: unknown = 1, _costMult: unknown = 1): number { - const startingCore = helper.number("coreUpgradeCost", "startingCore", _startingCore); - const extraCores = helper.number("coreUpgradeCost", "extraCores", _extraCores); - const costMult = helper.number("coreUpgradeCost", "costMult", _costMult); - checkFormulasAccess("hacknetNodes.coreUpgradeCost"); - return calculateCoreUpgradeCost(startingCore, extraCores, costMult); - }, - hacknetNodeCost: function (_n: unknown, _mult: unknown): number { - const n = helper.number("hacknetNodeCost", "n", _n); - const mult = helper.number("hacknetNodeCost", "mult", _mult); - checkFormulasAccess("hacknetNodes.hacknetNodeCost"); - return calculateNodeCost(n, mult); - }, - constants: function (): any { - checkFormulasAccess("hacknetNodes.constants"); + 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); + 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); + 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); + 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); + 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); + checkFormulasAccess(ctx); + return calculateNodeCost(n, mult); + }, + constants: (ctx: NetscriptContext) => (): any => { + checkFormulasAccess(ctx); return Object.assign({}, HacknetNodeConstants); }, }, hacknetServers: { - hashGainRate: function ( - _level: unknown, - _ramUsed: unknown, - _maxRam: unknown, - _cores: unknown, - _mult: unknown = 1, - ): number { - const level = helper.number("hashGainRate", "level", _level); - const ramUsed = helper.number("hashGainRate", "ramUsed", _ramUsed); - const maxRam = helper.number("hashGainRate", "maxRam", _maxRam); - const cores = helper.number("hashGainRate", "cores", _cores); - const mult = helper.number("hashGainRate", "mult", _mult); - checkFormulasAccess("hacknetServers.hashGainRate"); - return HScalculateHashGainRate(level, ramUsed, maxRam, cores, mult); - }, - levelUpgradeCost: function (_startingLevel: unknown, _extraLevels: unknown = 1, _costMult: unknown = 1): number { - const startingLevel = helper.number("levelUpgradeCost", "startingLevel", _startingLevel); - const extraLevels = helper.number("levelUpgradeCost", "extraLevels", _extraLevels); - const costMult = helper.number("levelUpgradeCost", "costMult", _costMult); - checkFormulasAccess("hacknetServers.levelUpgradeCost"); - return HScalculateLevelUpgradeCost(startingLevel, extraLevels, costMult); - }, - ramUpgradeCost: function (_startingRam: unknown, _extraLevels: unknown = 1, _costMult: unknown = 1): number { - const startingRam = helper.number("ramUpgradeCost", "startingRam", _startingRam); - const extraLevels = helper.number("ramUpgradeCost", "extraLevels", _extraLevels); - const costMult = helper.number("ramUpgradeCost", "costMult", _costMult); - checkFormulasAccess("hacknetServers.ramUpgradeCost"); - return HScalculateRamUpgradeCost(startingRam, extraLevels, costMult); - }, - coreUpgradeCost: function (_startingCore: unknown, _extraCores: unknown = 1, _costMult: unknown = 1): number { - const startingCore = helper.number("coreUpgradeCost", "startingCore", _startingCore); - const extraCores = helper.number("coreUpgradeCost", "extraCores", _extraCores); - const costMult = helper.number("coreUpgradeCost", "costMult", _costMult); - checkFormulasAccess("hacknetServers.coreUpgradeCost"); - return HScalculateCoreUpgradeCost(startingCore, extraCores, costMult); - }, - cacheUpgradeCost: function (_startingCache: unknown, _extraCache: unknown = 1): number { - const startingCache = helper.number("cacheUpgradeCost", "startingCache", _startingCache); - const extraCache = helper.number("cacheUpgradeCost", "extraCache", _extraCache); - checkFormulasAccess("hacknetServers.cacheUpgradeCost"); - return HScalculateCacheUpgradeCost(startingCache, extraCache); - }, - hashUpgradeCost: function (_upgName: unknown, _level: unknown): number { - const upgName = helper.string("hashUpgradeCost", "upgName", _upgName); - const level = helper.number("hashUpgradeCost", "level", _level); - checkFormulasAccess("hacknetServers.hashUpgradeCost"); - const upg = player.hashManager.getUpgrade(upgName); - if (!upg) { - throw helper.makeRuntimeErrorMsg( - "formulas.hacknetServers.calculateHashUpgradeCost", - `Invalid Hash Upgrade: ${upgName}`, - ); - } - return upg.getCost(level); - }, - hacknetServerCost: function (_n: unknown, _mult: unknown = 1): number { - const n = helper.number("hacknetServerCost", "n", _n); - const mult = helper.number("hacknetServerCost", "mult", _mult); - checkFormulasAccess("hacknetServers.hacknetServerCost"); - return HScalculateServerCost(n, mult); - }, - constants: function (): any { - checkFormulasAccess("hacknetServers.constants"); + 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); + 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); + 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); + 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); + 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); + 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); + checkFormulasAccess(ctx); + const upg = player.hashManager.getUpgrade(upgName); + if (!upg) { + throw helper.makeRuntimeErrorMsg( + "formulas.hacknetServers.calculateHashUpgradeCost", + `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); + checkFormulasAccess(ctx); + return HScalculateServerCost(n, mult); + }, + constants: (ctx: NetscriptContext) => (): any => { + checkFormulasAccess(ctx); return Object.assign({}, HacknetServerConstants); }, }, gang: { - wantedPenalty(gang: any): number { - checkFormulasAccess("gang.wantedPenalty"); - return calculateWantedPenalty(gang); - }, - respectGain: function (gang: any, member: any, task: any): number { - checkFormulasAccess("gang.respectGain"); - return calculateRespectGain(gang, member, task); - }, - wantedLevelGain: function (gang: any, member: any, task: any): number { - checkFormulasAccess("gang.wantedLevelGain"); - return calculateWantedLevelGain(gang, member, task); - }, - moneyGain: function (gang: any, member: any, task: any): number { - checkFormulasAccess("gang.moneyGain"); - return calculateMoneyGain(gang, member, task); - }, - ascensionPointsGain: function (_exp: unknown): number { - const exp = helper.number("ascensionPointsGain", "exp", _exp); - checkFormulasAccess("gang.ascensionPointsGain"); - return calculateAscensionPointsGain(exp); - }, - ascensionMultiplier: function (_points: unknown): number { - const points = helper.number("ascensionMultiplier", "points", _points); - checkFormulasAccess("gang.ascensionMultiplier"); - return calculateAscensionMult(points); - }, + wantedPenalty: + (ctx: NetscriptContext) => + (gang: any): number => { + checkFormulasAccess(ctx); + return calculateWantedPenalty(gang); + }, + respectGain: + (ctx: NetscriptContext) => + (gang: any, member: any, task: any): number => { + checkFormulasAccess(ctx); + return calculateRespectGain(gang, member, task); + }, + wantedLevelGain: + (ctx: NetscriptContext) => + (gang: any, member: any, task: any): number => { + checkFormulasAccess(ctx); + return calculateWantedLevelGain(gang, member, task); + }, + moneyGain: + (ctx: NetscriptContext) => + (gang: any, member: any, task: any): number => { + checkFormulasAccess(ctx); + return calculateMoneyGain(gang, member, task); + }, + ascensionPointsGain: + (ctx: NetscriptContext) => + (_exp: unknown): number => { + const exp = ctx.helper.number("exp", _exp); + checkFormulasAccess(ctx); + return calculateAscensionPointsGain(exp); + }, + ascensionMultiplier: + (ctx: NetscriptContext) => + (_points: unknown): number => { + const points = ctx.helper.number("points", _points); + checkFormulasAccess(ctx); + return calculateAscensionMult(points); + }, }, }; } diff --git a/src/NetscriptFunctions/INetscriptHelper.ts b/src/NetscriptFunctions/INetscriptHelper.ts index 2fcbe6588..ac317c474 100644 --- a/src/NetscriptFunctions/INetscriptHelper.ts +++ b/src/NetscriptFunctions/INetscriptHelper.ts @@ -1,4 +1,6 @@ import { CityName } from "src/Locations/data/CityNames"; +import { NetscriptContext } from "src/Netscript/APIWrapper"; +import { IPort } from "src/NetscriptPort"; import { BaseServer } from "../Server/BaseServer"; export interface INetscriptHelper { @@ -8,7 +10,8 @@ export interface INetscriptHelper { number(funcName: string, argName: string, v: unknown): number; city(funcName: string, argName: string, v: unknown): CityName; boolean(v: unknown): boolean; - getServer(ip: any, fn: any): BaseServer; + getServer(ip: any, ctx: NetscriptContext): BaseServer; checkSingularityAccess(func: string): void; - hack(hostname: string, manual: boolean): Promise; + hack(ctx: NetscriptContext, hostname: string, manual: boolean): Promise; + getValidPort(funcName: string, port: number): IPort; }