diff --git a/doc/source/netscript/netscriptsingularityfunctions.rst b/doc/source/netscript/netscriptsingularityfunctions.rst index d36d901ae..b9ed4ea56 100644 --- a/doc/source/netscript/netscriptsingularityfunctions.rst +++ b/doc/source/netscript/netscriptsingularityfunctions.rst @@ -16,8 +16,6 @@ You can use the Singularity Functions in other BitNodes if and only if you have Source-File 4 will open up additional Singularity Functions that you can use in other BitNodes. If your Source-File 4 is upgraded all the way to level 3, then you will be able to access all of the Singularity Functions. -Note that Singularity Functions require twice as much RAM outside of BitNode-4 - .. toctree:: :caption: Functions: diff --git a/doc/source/netscript/tixapi/getStockSaleGain.rst b/doc/source/netscript/tixapi/getStockSaleGain.rst index 09c81fdd2..4716f6514 100644 --- a/doc/source/netscript/tixapi/getStockSaleGain.rst +++ b/doc/source/netscript/tixapi/getStockSaleGain.rst @@ -4,7 +4,7 @@ getStockSaleGain() Netscript Function .. js:function:: getStockSaleGain(sym, shares, posType) :param string sym: Stock symbol - :param number shares: Number of shares to purchase + :param number shares: Number of shares to sell :param string posType: Specifies whether the order is a "Long" or "Short" position. The values "L" or "S" can also be used. :RAM cost: 2 GB diff --git a/src/Hacknet/HacknetHelpers.jsx b/src/Hacknet/HacknetHelpers.jsx index f40934c96..6172593e2 100644 --- a/src/Hacknet/HacknetHelpers.jsx +++ b/src/Hacknet/HacknetHelpers.jsx @@ -1,3 +1,13 @@ +/** + * Generic helper/utility functions for the Hacknet mechanic: + * - Purchase nodes/upgrades + * - Calculating maximum number of upgrades + * - Processing Hacknet earnings + * - Updating Hash Manager capacity + * - Purchasing hash upgrades + * + * TODO Should probably split the different types of functions into their own modules + */ import { HacknetNode, BaseCostForHacknetNode, @@ -26,7 +36,7 @@ import { ITutorial } from "../InteractiveTutorial"; import { Player } from "../Player"; -import { AddToAllServers, AllServers } from "../Server/AllServers"; +import { AllServers } from "../Server/AllServers"; import { GetServerByHostname } from "../Server/ServerHelpers"; import { SourceFileFlags } from "../SourceFile/SourceFileFlags"; import { Page, routing } from "../ui/navigationTracking"; @@ -115,7 +125,7 @@ export function getCostOfNextHacknetServer() { return BaseCostForHacknetServer * Math.pow(mult, numOwned) * Player.hacknet_node_purchase_cost_mult; } -//Calculate the maximum number of times the Player can afford to upgrade a Hacknet Node +// Calculate the maximum number of times the Player can afford to upgrade a Hacknet Node's level export function getMaxNumberLevelUpgrades(nodeObj, maxLevel) { if (maxLevel == null) { throw new Error(`getMaxNumberLevelUpgrades() called without maxLevel arg`); @@ -149,6 +159,7 @@ export function getMaxNumberLevelUpgrades(nodeObj, maxLevel) { return 0; } +// Calculate the maximum number of times the Player can afford to upgrade a Hacknet Node's RAM export function getMaxNumberRamUpgrades(nodeObj, maxLevel) { if (maxLevel == null) { throw new Error(`getMaxNumberRamUpgrades() called without maxLevel arg`); @@ -177,6 +188,7 @@ export function getMaxNumberRamUpgrades(nodeObj, maxLevel) { return 0; } +// Calculate the maximum number of times the Player can afford to upgrade a Hacknet Node's cores export function getMaxNumberCoreUpgrades(nodeObj, maxLevel) { if (maxLevel == null) { throw new Error(`getMaxNumberCoreUpgrades() called without maxLevel arg`); @@ -193,7 +205,7 @@ export function getMaxNumberCoreUpgrades(nodeObj, maxLevel) { return levelsToMax; } - //Use a binary search to find the max possible number of upgrades + // Use a binary search to find the max possible number of upgrades while (min <= max) { let curr = (min + max) / 2 | 0; if (curr != maxLevel && @@ -212,6 +224,7 @@ export function getMaxNumberCoreUpgrades(nodeObj, maxLevel) { return 0; } +// Calculate the maximum number of times the Player can afford to upgrade a Hacknet Node's cache export function getMaxNumberCacheUpgrades(nodeObj, maxLevel) { if (maxLevel == null) { throw new Error(`getMaxNumberCacheUpgrades() called without maxLevel arg`); diff --git a/src/Hacknet/HacknetServer.ts b/src/Hacknet/HacknetServer.ts index 236ff06e9..591704d26 100644 --- a/src/Hacknet/HacknetServer.ts +++ b/src/Hacknet/HacknetServer.ts @@ -176,33 +176,28 @@ export class HacknetServer extends BaseServer implements IHacknetNode { return totalCost; } - // Process this Hacknet Server in the game loop. - // Returns the number of hashes generated + // Process this Hacknet Server in the game loop. Returns the number of hashes generated process(numCycles: number=1): number { const seconds = numCycles * CONSTANTS.MilliPerCycle / 1000; return this.hashRate * seconds; } - // Returns a boolean indicating whether the cache was successfully upgraded upgradeCache(levels: number): void { this.cache = Math.min(HacknetServerMaxCache, Math.round(this.cache + levels)); this.updateHashCapacity(); } - // Returns a boolean indicating whether the number of cores was successfully upgraded upgradeCore(levels: number, prodMult: number): void { this.cores = Math.min(HacknetServerMaxCores, Math.round(this.cores + levels)); this.updateHashRate(prodMult); } - // Returns a boolean indicating whether the level was successfully upgraded upgradeLevel(levels: number, prodMult: number): void { this.level = Math.min(HacknetServerMaxLevel, Math.round(this.level + levels)); this.updateHashRate(prodMult); } - // Returns a boolean indicating whether the RAM was successfully upgraded upgradeRam(levels: number, prodMult: number): boolean { for (let i = 0; i < levels; ++i) { this.maxRam *= 2; @@ -212,10 +207,8 @@ export class HacknetServer extends BaseServer implements IHacknetNode { return true; } - - /** - * Whenever a script is run, we must update this server's hash rate - */ + + // Whenever a script is run, we must update this server's hash rate runScript(script: RunningScript, prodMult?: number): void { super.runScript(script); if (prodMult != null && typeof prodMult === "number") { diff --git a/src/Locations/LocationsHelpers.ts b/src/Locations/LocationsHelpers.ts index f50a27c2a..602f1d330 100644 --- a/src/Locations/LocationsHelpers.ts +++ b/src/Locations/LocationsHelpers.ts @@ -40,13 +40,15 @@ import { createPopupCloseButton } from "../../utils/uiHelpers/createPopupCloseBu import { removeElementById } from "../../utils/uiHelpers/removeElementById"; /** - * Create a pop-up box that lets the player confirm traveling to a different city - * If settings are configured to suppress this popup, just instantly travel + * Create a pop-up box that lets the player confirm traveling to a different city. + * If settings are configured to suppress this popup, just instantly travel. * The actual "Travel" implementation is implemented in the UI, and is passed in - * as an argument + * as an argument. + * @param {CityName} destination - City that the player is traveling to + * @param {Function} travelFn - Function that changes the player's state for traveling */ type TravelFunction = (to: CityName) => void; -export function createTravelPopup(destination: CityName, travelFn: TravelFunction) { +export function createTravelPopup(destination: CityName, travelFn: TravelFunction): void { const cost = CONSTANTS.TravelCost; if (Settings.SuppressTravelConfirmation) { @@ -80,10 +82,10 @@ export function createTravelPopup(destination: CityName, travelFn: TravelFunctio /** * Create a pop-up box that lets the player purchase a server. - * @param ram - Amount of RAM (GB) on server - * @param p - Player object + * @param {number} ram - Amount of RAM (GB) on server + * @param {IPlayer} p - Player object */ -export function createPurchaseServerPopup(ram: number, p: IPlayer) { +export function createPurchaseServerPopup(ram: number, p: IPlayer): void { const cost = getPurchaseServerCost(ram); if (cost === Infinity) { dialogBoxCreate("Something went wrong when trying to purchase this server. Please contact developer"); @@ -111,6 +113,7 @@ export function createPurchaseServerPopup(ram: number, p: IPlayer) { /** * Create a popup that lets the player start a Corporation + * @param {IPlayer} p - Player object */ export function createStartCorporationPopup(p: IPlayer) { if (!p.canAccessCorporation() || p.hasCorporation()) { return; } @@ -172,8 +175,10 @@ export function createStartCorporationPopup(p: IPlayer) { if (worldHeader instanceof HTMLElement) { worldHeader.click(); worldHeader.click(); } - dialogBoxCreate("Congratulations! You just started your own corporation with government seed money. " + - "You can visit and manage your company in the City"); + dialogBoxCreate( + "Congratulations! You just started your own corporation with government seed money. " + + "You can visit and manage your company in the City" + ); removeElementById(popupId); return false; } @@ -187,21 +192,23 @@ export function createStartCorporationPopup(p: IPlayer) { /** * Create a popup that lets the player upgrade the cores on his/her home computer - * @param p - Player object + * @param {IPlayer} p - Player object */ export function createUpgradeHomeCoresPopup(p: IPlayer) { const currentCores = p.getHomeComputer().cpuCores; if (currentCores >= 8) { return; } // Max of 8 cores - //Cost of purchasing another cost is found by indexing this array with number of current cores - const allCosts = [0, - 10e9, // 1->2 Cores - 10 bn - 250e9, // 2->3 Cores - 250 bn - 5e12, // 3->4 Cores - 5 trillion - 100e12, // 4->5 Cores - 100 trillion - 1e15, // 5->6 Cores - 1 quadrillion - 20e15, // 6->7 Cores - 20 quadrillion - 200e15]; // 7->8 Cores - 200 quadrillion + // Cost of purchasing another cost is found by indexing this array with number of current cores + const allCosts = [ + 0, + 10e9, + 250e9, + 5e12, + 100e12, + 1e15, + 20e15, + 200e15 + ]; const cost: number = allCosts[currentCores]; const yesBtn = yesNoBoxGetYesButton(); @@ -215,8 +222,10 @@ export function createUpgradeHomeCoresPopup(p: IPlayer) { } else { p.loseMoney(cost); p.getHomeComputer().cpuCores++; - dialogBoxCreate("You purchased an additional CPU Core for your home computer! It now has " + - p.getHomeComputer().cpuCores + " cores."); + dialogBoxCreate( + "You purchased an additional CPU Core for your home computer! It now has " + + p.getHomeComputer().cpuCores + " cores." + ); } yesNoBoxClose(); }); @@ -226,15 +235,17 @@ export function createUpgradeHomeCoresPopup(p: IPlayer) { yesNoBoxClose(); }); - yesNoBoxCreate("Would you like to purchase an additional CPU Core for your home computer? Each CPU Core " + - "lets you start with an additional Core Node in Hacking Missions.

" + - "Purchasing an additional core (for a total of " + (p.getHomeComputer().cpuCores + 1) + ") will " + - "cost " + numeralWrapper.formatMoney(cost)); + yesNoBoxCreate( + "Would you like to purchase an additional CPU Core for your home computer? Each CPU Core " + + "lets you start with an additional Core Node in Hacking Missions.

" + + "Purchasing an additional core (for a total of " + (p.getHomeComputer().cpuCores + 1) + ") will " + + "cost " + numeralWrapper.formatMoney(cost) + ); } /** * Create a popup that lets the player upgrade the RAM on his/her home computer - * @param p - Player object + * @param {IPlayer} p - Player object */ export function createUpgradeHomeRamPopup(p: IPlayer) { const cost: number = p.getUpgradeHomeRamCost(); @@ -255,15 +266,17 @@ export function createUpgradeHomeRamPopup(p: IPlayer) { yesNoBoxClose(); }); - yesNoBoxCreate("Would you like to purchase additional RAM for your home computer?

" + - "This will upgrade your RAM from " + ram + "GB to " + ram*2 + "GB.

" + - "This will cost " + numeralWrapper.format(cost, '$0.000a')); + yesNoBoxCreate( + "Would you like to purchase additional RAM for your home computer?

" + + "This will upgrade your RAM from " + ram + "GB to " + ram*2 + "GB.

" + + "This will cost " + numeralWrapper.format(cost, '$0.000a') + ); } /** * Attempt to purchase a TOR router - * @param p - Player object + * @param {IPlayer} p - Player object */ export function purchaseTorRouter(p: IPlayer) { if (p.hasTorRouter()) { @@ -285,7 +298,9 @@ export function purchaseTorRouter(p: IPlayer) { p.getHomeComputer().serversOnNetwork.push(darkweb.ip); darkweb.serversOnNetwork.push(p.getHomeComputer().ip); - dialogBoxCreate("You have purchased a Tor router!
" + - "You now have access to the dark web from your home computer
" + - "Use the scan/scan-analyze commands to search for the dark web connection."); + dialogBoxCreate( + "You have purchased a Tor router!
" + + "You now have access to the dark web from your home computer
" + + "Use the scan/scan-analyze commands to search for the dark web connection." + ); } diff --git a/src/Script/RamCalculations.js b/src/Script/RamCalculations.js index 3013b8c2f..1d2a77823 100644 --- a/src/Script/RamCalculations.js +++ b/src/Script/RamCalculations.js @@ -1,4 +1,10 @@ -// Calculate a script's RAM usage +/** + * Implements RAM Calculation functionality. + * + * Uses the acorn.js library to parse a script's code into an AST and + * recursively walk through that AST, calculating RAM usage along + * the way + */ import * as walk from "acorn-walk"; import { RamCalculationErrorCode } from "./RamCalculationErrorCodes"; @@ -15,19 +21,26 @@ const specialReferenceWHILE = "__SPECIAL_referenceWhile"; // The global scope of a script is registered under this key during parsing. const memCheckGlobalKey = ".__GLOBAL__"; -// Calcluates the amount of RAM a script uses. Uses parsing and AST walking only, -// rather than NetscriptEvaluator. This is useful because NetscriptJS code does -// not work under NetscriptEvaluator. +/** + * Parses code into an AST and walks through it recursively to calculate + * RAM usage. Also accounts for imported modules. + * @param {Script[]} otherScripts - All other scripts on the server. Used to account for imported scripts + * @param {string} codeCopy - The code being parsed + * @param {WorkerScript} workerScript - Object containing RAM costs of Netscript functions. Also used to + * keep track of what functions have/havent been accounted for + */ async function parseOnlyRamCalculate(otherScripts, code, workerScript) { try { - // Maps dependent identifiers to their dependencies. - // - // The initial identifier is __SPECIAL_INITIAL_MODULE__.__GLOBAL__. - // It depends on all the functions declared in the module, all the global scopes - // of its imports, and any identifiers referenced in this global scope. Each - // function depends on all the identifiers referenced internally. - // We walk the dependency graph to calculate RAM usage, given that some identifiers - // reference Netscript functions which have a RAM cost. + /** + * Maps dependent identifiers to their dependencies. + * + * The initial identifier is __SPECIAL_INITIAL_MODULE__.__GLOBAL__. + * It depends on all the functions declared in the module, all the global scopes + * of its imports, and any identifiers referenced in this global scope. Each + * function depends on all the identifiers referenced internally. + * We walk the dependency graph to calculate RAM usage, given that some identifiers + * reference Netscript functions which have a RAM cost. + */ let dependencyMap = {}; // Scripts we've parsed. @@ -48,19 +61,20 @@ async function parseOnlyRamCalculate(otherScripts, code, workerScript) { } } - // Splice all the references in. - //Spread syntax not supported in edge, use Object.assign instead - //dependencyMap = {...dependencyMap, ...result.dependencyMap}; + // Splice all the references in dependencyMap = Object.assign(dependencyMap, result.dependencyMap); } + // Parse the initial module, which is the "main" script that is being run const initialModule = "__SPECIAL_INITIAL_MODULE__"; parseCode(code, initialModule); + // Process additional modules, which occurs if the "main" script has any imports while (parseQueue.length > 0) { - // Get the code from the server. const nextModule = parseQueue.shift(); + // Additional modules can either be imported from the web (in which case we use + // a dynamic import), or from other in-game scripts let code; if (nextModule.startsWith("https://") || nextModule.startsWith("http://")) { try { @@ -91,7 +105,7 @@ async function parseOnlyRamCalculate(otherScripts, code, workerScript) { } if (script == null) { - return RamCalculationErrorCode.ImportError; // No such script on the server + return RamCalculationErrorCode.ImportError; // No such script on the server } code = script.code; @@ -136,10 +150,8 @@ async function parseOnlyRamCalculate(otherScripts, code, workerScript) { } } - // Check if this identifier is a function in the workerscript env. + // Check if this identifier is a function in the workerScript environment. // If it is, then we need to get its RAM cost. - // - // TODO it would be simpler to just reference a dictionary. try { function applyFuncRam(func) { if (typeof func === "function") { @@ -170,7 +182,7 @@ async function parseOnlyRamCalculate(otherScripts, code, workerScript) { workerScript.loadedFns[ref] = true; } - // This accounts for namespaces (Bladeburner, CodingCOntract) + // This accounts for namespaces (Bladeburner, CodingCpntract, etc.) let func; if (ref in workerScript.env.vars.bladeburner) { func = workerScript.env.vars.bladeburner[ref]; @@ -196,9 +208,12 @@ async function parseOnlyRamCalculate(otherScripts, code, workerScript) { } } -// Parses one script and calculates its ram usage, for the global scope and each function. -// Returns a cost map and a dependencyMap for the module. Returns a reference map to be joined -// onto the main reference map, and a list of modules that need to be parsed. +/** + * Helper function that parses a single script. It returns a map of all dependencies, + * which are items in the code's AST that potentially need to be evaluated + * for RAM usage calculations. It also returns an array of additional modules + * that need to be parsed (i.e. are 'import'ed scripts). + */ function parseOnlyCalculateDeps(code, currentModule) { const ast = parse(code, {sourceType:"module", ecmaVersion: 8}); @@ -296,6 +311,12 @@ function parseOnlyCalculateDeps(code, currentModule) { return {dependencyMap: dependencyMap, additionalModules: additionalModules}; } +/** + * Calculate's a scripts RAM Usage + * @param {string} codeCopy - The script's code + * @param {Script[]} otherScripts - All other scripts on the server. + * Used to account for imported scripts + */ export async function calculateRamUsage(codeCopy, otherScripts) { // We don't need a real WorkerScript for this. Just an object that keeps // track of whatever's needed for RAM calculations diff --git a/src/Script/RunningScript.ts b/src/Script/RunningScript.ts index eca819276..59bde86b2 100644 --- a/src/Script/RunningScript.ts +++ b/src/Script/RunningScript.ts @@ -69,22 +69,20 @@ export class RunningScript { if (script == null) { return; } this.filename = script.filename; this.args = args; - - this.server = script.server; //IP Address only + this.server = script.server; this.ramUsage = script.ramUsage; } log(txt: string): void { if (this.logs.length > Settings.MaxLogCapacity) { - //Delete first element and add new log entry to the end. - //TODO Eventually it might be better to replace this with circular array - //to improve performance this.logs.shift(); } + let logEntry = txt; if (FconfSettings.ENABLE_TIMESTAMPS) { logEntry = "[" + getTimestamp() + "] " + logEntry; } + this.logs.push(logEntry); this.logUpd = true; } diff --git a/src/StockMarket/BuyingAndSelling.ts b/src/StockMarket/BuyingAndSelling.ts index 21a32313b..e628595af 100644 --- a/src/StockMarket/BuyingAndSelling.ts +++ b/src/StockMarket/BuyingAndSelling.ts @@ -57,7 +57,7 @@ export function buyStock(stock: Stock, shares: number, workerScript: WorkerScrip if (totalPrice == null) { return false; } if (Player.money.lt(totalPrice)) { if (tixApi) { - workerScript!.log(`ERROR: buyStock() failed because you do not have enough money to purchase this potiion. You need ${numeralWrapper.formatMoney(totalPrice)}`); + workerScript!.log(`ERROR: buyStock() failed because you do not have enough money to purchase this position. You need ${numeralWrapper.formatMoney(totalPrice)}`); } else if (opts.suppressDialog !== true) { dialogBoxCreate(`You do not have enough money to purchase this. You need ${numeralWrapper.formatMoney(totalPrice)}`); }