wip
This commit is contained in:
parent
1a0f8e6b16
commit
35fcc721fa
32
README.md
32
README.md
@ -13,7 +13,6 @@ These scripts are for playing the [open source](https://github.com/danielyxie/bi
|
|||||||
- [network-graph.js](#network-graphjs)
|
- [network-graph.js](#network-graphjs)
|
||||||
- [rootkit.js](#rootkitjs)
|
- [rootkit.js](#rootkitjs)
|
||||||
- [update.js](#updatejs)
|
- [update.js](#updatejs)
|
||||||
- [vanguard.js](#vanguardjs)
|
|
||||||
|
|
||||||
## Quick Start
|
## Quick Start
|
||||||
|
|
||||||
@ -151,10 +150,12 @@ Usage: run network-graph.js [OPTIONS] [DEVICE]
|
|||||||
DEVICE Point to start scan from, defaults to current machine
|
DEVICE Point to start scan from, defaults to current machine
|
||||||
|
|
||||||
Options:
|
Options:
|
||||||
-d --depth Depth to scan to, defaults to 3
|
-d --depth Depth to scan to, defaults is 3
|
||||||
-f --filter Display devices matching name
|
-f --filter Filter to device matching name
|
||||||
-r --regex Display devices matching pattern
|
-e --regex Filter to devices matching pattern
|
||||||
-v --verbose Displays the required hack level & ports needed to root: (level|port)
|
-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
|
-h --help Display this help message
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -204,24 +205,3 @@ Options:
|
|||||||
--no-banner Hide the banner (Used internally)
|
--no-banner Hide the banner (Used internally)
|
||||||
-h --help Display this help message
|
-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
|
|
||||||
```
|
|
||||||
|
@ -1,48 +1,38 @@
|
|||||||
import {ArgError, ArgParser} from './scripts/lib/arg-parser';
|
import {ArgError, ArgParser} from '/scripts/lib/arg-parser';
|
||||||
import {terminal} from './scripts/lib/utils';
|
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
|
* @param ns {NS} - BitBurner API
|
||||||
*/
|
*/
|
||||||
export function main(ns) {
|
export function main(ns) {
|
||||||
// Setup
|
// Setup
|
||||||
ns.disableLog('ALL');
|
ns.disableLog('ALL');
|
||||||
let args;
|
const argParser = new ArgParser('connect.js', 'Search the network for a device and connect to it.', null, [
|
||||||
const argParser = new ArgParser('connect.js', 'Connect to a device on a different network.', null, [
|
|
||||||
{name: 'device', desc: 'Device to connect to', default: ns.getHostname(), type: 'string'}
|
{name: 'device', desc: 'Device to connect to', default: ns.getHostname(), type: 'string'}
|
||||||
]);
|
]);
|
||||||
|
|
||||||
try {
|
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) {
|
} catch(err) {
|
||||||
if(err instanceof ArgError) return ns.tprint(argParser.help(err.message));
|
if(err instanceof ArgError) return ns.tprint(argParser.help(err.message));
|
||||||
throw err;
|
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];
|
|
||||||
}
|
}
|
||||||
|
@ -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.
|
* Search the network for targets to execute a script against.
|
||||||
* @param ns {NS} - BitBurner API
|
* @param ns {NS} - BitBurner API
|
||||||
*/
|
*/
|
||||||
export async function main(ns) {
|
export async function main(ns) {
|
||||||
// Setup
|
// Setup
|
||||||
ns.disableLog('ALL');
|
ns.disableLog('ALL');
|
||||||
const argParser = new ArgParser('crawler.js', 'Search the network for targets to execute a script against.', null, [
|
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: '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: '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: '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: '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: '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: '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);
|
], true);
|
||||||
let args;
|
|
||||||
try {
|
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) {
|
} catch(err) {
|
||||||
if(err instanceof ArgError) return ns.tprint(argParser.help(err.message));
|
if(err instanceof ArgError) return ns.tprint(argParser.help(err.message));
|
||||||
throw err;
|
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('');
|
|
||||||
}
|
}
|
||||||
|
@ -7,97 +7,81 @@ import {ArgError, ArgParser} from './scripts/lib/arg-parser';
|
|||||||
export async function main(ns) {
|
export async function main(ns) {
|
||||||
// Setup
|
// Setup
|
||||||
ns.disableLog('ALL');
|
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, [
|
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: '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 {
|
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) {
|
} catch(err) {
|
||||||
if(err instanceof ArgError) return ns.tprint(argParser.help(err.message));
|
if(err instanceof ArgError) return ns.tprint(argParser.help(err.message));
|
||||||
throw err;
|
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,7 @@ export class Logger {
|
|||||||
this.fns = lineFns;
|
this.fns = lineFns;
|
||||||
this.historyLen -= fns.length * 2;
|
this.historyLen -= fns.length * 2;
|
||||||
this.history = Array(this.historyLen).fill('');
|
this.history = Array(this.historyLen).fill('');
|
||||||
|
this.log();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -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.
|
* Print a download bar to the terminal.
|
||||||
* @params ns {NS} - Bitburner API
|
* @param ns {NS} - BitBurner API
|
||||||
* @params file - Filename to display with progress bar
|
* @param file - Filename to display with progress bar
|
||||||
*/
|
*/
|
||||||
export async function downloadPrint(ns, file) {
|
export async function downloadPrint(ns, file) {
|
||||||
const speed = ~~(Math.random() * 100) / 10;
|
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)`);
|
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.
|
* Print text to the terminal & then delay for a random amount of time to emulate execution time.
|
||||||
* @params ns {NS} - Bitburner API
|
* @param ns {NS} - BitBurner API
|
||||||
* @params message {string} - Text to display
|
* @param message {string} - Text to display
|
||||||
* @params min {number} - minimum amount of time to wait after printing text
|
* @param min {number} - minimum amount of time to wait after printing text
|
||||||
* @params max {number} - maximum 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) {
|
export async function slowPrint(ns, message, min = 0.5, max = 1.5) {
|
||||||
const time = ~~(Math.random() * (max * 1000 - min * 1000)) + min * 1000;
|
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.
|
* 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
|
* @returns {string} - Response
|
||||||
*/
|
*/
|
||||||
export async function terminal(command) {
|
export async function terminal(command) {
|
||||||
// Get Terminal
|
// Get Terminal
|
||||||
const cli = eval('document').querySelector("#terminal-input"); // Terminal
|
const cli = eval('document').querySelector("#terminal-input"); // Terminal
|
||||||
const key = Object.keys(cli)[1];
|
const key = Object.keys(cli)[1];
|
||||||
|
|
||||||
// Send command
|
// Send command
|
||||||
cli[key].onChange({ target: {value: command} });
|
cli[key].onChange({ target: {value: command} });
|
||||||
cli[key].onKeyDown({ keyCode: 13, preventDefault: () => {} });
|
cli[key].onKeyDown({ keyCode: 13, preventDefault: () => {} });
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,7 @@ export async function main(ns) {
|
|||||||
const historyLength = 15;
|
const historyLength = 15;
|
||||||
const messageHistory = Array(historyLength).fill('');
|
const messageHistory = Array(historyLength).fill('');
|
||||||
let maxBalance, balance, minSecurity, security;
|
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'}
|
{name: 'device', desc: 'Device to mine, defaults to current machine', optional: true, default: ns.getHostname(), type: 'string'}
|
||||||
]);
|
]);
|
||||||
let args;
|
let args;
|
||||||
|
@ -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) {
|
export async function main(ns) {
|
||||||
// Setup
|
// Setup
|
||||||
ns.disableLog('ALL');
|
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, [
|
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: '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: 'depth', desc: 'Depth to scan to, defaults is 3', flags: ['-d', '--depth'], default: Infinity, type: 'num'},
|
||||||
{name: 'filter', desc: 'Display devices matching name', flags: ['-f', '--filter'], type: 'string'},
|
{name: 'filter', desc: 'Filter to device matching name', flags: ['-f', '--filter'], type: 'string'},
|
||||||
{name: 'regex', desc: 'Display devices matching pattern', flags: ['-r', '--regex'], type: 'string'},
|
{name: 'regex', desc: 'Filter to devices matching pattern', flags: ['-e', '--regex'], type: 'string'},
|
||||||
{name: 'verbose', desc: 'Displays the required hack level & ports needed to root: (level|port)', flags: ['-v', '--verbose'], type: 'bool'},
|
{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;
|
let args;
|
||||||
try {
|
try {
|
||||||
@ -18,81 +34,31 @@ export async function main(ns) {
|
|||||||
throw err;
|
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
|
* 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
|
* @param spacer {string} - Spacer text for tree formatting
|
||||||
*/
|
*/
|
||||||
function render(tree, spacer = ' ') {
|
function render(tree, stats, spacer = ' ') {
|
||||||
Object.keys(tree).forEach((key, i, arr) => {
|
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 last = i == arr.length - 1;
|
||||||
const branch = last ? '└─ ' : '├─ ';
|
const branch = last ? '└─ ' : '├─ ';
|
||||||
ns.tprint(`${spacer}${branch}${key}`);
|
ns.tprint(spacer + branch + device + stat);
|
||||||
render(tree[key], spacer + (last ? ' ' : '| '));
|
render(tree[device], stats, spacer + (last ? ' ' : '| '));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run
|
// 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']);
|
ns.tprint(args['device']);
|
||||||
const found = scan(args['device']);
|
render(network, args['verbose'] ? stats : null);
|
||||||
if(args['regex']) filter(found, args['regex'], true);
|
|
||||||
else if(args['filter']) filter(found, args['filter']);
|
|
||||||
render(found);
|
|
||||||
ns.tprint('');
|
ns.tprint('');
|
||||||
}
|
}
|
||||||
|
|
||||||
export function autocomplete(data) {
|
|
||||||
return [...data.servers];
|
|
||||||
}
|
|
||||||
|
@ -43,7 +43,7 @@ export class ArgParser {
|
|||||||
const value = arg.type == 'bool' ? true : split[1] || queue.splice(queue.findIndex(q => q[0] != '-'), 1)[0];
|
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}`);
|
if(value == null) throw new ArgError(`Option missing value: ${arg.name}`);
|
||||||
parsed[arg.name] = value;
|
parsed[arg.name] = value;
|
||||||
} else {
|
} else {
|
||||||
// Save for required parsing
|
// Save for required parsing
|
||||||
extra.push(parse);
|
extra.push(parse);
|
||||||
}
|
}
|
||||||
@ -63,7 +63,7 @@ export class ArgParser {
|
|||||||
/**
|
/**
|
||||||
* Create help message from the provided description, examples & argument list.
|
* Create help message from the provided description, examples & argument list.
|
||||||
* @param message {string} - Message to display, defaults to the description
|
* @param message {string} - Message to display, defaults to the description
|
||||||
* @returns {string} - Help message
|
* @returns {string} - Help message
|
||||||
*/
|
*/
|
||||||
help(msg) {
|
help(msg) {
|
||||||
// Description
|
// Description
|
||||||
@ -136,8 +136,7 @@ export async function main(ns) {
|
|||||||
'hacknet-manager.js',
|
'hacknet-manager.js',
|
||||||
'miner.js',
|
'miner.js',
|
||||||
'network-graph.js',
|
'network-graph.js',
|
||||||
'rootkit.js',
|
'rootkit.js'
|
||||||
'vanguard.js'
|
|
||||||
];
|
];
|
||||||
let args;
|
let args;
|
||||||
try {
|
try {
|
||||||
@ -155,7 +154,7 @@ export async function main(ns) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Run
|
// Run
|
||||||
if(!args['skip-self']) { // Update self & restart
|
if(!args['skip-self']) { // Update self & restart
|
||||||
await slowPrint(ns, 'Updating self:');
|
await slowPrint(ns, 'Updating self:');
|
||||||
await ns.wget(`${src}${updateFile}`, `${dest}${updateFile}`, args['device']);
|
await ns.wget(`${src}${updateFile}`, `${dest}${updateFile}`, args['device']);
|
||||||
await downloadPrint(ns, `${dest}${updateFile}`);
|
await downloadPrint(ns, `${dest}${updateFile}`);
|
||||||
|
@ -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];
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user