├── scripts ├── dashboard.js ├── hack.js ├── remoteHack.js ├── serverStatus.js ├── autoRemoteHack.js ├── hax.js ├── purchaseServers.js ├── autoHack.js └── buyHacknet.js ├── README.md └── import.js /scripts/dashboard.js: -------------------------------------------------------------------------------- 1 | import { getFolder, getServerPrefix } from 'import.js'; 2 | import { serverHackStatus, serverReport } from '/scripts/serverStatus.js'; 3 | 4 | /* 5 | * This is a dashboard that outputs the status 6 | * of your entire server network. 7 | */ 8 | export async function main(ns) { 9 | findServer(ns, 'home', 'home', 1); 10 | } 11 | 12 | function findServer(ns, startServer, targetServer, i) { 13 | let servers = ns.scan(targetServer, true) 14 | .filter((server) => server !== startServer && !server.includes(getServerPrefix())); 15 | servers.forEach((server) => { 16 | ns.tprint(`😹${'>'.repeat(i)}`); 17 | serverReport(ns, server); 18 | if (serverHackStatus(ns, server) !== "🔐") { 19 | findServer(ns, targetServer, server, i + 1); 20 | } 21 | }); 22 | } 23 | -------------------------------------------------------------------------------- /scripts/hack.js: -------------------------------------------------------------------------------- 1 | /* A simple lightweight script that is deployed 2 | * to remote and local servers to repeatedly hack 3 | * a particular server. 4 | * The smaller this is, the more threads can be deployed. 5 | * args[0] - server name 6 | * args[1] - threads to attack with 7 | */ 8 | export async function main(ns, args) { 9 | await hackServer(ns, ns.args[0], ns.args[1]); 10 | } 11 | 12 | async function hackServer(ns, server, threads) { 13 | ns.disableLog('getServerSecurityLevel'); 14 | let serverSecurityThreshold = ns.getServerMinSecurityLevel(server) + 2; 15 | let serverMoneyThreshold = ns.getServerMaxMoney(server) * 0.95; 16 | let opts = { threads: threads, stock: true }; 17 | while (true) { 18 | if (ns.getServerSecurityLevel(server) > serverSecurityThreshold) { 19 | await ns.weaken(server, opts); 20 | } else if (ns.getServerMoneyAvailable(server) < serverMoneyThreshold) { 21 | await ns.grow(server, opts); 22 | } else { 23 | await ns.hack(server, opts); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /scripts/remoteHack.js: -------------------------------------------------------------------------------- 1 | import { getHackScript } from 'import.js'; 2 | 3 | /* Deploys the hack script to all purchased servers 4 | * Differs from autoRemoteHack because you can specify 5 | * the target 6 | * args[0] - list of servers 7 | * args[1] - alternate Hackscript (default from import) 8 | */ 9 | export async function main(ns) { 10 | let myServers = ns.getPurchasedServers(); 11 | let targetServers = ns.args[0].split(','); 12 | let hackScript = ns.args[1] || getHackScript(); 13 | let scriptRam = ns.getScriptRam(hackScript); 14 | for (const [index, server] of myServers.entries()) { 15 | ns.killall(server); 16 | let serverRam = ns.getServerRam(server)[0]; 17 | let threads = Math.floor(serverRam / scriptRam); 18 | let serverIndex = index % targetServers.length; 19 | let targetServer = targetServers[serverIndex]; 20 | ns.print(`${server} is hacking ${targetServer} with ${threads} threads.`); 21 | await ns.scp(hackScript, server); 22 | if (threads > 0) { 23 | ns.exec(hackScript, server, threads, targetServer, threads); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # bitburner-scripts 2 | 3 | I'm playing [BitBurner]() on Thursdays at https://twitch.tv/ChaelCodes ! This is my Repo of BitBurner Scripts. 4 | 5 | You can import these scripts by running the following in Bitburner. 6 | 7 | # Get the importer 8 | `wget https://raw.githubusercontent.com/ChaelCodes/bitburner-scripts/main/import.js import.js` 9 | # Configure your Import 10 | You can configure your import inside `import.js`. 11 | ``` 12 | config = { 13 | folder: 'scripts', 14 | rootUrl: 'https://raw.githubusercontent.com/ChaelCodes/bitburner-scripts/main/', 15 | serverPrefix: 'ChaelPwns', 16 | }; 17 | ``` 18 | 19 | - `folder` will determine where your scripts are stored 20 | - `rootUrl` is the source, if you fork this repo, update this to you repo 21 | - `serverPrefix` your automatically purchased servers will be filtered from auto hacks using this prefix. Pick a fun name! 22 | 23 | # Run your import! 24 | `run import.js` will tell you if everything worked. Please reach out and create an issue for troubleshooting. Please include your import.js, and make sure your forked repo is public! 25 | 26 | # Explore! Enjoy! 27 | Follow instructions, try help with various commands. 28 | Try running `run /scripts/hax.js autoHack` there's numerous commands available there. Explore! 29 | 30 | If you see this error: 31 | ![image](https://user-images.githubusercontent.com/8124558/101851194-1b246500-3b29-11eb-9986-7b626bdea51d.png) 32 | Open the file using `nano` Save & Close it, and try again. There's a bug. 33 | -------------------------------------------------------------------------------- /scripts/serverStatus.js: -------------------------------------------------------------------------------- 1 | let hackPorts = 0; 2 | 3 | /* 4 | * Utility functions that report serverStatus 5 | * and Hackability 6 | */ 7 | export async function main(ns) { 8 | let server = ns.args[0]; 9 | serverReport(ns, server); 10 | } 11 | 12 | export function serverReport(ns, server) { 13 | let serverLock = serverHackStatus(ns, server); 14 | ns.tprint(`${serverLock} ${server}`); 15 | if (serverLock == "🔓") { 16 | ns.tprint(`🛡️${Math.round(ns.getServerSecurityLevel(server))}/${ns.getServerMinSecurityLevel(server)}`); 17 | ns.tprint(`💸${ns.nFormat(ns.getServerMoneyAvailable(server), "$0.000a")}/${ns.nFormat(ns.getServerMaxMoney(server), "$0.000a")}`); 18 | } else { 19 | ns.tprint(`Hack Level: ${ns.getServerRequiredHackingLevel(server)}`); 20 | ns.tprint(`Ports: ${ns.getServerNumPortsRequired(server)}`); 21 | } 22 | ns.tprint('-----------'); 23 | } 24 | 25 | export function serverHackStatus(ns, server) { 26 | if (ns.hasRootAccess(server)) { 27 | return "🔓"; 28 | } 29 | if (ns.getServerRequiredHackingLevel(server) > ns.getHackingLevel() || 30 | ns.getServerNumPortsRequired(server) > hackablePorts) { 31 | return "🔐"; 32 | } 33 | return "🔒"; 34 | } 35 | 36 | export function hackablePorts(ns) { 37 | if (hackPorts > 0) { 38 | return hackPorts; 39 | } 40 | if (ns.fileExists('BruteSSH.exe')) { 41 | hackPorts += 1; 42 | } 43 | if (ns.fileExists('FTPCrack.exe')) { 44 | hackPorts += 1; 45 | } 46 | if (ns.fileExists('relaySMTP.exe')) { 47 | hackPorts += 1; 48 | } 49 | if (ns.fileExists('HTTPWorm.exe')) { 50 | hackPorts += 1; 51 | } 52 | if (ns.fileExists('SQLInject.exe')) { 53 | hackPorts += 1; 54 | } 55 | return hackPorts; 56 | } 57 | -------------------------------------------------------------------------------- /import.js: -------------------------------------------------------------------------------- 1 | let config = { 2 | folder: 'scripts', 3 | rootUrl: 'https://raw.githubusercontent.com/ChaelCodes/bitburner-scripts/main/', 4 | serverPrefix: 'ChaelPwns', 5 | }; 6 | /* 7 | * This will import all files listed in importFiles. 8 | */ 9 | export async function main(ns) { 10 | let filesImported = await importFiles(ns); 11 | ns.tprint('='.repeat(20)); 12 | if (filesImported) { 13 | ns.tprint('Hey! Thank you for downloading the BitBurner Scripts.'); 14 | ns.tprint(`You've installed these in the ${config.folder} directory.`); 15 | ns.tprint( 16 | `A good place to start is running \`run /${config.folder}/hax.js\`` 17 | ); 18 | } else { 19 | ns.tprint( 20 | 'You had some issues downloading files, please reach out to the repo maintainer or check your config.' 21 | ); 22 | } 23 | } 24 | 25 | async function importFiles(ns) { 26 | let files = [ 27 | 'autoHack.js', 28 | 'autoRemoteHack.js', 29 | 'buyHacknet.js', 30 | 'dashboard.js', 31 | 'hack.js', 32 | 'hax.js', 33 | 'purchaseServers.js', 34 | 'remoteHack.js', 35 | 'serverStatus.js', 36 | ]; 37 | let filesImported = true; 38 | for (let file of files) { 39 | let remoteFileName = `${config.rootUrl}scripts/${file}`; 40 | let result = await ns.wget(remoteFileName, `/${getFolder()}/${file}`); 41 | filesImported = filesImported && result; 42 | ns.tprint(`File: ${file}: ${result ? '✔️' : '❌'}`); 43 | } 44 | return filesImported; 45 | } 46 | 47 | export function getFolder() { 48 | return config.folder; 49 | } 50 | 51 | export function getServerPrefix() { 52 | return config.serverPrefix; 53 | } 54 | 55 | export function getHackScript() { 56 | return `/${getFolder()}/hack.js`; 57 | } 58 | -------------------------------------------------------------------------------- /scripts/autoRemoteHack.js: -------------------------------------------------------------------------------- 1 | import { getFolder, getServerPrefix } from 'import.js'; 2 | let maxValueServers; 3 | let serverValue = 0; 4 | 5 | /* Identify servers worth more than $10 Billion, 6 | * deploy the hack script and attack those servers 7 | * using all purchased servers. 8 | */ 9 | export async function main(ns) { 10 | maxValueServers = { 11 | zero: [], 12 | million: [], 13 | billion: [], 14 | trillion: [] 15 | }; 16 | findServer(ns, 'home', 'home', checkValue); 17 | ns.run(`/${getFolder()}/remoteHack.js`, 1, highest(ns).join(',')); 18 | } 19 | 20 | function findServer(ns, startServer, targetServer, func) { 21 | let servers = ns.scan(targetServer, true).filter((server) => server !== startServer && !server.includes(getServerPrefix)); 22 | if (!ns.hasRootAccess(targetServer)) { return false; } 23 | servers.forEach((server) => { 24 | func.call(this, ns, server); 25 | if (ns.hasRootAccess(server)) { 26 | findServer(ns, targetServer, server, func); 27 | } 28 | }); 29 | } 30 | 31 | function checkValue(ns, server) { 32 | if (!ns.hasRootAccess(server)) { 33 | return; 34 | } 35 | let serverMoney = ns.getServerMaxMoney(server); 36 | if (serverMoney > 1e12) { 37 | maxValueServers.trillion.push(server); 38 | } else if (serverMoney > 1e9) { 39 | maxValueServers.billion.push(server); 40 | } else if (serverMoney > 0) { 41 | maxValueServers.million.push(server); 42 | } else { 43 | maxValueServers.zero.push(server); 44 | } 45 | } 46 | 47 | function highest(ns) { 48 | if (maxValueServers.trillion.length > 0) { 49 | return maxValueServers.trillion; 50 | } 51 | if (maxValueServers.billion.length > 0) { 52 | return maxValueServers.billion; 53 | } 54 | if (maxValueServers.million.length > 0) { 55 | return maxValueServers.million; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /scripts/hax.js: -------------------------------------------------------------------------------- 1 | import { getFolder } from 'import.js'; 2 | /* Run various scripts easily and from one interface 3 | */ 4 | export async function main(ns) { 5 | await runCommand(ns, ns.args[0]); 6 | } 7 | 8 | async function runCommand(ns, command) { 9 | switch (command) { 10 | case 'autoHack': 11 | case 'autoRemoteHack': 12 | case 'dashboard': 13 | case 'purchaseServers': 14 | ns.run(`/${getFolder()}/${command}.js`); 15 | break; 16 | case 'sudo autohack': 17 | case 'sudo autoHack': 18 | ns.run(`/${getFolder()}/autoHack.js`); 19 | ns.run(`/${getFolder()}/autoRemoteHack.js`); 20 | break; 21 | case 'autohack': 22 | ns.run(`/${getFolder()}/autoHack.js`); 23 | break; 24 | case 'serverhack': 25 | case 'serverHack': 26 | ns.run(`/${getFolder()}/autoRemoteHack.js`); 27 | break; 28 | case 'status': 29 | ns.run(`/${getFolder()}/dashboard.js`); 30 | break; 31 | case 'buy': 32 | case 'purchase': 33 | await indecisiveBuyer(ns); 34 | break; 35 | case 'buyHacknet': 36 | case 'purchaseHacknet': 37 | ns.run(`/${getFolder()}/buyHacknet.js`, 1, 'buyNode'); 38 | break; 39 | case 'upgradeHacknet': 40 | ns.run(`/${getFolder()}/buyHacknet.js`, 1, 'buyNode'); 41 | ns.run(`/${getFolder()}/buyHacknet.js`, 1, 'upgradeNodes'); 42 | break; 43 | case 'buyServer': 44 | case 'purchaseServer': 45 | case 'buyServers': 46 | ns.run(`/${getFolder()}/purchaseServers.js`); 47 | break; 48 | default: 49 | ns.tprint(`Oh no! ${command} isn't a valid command. Try: dashboard, autoHack, autoRemoteHack, or buy.`); 50 | } 51 | } 52 | 53 | async function indecisiveBuyer(ns) { 54 | let buyServer = await ns.prompt("Did you want to buy servers?"); 55 | if (buyServer) { ns.run(`/${getFolder()}/purchaseServers.js`); } 56 | let buyHacknet = await ns.prompt("Did you want to buy hacknet nodes?"); 57 | if (buyHacknet) { 58 | ns.run(`/${getFolder()}/buyHacknet.js`, 1, 'buyNode'); 59 | ns.tprint(`You've bought a node. See \`run /${getFolder()}/buyHacknet.js\` for more options.`); 60 | } 61 | } 62 | 63 | export function autocomplete(data, args){ 64 | return ['autoHack', 'autoRemoteHack','dashboard','buy', 'purchaseServer', 'buyHacknet', 'upgradeHacknet']; 65 | } 66 | -------------------------------------------------------------------------------- /scripts/purchaseServers.js: -------------------------------------------------------------------------------- 1 | import { getServerPrefix } from 'import.js'; 2 | 3 | let maxServers; 4 | let servers; 5 | 6 | /* 7 | * Purchases the best server available with the 8 | * user's current money. If the server limit is 9 | * reached, replaces the worst server. Repeatable. 10 | */ 11 | export async function main(ns) { 12 | // Default Values 13 | maxServers = ns.getPurchasedServerLimit(); 14 | servers = ns.getPurchasedServers(true); 15 | serverInfo(ns); 16 | await buyServers(ns); 17 | } 18 | 19 | function serverInfo(ns) { 20 | ns.tprint(`You have ${servers.length}/${maxServers} servers`); 21 | Object.entries(groupServers(ns)).map((ramServers) => { 22 | ns.tprint(`${ramServers[0]}GB: ${ramServers[1]}`); 23 | }); 24 | } 25 | 26 | function groupServers(ns) { 27 | let groupedServers = {}; 28 | servers.forEach((server) => { 29 | let ram = ns.getServerRam(server)[0]; 30 | groupedServers[ram] = groupedServers[ram] || []; 31 | groupedServers[ram].push(server); 32 | }); 33 | return groupedServers; 34 | } 35 | 36 | async function buyServers(ns) { 37 | let ram = ns.getPurchasedServerMaxRam(); 38 | let shopServer = true; 39 | while (shopServer) { 40 | let myMoney = ns.getServerMoneyAvailable('home'); 41 | let serverCost = ns.getPurchasedServerCost(ram); 42 | while (serverCost > myMoney && ram > 2) { 43 | ram = ram / 2; 44 | serverCost = ns.getPurchasedServerCost(ram); 45 | } 46 | shopServer = await ns.prompt(`Would you like to buy a ${ram}GB server for ${ns.nFormat(serverCost, "$0.00a")}`); 47 | if (shopServer) { shopServer = buyServer(ns, ram); } 48 | } 49 | } 50 | 51 | function buyServer(ns, ram) { 52 | if (servers.length == maxServers) { 53 | let success = removeWeakestServer(ns, ram); 54 | if (!success) { return false; } 55 | } 56 | let server = ns.purchaseServer(`${getServerPrefix()}-${ram}GB`, ram); 57 | servers.push(server); 58 | ns.tprint(`Purchased ${server}: ${ram}GB`); 59 | return true; 60 | } 61 | 62 | function removeWeakestServer(ns, newRam) { 63 | let groupedServers = groupServers(ns); 64 | let min = Math.min(...Object.keys(groupedServers)); 65 | if (min >= newRam) { 66 | ns.tprint(`Your smallest server has ${min}GB RAM and you wanted to purchase ${newRam}GB server`); 67 | return false; 68 | } 69 | let smallest_server = groupedServers[min][0]; 70 | ns.killall(smallest_server); 71 | let result = ns.deleteServer(smallest_server); 72 | servers = ns.getPurchasedServers(true); 73 | return true; 74 | } 75 | -------------------------------------------------------------------------------- /scripts/autoHack.js: -------------------------------------------------------------------------------- 1 | import { getHackScript, getServerPrefix } from 'import.js'; 2 | let hackablePorts; 3 | 4 | /* Searches for servers that are hackable, 5 | * cracks them if you don't have root access, 6 | * installs a hack script, and instructs them to 7 | * HACK THEMSELVES 8 | */ 9 | export const main = async function (ns) { 10 | findHackablePorts(ns); 11 | await findServer(ns, 'home', 'home', hackServer); 12 | } 13 | 14 | async function findServer(ns, startServer, targetServer, func) { 15 | let servers = ns.scan(targetServer, true).filter((server) => server !== startServer && !server.includes(getServerPrefix())); 16 | for (const server of servers) { 17 | const success = await func.call(this, ns, server); 18 | if (success) { 19 | await findServer(ns, targetServer, server, func); 20 | } 21 | } 22 | } 23 | 24 | async function hackServer(ns, server) { 25 | if (!crackServer(ns, server)) { 26 | return false; 27 | } 28 | ns.killall(server); 29 | let scriptRam = ns.getScriptRam(getHackScript()); 30 | let serverRam = ns.getServerMaxRam(server); 31 | let threads = Math.floor(serverRam / scriptRam); 32 | await ns.scp(getHackScript(), server); 33 | if (threads > 0) { 34 | ns.print(`Starting ${threads} processes on ${server}`); 35 | ns.exec(getHackScript(), server, threads, server, threads); 36 | } 37 | return true; 38 | } 39 | 40 | function crackServer(ns, server) { 41 | if (ns.hasRootAccess(server)) { 42 | return true; 43 | } 44 | 45 | if (ns.fileExists('BruteSSH.exe')) { 46 | ns.brutessh(server); 47 | } 48 | if (ns.fileExists('FTPCrack.exe')) { 49 | ns.ftpcrack(server); 50 | } 51 | if (ns.fileExists('relaySMTP.exe')) { 52 | ns.relaysmtp(server); 53 | } 54 | if (ns.fileExists('HTTPWorm.exe')) { 55 | ns.httpworm(server); 56 | } 57 | if (ns.fileExists('SQLInject.exe')) { 58 | ns.sqlinject(server); 59 | } 60 | if (ns.getServerRequiredHackingLevel(server) > ns.getHackingLevel() || 61 | ns.getServerNumPortsRequired(server) > hackablePorts) { 62 | return false; 63 | } else { 64 | ns.nuke(server); 65 | ns.tprint(`New Server Cracked: ${server}!`); 66 | return true; 67 | } 68 | } 69 | 70 | export function findHackablePorts(ns) { 71 | let hackPorts = 0; 72 | if (ns.fileExists('BruteSSH.exe')) { 73 | hackPorts += 1; 74 | } 75 | if (ns.fileExists('FTPCrack.exe')) { 76 | hackPorts += 1; 77 | } 78 | if (ns.fileExists('relaySMTP.exe')) { 79 | hackPorts += 1; 80 | } 81 | if (ns.fileExists('HTTPWorm.exe')) { 82 | hackPorts += 1; 83 | } 84 | if (ns.fileExists('SQLInject.exe')) { 85 | hackPorts += 1; 86 | } 87 | hackablePorts = hackPorts; 88 | } 89 | -------------------------------------------------------------------------------- /scripts/buyHacknet.js: -------------------------------------------------------------------------------- 1 | let functions = { buyNode, upgradeNodes, buyNodes, help }; 2 | let maxNodeCost; 3 | /* 4 | * Time to get some Hacknet Nodes! 5 | * You can call this script with the options 6 | * buyNode - purchase and upgrade one new node 7 | * upgradeNodes - upgrade your existing network to max 8 | * buyNodes - buy as many nodes as you can afford 9 | * help - help with the command 10 | */ 11 | export async function main(ns) { 12 | let result; 13 | if (!Object.keys(functions).includes(ns.args[0])) { 14 | result = 'Not Found'; 15 | help(ns); 16 | } else { 17 | result = functions[ns.args[0]](ns); 18 | } 19 | response(ns, ns.args[0], result); 20 | } 21 | 22 | function help(ns) { 23 | ns.tprint("This script helps you manage your Hacknet Nodes. " + 24 | "You can purchase new nodes by running `run buyHacknet.js buyNode` " + 25 | "or `run buyHacknet.js buyNodes`. It will purchase as many nodes as you can afford. " + 26 | "You can automatically upgrade your existing hacknet nodes by running " + 27 | "`run buyHacknet.js upgradeNodes`"); 28 | return ''; 29 | } 30 | 31 | function response(ns, func, result) { 32 | switch (func) { 33 | case 'buyNode': 34 | ns.tprint(result ? '✔️Purchased one node' : 'You cannot afford a node.😢'); 35 | break; 36 | case 'buyNodes': 37 | ns.tprint(result > 0 ? `✔️Purchased ${result} nodes.` : 'You cannot afford a node.😢'); 38 | break; 39 | case 'upgradeNodes': 40 | ns.tprint(result ? '✔️Nodes Upgraded' : '⭐You are already upgraded as much as you can afford.'); 41 | break; 42 | default: 43 | if (result === 'Not Found') { ns.tprint(`${func} is not a valid option`); } 44 | } 45 | } 46 | 47 | function buyNode(ns) { 48 | let index = ns.hacknet.purchaseNode(); 49 | if (index < 0) { 50 | return false; 51 | } 52 | maxNodeCost = maxNodeCost || getMaxNodeCost(ns, index); 53 | 54 | if (ns.getServerMoneyAvailable('home') >= maxNodeCost) { 55 | ns.hacknet.upgradeLevel(index, 200); 56 | ns.hacknet.upgradeRam(index, 6); 57 | ns.hacknet.upgradeCore(index, 16); 58 | } else { 59 | upgradeNode(ns, index); 60 | } 61 | return true; 62 | } 63 | 64 | function buyNodes(ns) { 65 | let purchaseNodes = true; 66 | let i = 0; 67 | while (purchaseNodes) { 68 | purchaseNodes = buyNode(ns); 69 | if (purchaseNodes) { i++; } 70 | } 71 | return i; 72 | } 73 | 74 | function getMaxNodeCost(ns, index) { 75 | ns.tprint('Fetching cost'); 76 | return ns.hacknet.getLevelUpgradeCost(index, 200) + 77 | ns.hacknet.getRamUpgradeCost(index, 6) + 78 | ns.hacknet.getCoreUpgradeCost(index, 16); 79 | } 80 | 81 | function upgradeNodes(ns) { 82 | let upgrade = false; 83 | let nodes = ns.hacknet.numNodes(); 84 | for (let i = 0; i < nodes; i++) { 85 | upgrade = upgradeNode(ns, i) || upgrade; 86 | } 87 | return upgrade; 88 | } 89 | 90 | function upgradeNode(ns, index) { 91 | let level = upgradeNodePart(ns, index, 'Level', 10); 92 | let node = upgradeNodePart(ns, index, 'Ram', 2); 93 | let core = upgradeNodePart(ns, index, 'Core', 1); 94 | return level || node || core; 95 | } 96 | 97 | function upgradeNodePart(ns, nodeIndex, upgradePart, increment) { 98 | let upgrade = false; 99 | let costFunction = `get${upgradePart}UpgradeCost`; 100 | let upgradeFunction = `upgrade${upgradePart}`; 101 | let cost = ns.hacknet[costFunction](nodeIndex, increment); 102 | while (isFinite(cost) && ns.getServerMoneyAvailable('home') >= cost) { 103 | ns.hacknet[upgradeFunction](nodeIndex, increment); 104 | cost = ns.hacknet[costFunction](nodeIndex, increment); 105 | upgrade = true; 106 | } 107 | return upgrade; 108 | } 109 | --------------------------------------------------------------------------------