From 35fcc721fa9edb0f3365a6acfd7abc8a9faa504e Mon Sep 17 00:00:00 2001 From: ztimson Date: Tue, 15 Mar 2022 20:41:23 -0400 Subject: [PATCH] wip --- README.md | 32 ++------ scripts/connect.js | 56 ++++++-------- scripts/crawler.js | 102 ++++++++++++------------ scripts/hacknet-manager.js | 154 +++++++++++++++++-------------------- scripts/lib/logger.js | 1 + scripts/lib/utils.js | 82 +++++++++++++++++--- scripts/miner.js | 2 +- scripts/network-graph.js | 106 +++++++++---------------- scripts/update.js | 9 +-- scripts/vanguard.js | 54 ------------- 10 files changed, 261 insertions(+), 337 deletions(-) delete mode 100644 scripts/vanguard.js diff --git a/README.md b/README.md index 239ceb7..2226c73 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,6 @@ These scripts are for playing the [open source](https://github.com/danielyxie/bi - [network-graph.js](#network-graphjs) - [rootkit.js](#rootkitjs) - [update.js](#updatejs) - - [vanguard.js](#vanguardjs) ## Quick Start @@ -151,10 +150,12 @@ Usage: run network-graph.js [OPTIONS] [DEVICE] DEVICE Point to start scan from, defaults to current machine Options: - -d --depth Depth to scan to, defaults to 3 - -f --filter Display devices matching name - -r --regex Display devices matching pattern - -v --verbose Displays the required hack level & ports needed to root: (level|port) + -d --depth Depth to scan to, defaults is 3 + -f --filter Filter to device matching name + -e --regex Filter to devices matching pattern + -r --rooted Filter to devices that have been rooted + -n --not-rooted Filter to devices that have not been rooted + -v --verbose Display the required hack level & number of ports to root: (level|port) -h --help Display this help message ``` @@ -204,24 +205,3 @@ Options: --no-banner Hide the banner (Used internally) -h --help Display this help message ``` - -### [vanguard.js](./scripts/vanguard.js) -**RAM:** 1.90 GB - -Weaken a device indefinitely. -``` -[home ~/scripts]> run /scripts/vanguard.js --help -Running script with 1 thread(s), pid 1 and args: ["--help"]. -/scripts/vanguard.js: - -Weaken a device indefinitely. - -Usage: run vanguard.js [OPTIONS] [DEVICE] - run vanguard.js --help - - DEVICE Device to weaken, defaults to the current machine - -Options: - -l --limit Limit the number of times to run - -h --help Display this help message -``` diff --git a/scripts/connect.js b/scripts/connect.js index 5fb6e9c..60607ff 100644 --- a/scripts/connect.js +++ b/scripts/connect.js @@ -1,48 +1,38 @@ -import {ArgError, ArgParser} from './scripts/lib/arg-parser'; -import {terminal} from './scripts/lib/utils'; +import {ArgError, ArgParser} from '/scripts/lib/arg-parser'; +import {pruneTree, scanNetwork, terminal} from '/scripts/lib/utils'; /** - * Connect to a device on a different network. + * BitBurner autocomplete + * @param data {server: string[], txts: string[], scripts: string[], flags: string[]} - Contextual information + * @returns {string[]} - Pool of autocomplete options + */ +export function autocomplete(data) { + return [...data.servers]; +} + +/** + * Search the network for a device and connect to it. * @param ns {NS} - BitBurner API */ export function main(ns) { // Setup ns.disableLog('ALL'); - let args; - const argParser = new ArgParser('connect.js', 'Connect to a device on a different network.', null, [ + const argParser = new ArgParser('connect.js', 'Search the network for a device and connect to it.', null, [ {name: 'device', desc: 'Device to connect to', default: ns.getHostname(), type: 'string'} ]); + try { - args = argParser.parse(ns.args); + // Run + const args = argParser.parse(ns.args); + const [devices, network] = scanNetwork(ns); + pruneTree(network, d => d == args['device']); + let current = network, name; + while(name = Object.keys(current)[0]) { + terminal(`connect ${name}`); + current = current[name]; + } } catch(err) { if(err instanceof ArgError) return ns.tprint(argParser.help(err.message)); throw err; } - - /** - * Find path to a device recursively - * @param device {string} - Device to locate - * @param current {string} - Current device to scan - * @param path {string[]} - Path the the current device - * @param all {Set} - Stop devices from being scanned - * @returns {string[]} - Path to the located device - */ - function find(device, current = 'home', path = [current], blacklist = new Set()) { - blacklist.add(current); - const newDevices = ns.scan(current).filter(d => !blacklist.has(d)); - if(newDevices.length == 0) return []; - if(newDevices.find(d => d == device)) return [...path, device]; - return newDevices.map(d => find(device, d, [...path, d], blacklist)).find(p => p && p.length); - } - - // Run - const path = find(args['device']); - path.splice(0, 1); // Delete 'home' from from the path - for(let d of path) { - terminal(`connect ${d}`); - } -} - -export function autocomplete(data) { - return [...data.servers]; } diff --git a/scripts/crawler.js b/scripts/crawler.js index f7cbd51..15b40a8 100644 --- a/scripts/crawler.js +++ b/scripts/crawler.js @@ -1,72 +1,68 @@ -import {ArgError, ArgParser} from './scripts/lib/arg-parser'; +import {ArgError, ArgParser} from '/scripts/lib/arg-parser'; +import {scanNetwork} from '/scripts/lib/utils'; + +/** + * BitBurner autocomplete + * @param data {server: string[], txts: string[], scripts: string[], flags: string[]} - Contextual information + * @returns {string[]} - Pool of autocomplete options + */ +export function autocomplete(data) { + return [...data.scripts, '{{TARGET}}']; +} /** * Search the network for targets to execute a script against. * @param ns {NS} - BitBurner API */ export async function main(ns) { - // Setup + // Setup ns.disableLog('ALL'); const argParser = new ArgParser('crawler.js', 'Search the network for targets to execute a script against.', null, [ {name: 'script', desc: 'Script to copy & execute', type: 'string'}, {name: 'args', desc: 'Arguments for script. Forward the current target with: {{TARGET}}', optional: true, extras: true, type: 'string'}, {name: 'cpu', desc: 'Number of CPU threads to use with script', flags: ['-c', '--cpu'], default: 1, type: 'num'}, {name: 'depth', desc: 'Depth to scan to, defaults to 3', flags: ['-d', '--depth'], default: Infinity, type: 'num'}, - {name: 'level', desc: 'Exclude targets with higher hack level, defaults to current hack level', flags: ['-l', '--level'], default: ns.getHackingLevel(), type: 'num'}, - {name: 'ports', desc: 'Exclute targets with too many closed ports', flags: ['-p', '--ports'], optional: true, default: Infinity, type: 'num'}, + {name: 'level', desc: 'Exclude targets with higher hack level, defaults to current hack level', flags: ['-l', '--level'], default: ns.getHackingLevel(), type: 'num'}, + {name: 'ports', desc: 'Exclute targets with too many closed ports', flags: ['-p', '--ports'], default: Infinity, type: 'num'}, + {name: 'silent', desc: 'Surpress program output', flags: ['-s', '--silent'], type: 'bool'} ], true); - let args; + try { - args = argParser.parse(ns.args); + // Run + const args = argParser.parse(ns.args); + const [devices, network] = scanNetwork(ns); + let complete = 0, failed = 0, skipped = 0; + for(let device of devices) { + // Skip invalid devices + if(device == 'home' || args['level'] < ns.getServerRequiredHackingLevel(device) || args['ports'] < ns.getServerNumPortsRequired(device)) { + skipped++; + continue; + } + + // Start script + const scriptArgs = args['args'].map(arg => arg.toUpperCase() == '{{TARGET}}' ? device : arg); + const pid = ns.run(args['script'], args['cpu'], ...scriptArgs); + if(pid == 0) { + failed++; + continue; + } + + // Wait for script to finish + while(ns.scriptRunning(args['script'], 'home')) + await ns.sleep(1000); + complete++; + } + + // Output report + if(!args['silent']) { + ns.tprint('==================================================='); + ns.tprint(`Crawler Report: ${devices.length} Device${devices.length > 1 ? 's' : ''}`); + ns.tprint('==================================================='); + ns.tprint(`Complete: ${complete}\tFailed: ${failed}\tSkipped: ${skipped}`); + ns.tprint(''); + } } catch(err) { if(err instanceof ArgError) return ns.tprint(argParser.help(err.message)); throw err; } - - /** - * Recursively search network & build a tree - * @param host {string} - Point to scan from - * @param depth {number} - Current scanning depth - * @param blacklist {String[]} - Devices already discovered - * @returns Dicionary of discovered devices - */ - function scan(target = 'home', depth = 1, found = new Set()) { - if(found.size == 0) found.add(target); - ns.scan(target).filter(t => !found.has(t)).forEach(t => { - found.add(t); - scan(t, depth + 1, found); - }); - found.delete('home'); - return Array.from(found); - } - - // Run - const targets = scan(); - let complete = 0, failed = 0, skipped = 0; - for(let target of targets) { - if(target == 'home') continue; - - if(args['level'] < ns.getServerRequiredHackingLevel(target) || args['ports'] < ns.getServerNumPortsRequired(target)) { - skipped++; - continue; - } - - const scriptArgs = args['args'].map(arg => arg == '{{TARGET}}' ? target : arg); - const pid = ns.run(args['script'], args['cpu'], ...scriptArgs); - if(pid == 0) { - failed++; - continue; - } - - // Wait for script to finish - do { await ns.sleep(1000); } - while(ns.scriptRunning(args['script'], 'home')); - complete++; - } - // Output report - ns.tprint('==================================================='); - ns.tprint(`Crawler Report: ${targets.length} Device${targets.length > 1 ? 's' : ''}`); - ns.tprint('==================================================='); - ns.tprint(`Complete: ${complete}\tFailed: ${failed}\tSkipped: ${skipped}`); - ns.tprint(''); } diff --git a/scripts/hacknet-manager.js b/scripts/hacknet-manager.js index 075a3b9..fc53c35 100644 --- a/scripts/hacknet-manager.js +++ b/scripts/hacknet-manager.js @@ -7,97 +7,81 @@ import {ArgError, ArgParser} from './scripts/lib/arg-parser'; export async function main(ns) { // Setup ns.disableLog('ALL'); - const historyLength = 17; - const messageHistory = Array(historyLength).fill(''); - let args, nodeCount = ns.hacknet.numNodes(); const argParser = new ArgParser('hacknet-manager.js', 'Buy, upgrade & manage Hacknet nodes automatically. Tail for live updates.', null, [ {name: 'limit', desc: 'Limit the number of nodes the manager will buy, defaults to 8', optional: true, default: 8, type: 'num'}, - {name: 'balance', desc: 'Prevent spending bellow point', flags: ['-b', '--balance'], type: 'num'} + {name: 'balance', desc: 'Prevent spending bellow point', flags: ['-b', '--balance'], type: 'num'}, + {name: 'sleep', desc: 'Amount of time to wait between purchases, defaults to 1 (second)', flags: ['-s', '--sleep'], default: 1, type: 'num'} ]); - + try { - args = argParser.parse(ns.args); + // Run + const args = argParser.parse(ns.args); + let nodeCount = ns.hacknet.numNodes(); + const logger = new Logger(ns, () => [() => `Hacknet Manager: ${nodeCount}/${args['limit']}`]); + while(true) { + const balance = ns.getServerMoneyAvailable('home'); + + // Check if we should buy a new node + if(nodeCount < args['limit'] && balance - ns.hacknet.getPurchaseNodeCost() >= args['balance']) { + nodeCount++; + ns.hacknet.purchaseNode(); + logger.log(`Buying Node ${nodeCount}`); + } else { + // Create an ordered list of nodes by their cheapest upgrade + const nodes = Array(nodeCount).fill(null) + .map((ignore, i) => ({ // Gather information + index: i, + cacheCost: ns.hacknet.getCacheUpgradeCost(i), + coreCost: ns.hacknet.getCoreUpgradeCost(i), + levelCost: ns.hacknet.getLevelUpgradeCost(i), + ramCost: ns.hacknet.getRamUpgradeCost(i), + ...ns.hacknet.getNodeStats(i) + })).map(node => { // Figure out cheapest upgrade + if(node.cacheCost != 0 && node.cacheCost != Infinity && node.cacheCost <= node.coreCost && node.cacheCost <= node.levelCost && node.cacheCost <= node.ramCost) { + node.bestUpgrade = { + name: 'cache', + cost: node.cacheCost, + purchase: () => ns.hacknet.upgradeCache(node.index) + }; + } else if(node.coreCost != 0 && node.coreCost != Infinity && node.coreCost <= node.cacheCost && node.coreCost <= node.levelCost && node.coreCost <= node.ramCost) { + node.bestUpgrade = { + name: 'cores', + cost: node.coreCost, + purchase: () => ns.hacknet.upgradeCore(node.index) + }; + } else if(node.ramCost != 0 && node.ramCost != Infinity && node.ramCost <= node.cacheCost && node.ramCost <= node.levelCost && node.ramCost <= node.coreCost) { + node.bestUpgrade = { + name: 'ram', + cost: node.ramCost, + purchase: () => ns.hacknet.upgradeRam(node.index) + }; + } else { + node.bestUpgrade = { + name: 'level', + cost: node.levelCost, + purchase: () => ns.hacknet.upgradeLevel(node.index) + }; + } + return node; + }).sort((a, b) => { // Sort by cheapest upgrade + if(a.bestUpgrade.cost > b.bestUpgrade.cost) return 1; + if(a.bestUpgrade.cost < b.bestUpgrade.cost) return -1; + return 0; + }); + + // Apply the cheapest upgrade + if(nodes.length && balance - nodes[0].bestUpgrade.cost >= args['balance']) { + const cost = Math.round(nodes[0].bestUpgrade.cost * 100) / 100; + logger.log(`Node ${nodes[0].index} - ${nodes[0].bestUpgrade.name} ${nodes[0][nodes[0].bestUpgrade.name] + 1} - $${cost}`); + nodes[0].bestUpgrade.purchase(); + } + } + + // Check again in 1s + await ns.sleep(args['sleep'] * 1000); + } } catch(err) { if(err instanceof ArgError) return ns.tprint(argParser.help(err.message)); throw err; } - - /** - * Print header with logs - * @param message - message to append to logs - */ - function log(message) { - ns.clearLog(); - ns.print('==================================================='); - ns.print(`Hacknet Manager: ${nodeCount}/${args['limit']} Nodes`); - ns.print('==================================================='); - if(message != null) messageHistory.push(message); - messageHistory.splice(0, messageHistory.length - historyLength); - messageHistory.forEach(m => ns.print(m)); - } - - // Run - log(); - while(true) { - const balance = ns.getServerMoneyAvailable('home'); - - // Check if we should buy a new node - if(nodeCount < args['limit'] && balance - ns.hacknet.getPurchaseNodeCost() >= args['balance']) { - nodeCount++; - ns.hacknet.purchaseNode(); - log(`Buying Node ${nodeCount}`); - } else { - // Create an ordered list of nodes by their cheapest upgrade - const nodes = Array(nodeCount).fill(null) - .map((ignore, i) => ({ // Gather information - index: i, - cacheCost: ns.hacknet.getCacheUpgradeCost(i), - coreCost: ns.hacknet.getCoreUpgradeCost(i), - levelCost: ns.hacknet.getLevelUpgradeCost(i), - ramCost: ns.hacknet.getRamUpgradeCost(i), - ...ns.hacknet.getNodeStats(i) - })).map(node => { // Figure out cheapest upgrade - if(node.cacheCost != 0 && node.cacheCost != Infinity && node.cacheCost <= node.coreCost && node.cacheCost <= node.levelCost && node.cacheCost <= node.ramCost) { - node.bestUpgrade = { - name: 'cache', - cost: node.cacheCost, - purchase: () => ns.hacknet.upgradeCache(node.index) - }; - } else if(node.coreCost != 0 && node.coreCost != Infinity && node.coreCost <= node.cacheCost && node.coreCost <= node.levelCost && node.coreCost <= node.ramCost) { - node.bestUpgrade = { - name: 'cores', - cost: node.coreCost, - purchase: () => ns.hacknet.upgradeCore(node.index) - }; - } else if(node.ramCost != 0 && node.ramCost != Infinity && node.ramCost <= node.cacheCost && node.ramCost <= node.levelCost && node.ramCost <= node.coreCost) { - node.bestUpgrade = { - name: 'ram', - cost: node.ramCost, - purchase: () => ns.hacknet.upgradeRam(node.index) - }; - } else { - node.bestUpgrade = { - name: 'level', - cost: node.levelCost, - purchase: () => ns.hacknet.upgradeLevel(node.index) - }; - } - return node; - }).sort((a, b) => { // Sort by cheapest upgrade - if(a.bestUpgrade.cost > b.bestUpgrade.cost) return 1; - if(a.bestUpgrade.cost < b.bestUpgrade.cost) return -1; - return 0; - }); - - // Apply the cheapest upgrade - if(nodes.length && balance - nodes[0].bestUpgrade.cost >= args['balance']) { - const cost = Math.round(nodes[0].bestUpgrade.cost * 100) / 100; - log(`Node ${nodes[0].index} - ${nodes[0].bestUpgrade.name} ${nodes[0][nodes[0].bestUpgrade.name] + 1} - $${cost}`); - nodes[0].bestUpgrade.purchase(); - } - } - - // Check again in 1s - await ns.sleep(1000); - } } diff --git a/scripts/lib/logger.js b/scripts/lib/logger.js index 328e063..89407b1 100644 --- a/scripts/lib/logger.js +++ b/scripts/lib/logger.js @@ -12,6 +12,7 @@ export class Logger { this.fns = lineFns; this.historyLen -= fns.length * 2; this.history = Array(this.historyLen).fill(''); + this.log(); } /** diff --git a/scripts/lib/utils.js b/scripts/lib/utils.js index 09ae6c9..f59b597 100644 --- a/scripts/lib/utils.js +++ b/scripts/lib/utils.js @@ -1,7 +1,31 @@ +/** + * Copy a file & scan it for dependencies copying them as well. + * @param ns {NS} - BitBurner API + * @param src {string} - File to scan & copy + * @param device {string} - Device to copy files to + * @returns {string[]} - Array of coppied files + */ +export async function copyWithDependencies(ns, src, device) { + const queue = [src], found = [src]; + while(queue.length) { + const file = queue.splice(0, 1)[0]; + const imports = new RegExp(/from ["']\.?(\/.+)["']/g); + const script = await ns.read(file); + let match; + while((match = imports.exec(script)) != null) { + const path = `${match[1]}.js`; + queue.push(path); + found.push(path); + } + } + await ns.scp(found, device); + return found; +} + /** * Print a download bar to the terminal. - * @params ns {NS} - Bitburner API - * @params file - Filename to display with progress bar + * @param ns {NS} - BitBurner API + * @param file - Filename to display with progress bar */ export async function downloadPrint(ns, file) { const speed = ~~(Math.random() * 100) / 10; @@ -9,12 +33,50 @@ export async function downloadPrint(ns, file) { await slowPrint(ns, `${file}${spacing}[==================>] 100%\t(${speed} MB/s)`); } +export function flatten(object) { + +} + +/** + * **Impure:** Prune tree down to keys which pass function + * @param tree {object} - Tree to search + * @param fn {(key: string) => boolean} - Function to test each key with + * @returns {boolean} - True if a match was found + */ +export function pruneTree(tree, fn) { + return !!Object.keys(tree).map(k => { + let matches = fn(k), children = Object.keys(k), childrenMatch = false; + if(children.length) childrenMatch = pruneTree(tree[k], fn); + if(!childrenMatch && !matches) delete tree[k]; + return childrenMatch || matches; + }).find(el => el); +} + +/** + * Scan the network of a given device. + * @param ns {NS} - BitBurner API + * @param device {string} - Device network that will be scanned + * @param maxDepth - Depth to scan to + * @returns {[string[], Object]} - A tuple including an array of discovered devices & a tree of the network + */ +export function scanNetwork(ns, device = ns.getHostname(), maxDepth = Infinity) { + let discovered = [device]; + function scan (device, depth = 1) { + if(depth > maxDepth) return {}; + const localTargets = ns.scan(device).filter(newDevice => !discovered.includes(newDevice)); + discovered = [...discovered, ...localTargets]; + return localTargets.reduce((acc, device) => ({...acc, [device]: scan(device, depth + 1)}), {}); + } + const network = scan(device); + return [discovered.slice(1), network]; +} + /** * Print text to the terminal & then delay for a random amount of time to emulate execution time. - * @params ns {NS} - Bitburner API - * @params message {string} - Text to display - * @params min {number} - minimum amount of time to wait after printing text - * @params max {number} - maximum amount of time to wait after printing text + * @param ns {NS} - BitBurner API + * @param message {string} - Text to display + * @param min {number} - minimum amount of time to wait after printing text + * @param max {number} - maximum amount of time to wait after printing text */ export async function slowPrint(ns, message, min = 0.5, max = 1.5) { const time = ~~(Math.random() * (max * 1000 - min * 1000)) + min * 1000; @@ -24,15 +86,15 @@ export async function slowPrint(ns, message, min = 0.5, max = 1.5) { /** * Write a command to the terminal. - * @params command {string} - Command that will be run + * @param command {string} - Command that will be run * @returns {string} - Response */ export async function terminal(command) { // Get Terminal - const cli = eval('document').querySelector("#terminal-input"); // Terminal - const key = Object.keys(cli)[1]; + const cli = eval('document').querySelector("#terminal-input"); // Terminal + const key = Object.keys(cli)[1]; // Send command - cli[key].onChange({ target: {value: command} }); + cli[key].onChange({ target: {value: command} }); cli[key].onKeyDown({ keyCode: 13, preventDefault: () => {} }); } diff --git a/scripts/miner.js b/scripts/miner.js index cc4f850..5c38756 100644 --- a/scripts/miner.js +++ b/scripts/miner.js @@ -10,7 +10,7 @@ export async function main(ns) { const historyLength = 15; const messageHistory = Array(historyLength).fill(''); let maxBalance, balance, minSecurity, security; - const argParser = new ArgParser('miner.js', 'Weaken, Grow, Hack loop to "mine" machine for money.', null, [ + const argParser = new ArgParser('miner.js', 'Weaken, Grow, Hack loop to "mine" machine for money. Tail for live updates', null, [ {name: 'device', desc: 'Device to mine, defaults to current machine', optional: true, default: ns.getHostname(), type: 'string'} ]); let args; diff --git a/scripts/network-graph.js b/scripts/network-graph.js index de5b281..6926599 100644 --- a/scripts/network-graph.js +++ b/scripts/network-graph.js @@ -1,14 +1,30 @@ -import {ArgError, ArgParser} from './scripts/lib/arg-parser'; +import {ArgError, ArgParser} from '/scripts/lib/arg-parser'; +import {pruneTree, scanNetwork} from '/scripts/lib/utils'; +/** + * BitBurner autocomplete + * @param data {server: string[], txts: string[], scripts: string[], flags: string[]} - Contextual information + * @returns {string[]} - Pool of autocomplete options + */ +export function autocomplete(data) { + return [...data.servers]; +} + +/** + * Scan the network for devices and display as an ASCII tree. + * @param ns {NS} - BitBurner API + */ export async function main(ns) { // Setup ns.disableLog('ALL'); const argParser = new ArgParser('network-graph.js', 'Scan the network for devices and display as an ASCII tree:\n home\n ├─ n00dles (ROOTED)\n | └─ max-hardware (80|1)\n | └─ neo-net (50|1)\n ├─ foodnstuff (ROOTED)\n └─ sigma-cosmetics (ROOTED)', null, [ {name: 'device', desc: 'Point to start scan from, defaults to current machine', optional: true, default: ns.getHostname(), type: 'string'}, - {name: 'depth', desc: 'Depth to scan to, defaults to 3', flags: ['-d', '--depth'], default: Infinity, type: 'num'}, - {name: 'filter', desc: 'Display devices matching name', flags: ['-f', '--filter'], type: 'string'}, - {name: 'regex', desc: 'Display devices matching pattern', flags: ['-r', '--regex'], type: 'string'}, - {name: 'verbose', desc: 'Displays the required hack level & ports needed to root: (level|port)', flags: ['-v', '--verbose'], type: 'bool'}, + {name: 'depth', desc: 'Depth to scan to, defaults is 3', flags: ['-d', '--depth'], default: Infinity, type: 'num'}, + {name: 'filter', desc: 'Filter to device matching name', flags: ['-f', '--filter'], type: 'string'}, + {name: 'regex', desc: 'Filter to devices matching pattern', flags: ['-e', '--regex'], type: 'string'}, + {name: 'rooted', desc: 'Filter to devices that have been rooted', flags: ['-r', '--rooted'], type: 'bool'}, + {name: 'notRooted', desc: 'Filter to devices that have not been rooted', flags: ['-n', '--not-rooted'], type: 'bool'}, + {name: 'verbose', desc: 'Display the required hack level & number of ports to root: (level|port)', flags: ['-v', '--verbose'], type: 'bool'}, ]); let args; try { @@ -18,81 +34,31 @@ export async function main(ns) { throw err; } - /** - * Prune tree down to devices that match name or pattern. - * @param tree {object} - Tree to search - * @param find {string} - Device name or pattern to search for - * @param regex {boolean} - True to use regex, false for raw check - * @returns {object} - Pruned tree - */ - function filter(tree, find, regex = false) { - const found = new Set(); - function buildWhitelist(tree, find, path = []) { - const keys = Object.keys(tree); - if(!keys.length) return; - Object.keys(tree).forEach(n => { - const matches = regex ? new RegExp(find).test(n) : n == find; - if(n == 'n00dles') console.log(n, find, matches); - if(matches) { - found.add(n); - path.forEach(p => found.add(p)); - } - buildWhitelist(tree[n], find, [...path, n]); - }) - } - function prune(tree, whitelist) { - Object.keys(tree).forEach(n => { - if(Object.keys(tree[n]).length) prune(tree[n], whitelist); - if(!whitelist.includes(n)) delete tree[n]; - }); - } - buildWhitelist(tree, find); - prune(tree, Array.from(found)); - } - - /** - * Recursively search network & build a tree - * @param host {string} - Point to scan from - * @param depth {number} - Current scanning depth - * @param blacklist {String[]} - Devices already discovered - * @returns Dicionary of discovered devices - */ - function scan(host, depth = 1, blacklist = [host]) { - if(depth > args['depth']) return {}; - const localTargets = ns.scan(host).filter(target => !blacklist.includes(target)); - blacklist = [...blacklist, ...localTargets]; - return localTargets.reduce((acc, target) => { - const info = ns.getServer(target); - const verb = args['verbose'] ? ` (${info.hasAdminRights ? 'ROOTED' : `${info.requiredHackingSkill}|${info.numOpenPortsRequired}`})` : ''; - const name = `${target}${verb}`; - acc[name] = scan(target, depth + 1, blacklist); - return acc; - }, {}); - } - /** * Iterate tree & print to screen - * @param tree {object} - Tree to parse + * @param tree {Object} - Tree to parse + * @param stats {Object} - Pool of stats to pull extra information from * @param spacer {string} - Spacer text for tree formatting */ - function render(tree, spacer = ' ') { - Object.keys(tree).forEach((key, i, arr) => { + function render(tree, stats, spacer = ' ') { + Object.keys(tree).forEach((device, i, arr) => { + const deviceStats = stats ? stats[device] : null; + const stat = deviceStats ? ` (${deviceStats.hasAdminRights ? 'ROOTED' : `${deviceStats.requiredHackingSkill}|${deviceStats.numOpenPortsRequired}`})` : ''; const last = i == arr.length - 1; const branch = last ? '└─ ' : '├─ '; - ns.tprint(`${spacer}${branch}${key}`); - render(tree[key], spacer + (last ? ' ' : '| ')); + ns.tprint(spacer + branch + device + stat); + render(tree[device], stats, spacer + (last ? ' ' : '| ')); }); } // Run + const [devices, network] = scanNetwork(ns, args['device'], args['depth']); + const stats = devices.reduce((acc, d) => ({...acc, [d]: ns.getServer(d)}), {}); + if(args['regex']) pruneTree(network, d => RegExp(args['regex']).test(d)); // Regex flag + else if(args['filter']) pruneTree(network, d => d == args['filter']); // Filter flag + if(args['rooted']) pruneTree(network, d => stats[d].hasAdminRights); // Rooted flag + else if(args['notRooted']) pruneTree(network, d => !stats[d].hasAdminRights); // Not rooted flag ns.tprint(args['device']); - const found = scan(args['device']); - if(args['regex']) filter(found, args['regex'], true); - else if(args['filter']) filter(found, args['filter']); - render(found); + render(network, args['verbose'] ? stats : null); ns.tprint(''); } - -export function autocomplete(data) { - return [...data.servers]; -} diff --git a/scripts/update.js b/scripts/update.js index 289f9dc..992928c 100644 --- a/scripts/update.js +++ b/scripts/update.js @@ -43,7 +43,7 @@ export class ArgParser { const value = arg.type == 'bool' ? true : split[1] || queue.splice(queue.findIndex(q => q[0] != '-'), 1)[0]; if(value == null) throw new ArgError(`Option missing value: ${arg.name}`); parsed[arg.name] = value; - } else { + } else { // Save for required parsing extra.push(parse); } @@ -63,7 +63,7 @@ export class ArgParser { /** * Create help message from the provided description, examples & argument list. * @param message {string} - Message to display, defaults to the description - * @returns {string} - Help message + * @returns {string} - Help message */ help(msg) { // Description @@ -136,8 +136,7 @@ export async function main(ns) { 'hacknet-manager.js', 'miner.js', 'network-graph.js', - 'rootkit.js', - 'vanguard.js' + 'rootkit.js' ]; let args; try { @@ -155,7 +154,7 @@ export async function main(ns) { } // Run - if(!args['skip-self']) { // Update self & restart + if(!args['skip-self']) { // Update self & restart await slowPrint(ns, 'Updating self:'); await ns.wget(`${src}${updateFile}`, `${dest}${updateFile}`, args['device']); await downloadPrint(ns, `${dest}${updateFile}`); diff --git a/scripts/vanguard.js b/scripts/vanguard.js deleted file mode 100644 index 9bcbe4f..0000000 --- a/scripts/vanguard.js +++ /dev/null @@ -1,54 +0,0 @@ -import {ArgError, ArgParser} from './scripts/lib/arg-parser'; - -/** - * Weaken a device indefinitely. - * @params ns {NS} - BitBurner API - */ -export async function main(ns) { - // Setup - ns.disableLog('ALL'); - let args, counter = 0, orgSecurity, security; - const historyLength = 17; - const messageHistory = Array(historyLength).fill(''); - const argParser = new ArgParser('vanguard.js', 'Weaken a device indefinitely.', null, [ - {name: 'device', desc: 'Device to weaken, defaults to the current machine', optional: true, default: ns.getHostname(), type: 'string'}, - {name: 'limit', desc: 'Limit the number of times to run', flags: ['-l', '--limit'], default: Infinity, type: 'num'} - ]); - try { - args = argParser.parse(ns.args); - orgSecurity = security = ns.getServerSecurityLevel(args['device']); - } catch(err) { - if(err instanceof ArgError) return ns.tprint(argParser.help(err.message)); - throw err; - } - - /** - * Print header with logs - * @param message - message to append to logs - */ - function log(message) { - ns.clearLog(); - ns.print('==================================================='); - ns.print(`Vanguard: ${args['device']}`); - ns.print('==================================================='); - ns.print(`Security: ${security}/${orgSecurity}`); - ns.print('==================================================='); - if(message != null) messageHistory.push(message); - messageHistory.splice(0, messageHistory.length - historyLength); - messageHistory.forEach(m => ns.print(m)); - } - - // Run - log(); - do { - security = ns.getServerSecurityLevel(args['device']); - log(`Attacking...`); - log(await ns.weaken(args['device'])); - counter++; - } while (counter < args['limit']); - ns.print('Complete!'); -} - -export function autocomplete(data) { - return [...data.servers]; -}