Skip to content

Commit

Permalink
Fix #419: Transient RAM errors due to backdoor-all-servers.ks
Browse files Browse the repository at this point in the history
- Stop backdooring additional servers if home RAM drops below the configured threshold
- Added script args
- Made spawn delay and reserved home ram configurable
- Minimized base ram footprint by dodging remaining functions
- Servers are now backdoored in order of lowest to highest hack level requirement (fastest to slowest)
  • Loading branch information
alainbryden committed Nov 3, 2024
1 parent 1f2c046 commit 8209a6d
Showing 1 changed file with 53 additions and 16 deletions.
69 changes: 53 additions & 16 deletions Tasks/backdoor-all-servers.js
Original file line number Diff line number Diff line change
@@ -1,57 +1,94 @@
import { getNsDataThroughFile, getFilePath, instanceCount, log, getErrorInfo } from '../helpers.js'
import { getNsDataThroughFile, getFilePath, getConfiguration, instanceCount, log, getErrorInfo } from '../helpers.js'

const spawnDelay = 50; // Delay to allow time for `installBackdoor` to start running before a background script connects back to 'home'
const argsSchema = [
['spawn-delay', 50], // Delay to allow time for `installBackdoor` to start running before a we connect back to 'home' and optionally start backdooring the next server
['reserved-home-ram', 22], // Don't spawn additional backdoor scripts if home free ram dips below this amount (each parallel backdoor consumes 3.6 GB)
];

export function autocomplete(data, args) {
data.flags(argsSchema);
return [];
}

/** Scan all servers, backdoor anything that can be backdoored, and leave a file to indicate it's been done
* Requires: SF-4.1 **/
/** @param {NS} ns **/
export async function main(ns) {
let notAtHome = false;
try {
const options = getConfiguration(ns, argsSchema);

// Prevent multiple instances of this script from being started
if (await instanceCount(ns, "home", false, false) > 1)
return log(ns, 'Another instance is already running. Shutting down...');

let servers = ["home"];
let routes = { home: ["home"] };
let myHackingLevel = ns.getHackingLevel();
const spawnDelay = options['spawn-delay'];

const servers = ["home"];
const routes = { home: ["home"] };
const myHackingLevel = await getNsDataThroughFile(ns, 'ns.getHackingLevel()');
// Scan all servers and keep track of the path to get to them
ns.disableLog("scan");
for (let i = 0, j; i < servers.length; i++)
for (j of ns.scan(servers[i]))
for (j of (await getNsDataThroughFile(ns, `ns.scan(ns.args[0])`, null, [servers[i]])))
if (!servers.includes(j)) servers.push(j), routes[j] = routes[servers[i]].slice(), routes[j].push(j);

// Get the required hacking level of each server
const dictRequiredHackingLevels = await getNsDataThroughFile(ns,
`Object.fromEntries(ns.args.map(server => [server, ns.getServerRequiredHackingLevel(server)]))`,
'/Temp/getServerRequiredHackingLevel-all.txt', servers);
// Get the root status for each server
const dictRootAccess = await getNsDataThroughFile(ns,
`Object.fromEntries(ns.args.map(server => [server, ns.hasRootAccess(server)]))`,
'/Temp/hasRootAccess-all.txt', servers);

// Filter out servers that cannot or should not be hacked / backdoored
ns.disableLog("getServerRequiredHackingLevel");
let hackableServers = servers.filter(s => s != "home" && !s.includes("hacknet-") && !s.includes("daemon")) /*or whatever you name your purchased servers*/
ns.print(`${hackableServers.length} not-owned servers on the network.`);
ns.print(`${hackableServers.filter(s => ns.hasRootAccess(s)).length} servers are currently rooted.`);
ns.print(`${hackableServers.filter(s => myHackingLevel > ns.getServerRequiredHackingLevel(s)).length} servers are within our hack level (${myHackingLevel}).`);
ns.print(`${hackableServers.filter(s => myHackingLevel > ns.getServerRequiredHackingLevel(s) && ns.hasRootAccess(s)).length} rooted servers are within our hack level (${myHackingLevel})`);
ns.print(`${hackableServers.filter(s => dictRootAccess[s]).length} servers are currently rooted.`);
ns.print(`${hackableServers.filter(s => myHackingLevel > dictRequiredHackingLevels[s]).length} servers are within our hack level (${myHackingLevel}).`);
ns.print(`${hackableServers.filter(s => myHackingLevel > dictRequiredHackingLevels[s] && dictRootAccess[s]).length} rooted servers are within our hack level (${myHackingLevel})`);

let toBackdoor = await getNsDataThroughFile(ns, `ns.args.filter(s => !ns.getServer(s).backdoorInstalled)`, '/Temp/servers-to-backdoor.txt', hackableServers);
// Get the set of servers that do not yet have a backdoor installed
let toBackdoor = await getNsDataThroughFile(ns,
`ns.args.filter(server => !ns.getServer(server).backdoorInstalled)`,
'/Temp/getServers-where-not-backdoorInstalled.txt', hackableServers);
let count = toBackdoor.length;
// Early exit condition if there are no servers left to backdoor
ns.print(`${count} servers have yet to be backdoored.`);
if (count == 0) return;

// Early exit condition if there are no servers we can currently backdoor
ns.print(`${toBackdoor.filter(s => ns.hasRootAccess(s)).length} of ${count} servers to backdoor are currently rooted.`);
toBackdoor = toBackdoor.filter(s => myHackingLevel > ns.getServerRequiredHackingLevel(s));
ns.print(`${toBackdoor.filter(s => dictRootAccess[s]).length} of ${count} servers to backdoor are currently rooted.`);
toBackdoor = toBackdoor.filter(s => myHackingLevel > dictRequiredHackingLevels[s]);
ns.print(`${toBackdoor.length} of ${count} servers to backdoor are within our hack level (${myHackingLevel}).`);
toBackdoor = toBackdoor.filter(s => ns.hasRootAccess(s));
toBackdoor = toBackdoor.filter(s => dictRootAccess[s]);
ns.print(`${toBackdoor.length} of ${count} servers to be backdoored are rooted and within our hack level (${myHackingLevel})`);
if (toBackdoor.length == 0) return;

// Sort servers by lowest required hacking level (fastest to backdoor)
toBackdoor.sort((a, b) => dictRequiredHackingLevels[a] - dictRequiredHackingLevels[b])
ns.print(`Servers will be backdoored in the following order:\n` + toBackdoor.join(', '));

// Collect information about any servers still being backdoored (from a prior run), so we can skip them
let scriptPath = getFilePath('/Tasks/backdoor-all-servers.js.backdoor-one.js');
let currentScripts = ns.ps().filter(s => s.filename == scriptPath).map(s => s.args[0]);
let serversBeingBackdoored = await getNsDataThroughFile(ns,
'ns.ps().filter(script => script.filename == ns.args[0]).map(script => script.args[0])',
'/Temp/servers-being-backdoored.txt', [scriptPath]);

for (const server of toBackdoor) {
if (currentScripts.find(x => x == server)) {
if (serversBeingBackdoored.includes(server)) {
log(ns, `INFO: Server already beeing backdoored: ${server}`);
continue;
}

// If we're running low on home ram, don't spawn any more backdoor scripts
const homeFreeRam = await getNsDataThroughFile(ns,
'ns.getServerMaxRam(ns.args[0]) - ns.getServerUsedRam(ns.args[0])',
'/Temp/getServerFreeRam.txt', ["home"]);
if (homeFreeRam < options['reserved-home-ram'])
return log(ns, `WARNING: Home is low on RAM, will skip backdooring remaining servers.`);

ns.print(`Hopping to ${server}`);
notAtHome = true; // Set a flag to get us back home if we encounter an error
const success = await getNsDataThroughFile(ns,
Expand Down

0 comments on commit 8209a6d

Please sign in to comment.