Explanation on how to HWGW Manager running, centralized prep and farm of one or many servers – Bitburner

This guide is mostly written for two groups you may belong to:

– You already did your scripting, and are just looking for some inspiration to optimize your own setup

– You did not succeed on starting your own centralized HWGW solution, so you will use this manager to get a good start.

If you do have any questions or improvements, be sure to leave a comment, i will get back to you.

I do use this script myself so this will work for you too.

Introduction – guide content

In this guide you will get all the necessary code and explanation to get the HWGW Manager running. This includes the needed one-shot scripts and simple data/code to do the needed basics for the script to work, if you do not yet have an own solution for that.

You will also find some explanation in this guide and in-line comments in the code itself.

This code is written so you can understand and modify or expand the manager to your needs. If you are not able to do that with the given information, please let me know.

Disclaimer:

This is my first manager script, also my first guide, and also i am not playing BitBurner for months, but rather for weeks.

I am also in no way a professional programmer, which you also do not need to be for this game.

I only used ‘special’ things like the ? : operation very rarely to make it easy to follow what the manager is actually doing.

Feel free to share your thoughts on how to improve code quality.

I am also not a native english speaker and did not double check the content of this guide with a translator, so there may be weird phrases. Just give me a hint so i can correct them.

What is…

Depending on your playtime and experience with BitBurner you may already have a few questions, so lets read the basics again:

What is HWGW?

HWGW Batching means you will queue

– a hack,

– a weaken to counter the hack,

– a grow to counter the hack,

– and another weaken to counter the grow

This will, if correctly done, result in no change of available Money and Security Level on the target server. This in return will make our life way easier, because we can do the same stuff over and over again effectively.

To read more about HWGW, read this if you have not done yet:

https://bitburner.readthedocs.io/en/latest/advancedgameplay/hackingalgorithms.html#batch-algorithms-hgw-hwgw-or-cycles – [readthedocs.io]

What do you mean, centralized?

The manager script acts much like a command-and-control (C2) server and will be run in a single thread on your home. It will act as the brain that orchestrates what the other servers of yours should do. Therefore the managing part is only on one and not every server, resulting in less RAM used and also better management (no need to communicate with the other 25 servers).

All other servers will only use one-shot scripts.

One-shot what?

These are one-time use only scripts, that will wait a specified time, start their hack/grow/weaken and finish at the same time with all the other scripts in the batch. No while(true) loops included.

This is very effective, as there are always only these threads running (once), that are currently needed. No need to kill loops or waste actions that are not needed anymore.

prep and farm, what is that?

In order to farm your target efficiently, you want to grow the money to the max, and reduce the security level to the minimum.

Getting the server in this state is called prepping (at least i name it that way).

Farming will be done on a fuly prepped server, where we only hack so much we will balance out by the grow and weaken done simultanously.

The manager does both, prepping servers that need it, and farming servers that are ready, automatically.

Prerequisite 1: One-Shot-Scripts

These are the scripts that do the actual work.

They need to be located on your home server, in the ‘shared’ folder.

To create them, just type/paste the command above the snippets in your terminal, and then the code in the editor, and save.

nano /shared/hack.js

/** @param {NS} ns **/export async function main(ns) {
 //sleep for given amount
 const sleep = ns.args[1] || 1;
 await ns.sleep(sleep);
 //and then hack!
 await ns.hack(ns.args[0]);
}

nano /shared/grow.js

/** @param {NS} ns **/export async function main(ns) {
 //sleep for given amount
 const sleep = ns.args[1] || 1;
 await ns.sleep(sleep);
 //and then grow!
 await ns.grow(ns.args[0]);
}

nano /shared/weaken.js

/** @param {NS} ns **/export async function main(ns) {
 //sleep for given amount
 const sleep = ns.args[1] || 1;
 await ns.sleep(sleep);
 //and then weaken!
 await ns.weaken(ns.args[0]);
}

 

Prerequisite 2: Gathering a target server list and/or the best target

For now I will a*sume you have a servercrawler orsome other solution that will scan the network for good targets.You can of course just manually scout targets with scan-an*lyze, although this should be automated by you eventually.

If automated, your script should either store them in a txt file with a filehandler, or you can just hardcode an array with all servers you know in our script (you do not need to filter based on root access/hack level).

Example:

const targets = ["n00dles","foodnstuff","sigma-cosmetics","joesguns","hong-fang-tea","harakiri-sushi","iron-gym","darkweb","home","zer0"]; // and all other servers you found

The same goes for a single best target. If you are early in your game, you may want to hack only one server instead of all of them. Chosing the best server is not yet done by the manager.

For this, you ideally have a scoring/targeter script that takes care of that.

If not, just hardcode your best target into the manager.

Example:

const primeTarget = "foodnstuff"; // or any else good server

Note for the primeTarget:

If you give the manager a target you cannot hack (because you cant get root access or do not have the required Hacklevel), nothing will happen.

The manager will just ignore the target, because it can’t be hacked. You won’t get a warning or a crash, so make sure to doublecheck if you hardcode a server here.

I did include these hardcoded examples in the manager script, so you just need to swap which lines should be a comment and which not.

original:

change it to:

If you prefer the hacknetmanager chosing the best target, let me know, and i will add this with the next change.

Prerequisite 3: Opening target servers and script distribution

The manager uses a list of servers (or a single server) that is already nuked.

Also the one-shot files also need to be on the servers you want to farm.

I have a seperate servercrawler script, which saves the servers in a txt file, takes care of all that.

I recommend you to do the same, but this is for now out of scope.

If you do not have this kind of thing, no problem, take this and insert it into the manager script:

await ns.scp([
 "/shared/weaken.js",
 "/shared/grow.js",
 "/shared/hack.js"
], server);

if (!ns.hasRootAccess(server)) {
 let openPorts = 0;
 if (ns.fileExists("BruteSSH.exe")) {
 ns.brutessh(server);
 openPorts++;
 }
 if (ns.fileExists("FTPCrack.exe")) {
 ns.ftpcrack(server);
 openPorts++;
 }
 if (ns.fileExists("RelaySMTP.exe")) {
 ns.relaysmtp(server);
 openPorts++;
 }
 if (ns.fileExists("HTTPWorm.exe")) {
 ns.httpworm(server);
 openPorts++;
 }
 if (ns.fileExists("SQLInject.exe")) {
 ns.sqlinject(server);
 openPorts++;
 }
 if (ns.getServerNumPortsRequired(server) <= openPorts) {
 ns.nuke(server);
 }
}

this is one possible solution showcased in Suikoudans Scripting 101 guide

This snippet needs to go below

for (let target of targets) {
 // ...we stick with THE BEST one. Adjust the criteria to your liking.
 if (earlyGameCheck)
 target = primeTarget;

and above

 //Is our given server even useable?
 if (!ns.serverExists(target) || !ns.hasRootAccess(target) ||
 ns.getServerMaxMoney(target) < 1)
 continue;

 

The HWGW Manager – Part 1

Alright, here it is, the manager itself.

I tried to comment every part more or less helpfully, if something misses, just tell me.

I do not explain the calculation in my code, so this will be in another chapter.

If you do not like the steam guide format for the code, here is a pastebin: https://pastebin.com/sg8mTfYD – [pastebin.com]

Because of steam guide limitations i need to split the code in 2 parts.

Be sure to double check if you got it together the correct way.

And now on to the code (you can use whatever name and folder you like)

nano kuposhwgwmanager.js

/** @param {NS} ns **/// Used for importing targets from other scripts
import { FileHandler } from "/data/file-handler.js";

export async function main(ns) {
 //Logs are nice to know whats going on
 ns.disableLog('sleep');
 ns.disableLog('getServerMaxRam');
 ns.disableLog('getServerUsedRam');
 ns.disableLog('getServerSecurityLevel');
 ns.disableLog('getServerMinSecurityLevel');
 ns.disableLog('getServerMaxMoney');
 ns.disableLog('getServerMoneyAvailable');
 ns.disableLog('getHackingLevel');
 // If you do not want an open tail window with information what this script is doing on every start, you can remove/comment the tail line below.
 ns.tail();

 // Used for importing targets from other scripts
 const fileHandlerServers = new FileHandler(ns, "/database/servers.txt");
 const fileHandlerTarget = new FileHandler(ns, "/database/target.txt");

 const ramUsePerThread = ns.getScriptRam("/shared/weaken.js");
 const weakenPerThread = ns.weakenAnalyze(1, 1);
 const reservedHomeRam = 50; //GB - change if you want to reserve more of you home ram
 const sleepPerIteration = 10; // ms - reduce if you need to go even faster, or raise if you get lag spikes
 const iterationPause = 10 // seconds - Pause after the script did touch every server and every target - how often should it run again

 // This will be used to make sure that every batch goes through and is not blocked my "script already running with same arguments"
 let randomArgument = 1;

 let earlyGameCheck = false;

 while (true) {

 // read (new) targets - if you do not use fileHandlers like i do, just throw in an array of targets or a function or anything really. 
 // If desperate or no clue, use the commented lines instead and change target to your highest/best target you currently have
 const targets = await fileHandlerServers.read();
 //const targets = ["n00dles","foodnstuff","sigma-cosmetics","joesguns","hong-fang-tea","harakiri-sushi","iron-gym","darkweb","home","zer0","CSEC","nectar-net","max-hardware","neo-net","silver-helix","phantasy","omega-net","computek","netlink","johnson-ortho","the-hub","crush-fitness","avmnite-02h","catalyst","I.I.I.I","summit-uni","syscore","rothman-uni","zb-institute","lexo-corp","rho-construction","millenium-fitness","alpha-ent","aevum-police","aerocorp","snap-fitness","galactic-cyber","global-pharm","omnia","deltaone","unitalife","solaris","defcomm","icarus","univ-energy","zeus-med","taiyang-digital","zb-def","infocomm","nova-med","titan-labs","applied-energetics","microdyne","run4theh111z","stormtech","helios","vitalife","fulcrumtech","4sigma","kuai-gong",".","omnitek","b-and-a","powerhouse-fitness","nwo","clarkinc","blade","ecorp","megacorp","fulcruma*sets","The-Cave"];

 // The best server we know for our current state. Or just a random one you can tackle. 
 const primeTarget = await fileHandlerTarget.read();
 //const primeTarget = "foodnstuff";

 // get (new) servers + home
 let servers = ns.getPurchasedServers();
 servers = servers.concat("home");

 // If we are so early in the game that we can't even effectively farm ALL servers...
 const goodHackingLevel = 750;
 if (ns.getHackingLevel() < goodHackingLevel && ns.serverExists(primeTarget) && ns.hasRootAccess(primeTarget) && ns.getServerMaxMoney(primeTarget) > 1)
 earlyGameCheck = true;

 for (let target of targets) {
 // ...we stick with THE BEST one. Adjust the criteria to your liking.
 if (earlyGameCheck)
 target = primeTarget;

 //Is our given server even useable?
 if (!ns.serverExists(target) || !ns.hasRootAccess(target) || ns.getServerMaxMoney(target) < 1)
 continue;

 //Target variables
 const maxMoney = ns.getServerMaxMoney(target);
 const minSecurityLevel = ns.getServerMinSecurityLevel(target);
 let currentSecLevel = ns.getServerSecurityLevel(target);
 let availableMoney = Math.max(1, ns.getServerMoneyAvailable(target));
 let growthCalculated = false;
 let requiredGrowThreads = 0;
 let batchDelay = 0;

 for (let myServer of servers) {
 if (!ns.serverExists(myServer) || !ns.hasRootAccess(myServer))
 continue;
 //Calculate possible threads - If "home", we want some spare some spare RAM
 let threadsToUse = Math.floor((ns.getServerMaxRam(myServer) - ns.getServerUsedRam(myServer) - (myServer == "home" ? reservedHomeRam : 0)) / ramUsePerThread);

 //Come on get out of here we have no use for you if you have no capacity
 if(threadsToUse < 1)
 continue;

 //Break the server down to its minimum sec level.
 if (currentSecLevel > minSecurityLevel) {
 const reducedSecLevel = weakenPerThread * threadsToUse;
 //Too strong? Only use needed threads then
 if (currentSecLevel - reducedSecLevel < minSecurityLevel) {
 threadsToUse = Math.ceil((currentSecLevel - minSecurityLevel) / weakenPerThread);
 currentSecLevel = minSecurityLevel;
 }
 else
 currentSecLevel -= reducedSecLevel;
 ns.exec("/shared/weaken.js", myServer, threadsToUse, target, 0, randomArgument++);
 }
 //Grow the server
 else if (availableMoney < maxMoney && (requiredGrowThreads != 0 || !growthCalculated)) {
 if (!growthCalculated) {
 requiredGrowThreads = Math.ceil(ns.growthAnalyze(target, maxMoney / availableMoney));
 growthCalculated = true;
 }
 //Do not use more than needed
 threadsToUse = Math.min(requiredGrowThreads, threadsToUse)

 //requiredGrowThreads will save how many more threads are needed, if any, for this iteration 
 requiredGrowThreads -= threadsToUse;

 //Let's also not forget that this will raise the security level.
 currentSecLevel += ns.growthAnalyzeSecurity(threadsToUse);
 ns.exec("/shared/grow.js", myServer, threadsToUse, target, 0, randomArgument++);
 }
 // Fully prepped? Let's do batching then
 else {
 
 //PASTE PART 2 HERE, DELETE DOUBLE LINES BELOW (orientation only)

 //How many threads per batch with one Hack thread? Adjust if needed
 //const minBatchThreads = 6;

 

The HWGW Manager – Part 2

// else {
// PART 1 SHOULD BE ABOVE THIS LINE

 //How many threads per batch with one Hack thread? Adjust if needed
 const minBatchThreads = 6;

 //Below this value we risk that not all HWGW execute, so lets not even try that and wait for more threads or other tasks in next rotation.
 if (threadsToUse < minBatchThreads)
 continue;

 const moneyPerHack = ns.hackAnalyze(target);

 // If we are so weak we cant even get a single dime out of a server, lets just not even try it. And also get notified.
 if (moneyPerHack == 0) {
 ns.tprint("INFO - We can not effectively hack this server: " + target);
 continue;
 }

 // Let's dream big and completely take 90% of the availableMoney! how many threads we need for that?
 let hackThreads = Math.ceil(0.9 / moneyPerHack);

 // Reality check. are we eben able to do so? Do we have lots of threads to waste ehm grow i mean? If not, just take a part of our threads for hack and go.
 if (hackThreads * minBatchThreads * 2 > threadsToUse) { // If we do hack 90% of availableMoney we will need more grow threads to compensate, so we double the needed threadsToUse.
 hackThreads = Math.floor(threadsToUse / minBatchThreads);
 }

 //Let's calculate how we divide our threads for HWGW.
 const growThreads = Math.ceil(ns.growthAnalyze(target, 1 / (1 - Math.min(0.99, moneyPerHack * hackThreads))));
 const weakenThreads = Math.ceil(ns.hackAnalyzeSecurity(hackThreads) / weakenPerThread);
 const weakenThreads2 = Math.ceil(ns.growthAnalyzeSecurity(growThreads) / weakenPerThread);

 if (hackThreads + growThreads + weakenThreads + weakenThreads2 > threadsToUse)
 ns.tprint("ERROR: some threads did not start, because we need more minimum threads per batch. raise 'minBatchThreads'");

 //Now get these threads executed. 
 const threadDelay = 150; //ms
 ns.exec("/shared/weaken.js", myServer, weakenThreads, target, batchDelay, randomArgument);
 ns.exec("/shared/weaken.js", myServer, weakenThreads2, target, threadDelay * 2 + batchDelay, randomArgument);
 ns.exec("/shared/grow.js", myServer, growThreads, target, ns.getWeakenTime(target) - ns.getGrowTime(target) + threadDelay + batchDelay, randomArgument);
 ns.exec("/shared/hack.js", myServer, hackThreads, target, ns.getWeakenTime(target) - ns.getHackTime(target) - threadDelay + batchDelay, randomArgument++);
 //If we would fire the next HWGW without this batchDelay, they might intersect. we could also use ns.sleep but ain't nobody got time for that
 batchDelay += 4*threadDelay;
 }
 await ns.sleep(sleepPerIteration);
 }
 await ns.sleep(sleepPerIteration);
 }
 await ns.sleep(iterationPause*1000);
 }
}

Again, i recommend to use the pastebin for easy copy pasting and better read with syntax highlighting, which I linked above part 1.

Nice-to-have: alias and service

This is completely optional and probably only useful, if you adjust and expand the manager to your liking.

With alias you can create a new, easier command for use.

Type or copy the following command in your terminal:

alias nanoman="nano kuposhwgwmanager.js"

change the name ‘nanoman’ to whatever you like. If you changed the name or path of the manager script, make sure to also use the correct one in your alias.

You can create another alias for (re)starting the manager script like that:

alias startman="kill kuposhwgwmanager.js;run kuposhwgwmanager.js"

also i recommend having a startup script, which starts all scripts you use.

This can be as simple as that:

nano start.js

/** @param {NS} ns */export async function main(ns) {
 while(true) {
 if(!ns.isRunning("kuposhwgwmanager.js","home"))
 ns.run("kuposhwgwmanager.js",1);
 await ns.sleep(60000);
 }
}

if you have more scripts, you can expand it like that:

/** @param {NS} ns */export async function main(ns) {
 sleepPause = 60; //seconds
 while (true) {
 if (!ns.isRunning("servercrawler.js", "home"))
 ns.run("servercrawler.js", 1);
 ns.run("/v1/targeter.js", 1);
 if (!ns.isRunning("purchase-server.js", "home") && ns.getServerMoneyAvailable("home") > 11000000)
 ns.run("purchase-server.js", 1);
 if(!ns.isRunning("kuposhwgwmanager.js","home"))
 ns.run("kuposhwgwmanager.js",1);
 if (!ns.isRunning("hacknetmanager.js", "home") && ns.getServerMoneyAvailable("home") > 25000000)
 ns.run("hacknetmanager.js", 1);
 await ns.sleep(sleepPause * 1000);
 }
}

With that you only need to use “run start.js” after a crash or a fresh start, you could even write alias for that too.

Nice-to-have: Log-Tail (or how to disable the window opening on startup)

If you are struggling to get this script to run or if you are expanding and adjusting the script it is always good to have the script log open.

For this i do have this part in the script, you can find it right below

export async function main(ns) {

The log partin the manager script is this:

//Logs are nice to know whats going on
 ns.disableLog('sleep');
 ns.disableLog('getServerMaxRam');
 ns.disableLog('getServerUsedRam');
 ns.disableLog('getServerSecurityLevel');
 ns.disableLog('getServerMinSecurityLevel');
 ns.disableLog('getServerMaxMoney');
 ns.disableLog('getServerMoneyAvailable');
 ns.disableLog('getHackingLevel');
 // If you do not want an open tail window with information what this script is doing on every start, you can remove/comment the tail line below.
 ns.tail();

If you are annoyed by it, you can simply comment the last line.

You can also adjust what you want to get shown in your log by changing the disable logs above.

Inspiration for this guide

I started out by using the guide “Scripting 101” by Suikoudan.

If you did not read that guide yet, i heavily recommend to do so.

You will learn how to build your own servercrawler and also how to do the filemanager thing.

Or at least get a good idea, how to do it yourself.

The opening of the server is also from that guide.

Details, Explanations, Calculations

In this section I will give some additional information about some parts of the script.

If something is missing, feel free to request it in the comments.

Targeting – when to use one target

The manager will only check your hacking level for now, and match it against the “goodHackingLevel” number which is at 750.

This could be done way better, but as i just came up with this a few days ago, it will be good for now.

I hope to improve this check in the future so the script will exactly switch between a single target or all targets, when the best time has come. Share your thoughts to improve it.

Rotations

The manager will loop endless with a pause of 10 seconds by default.

In every loop he will select every target once.

And for every target, the manager will enroll the necessary tasks on all your available servers.

Prepping – reducing the security level to minimum

Because of the rotation, prepping is simple and done by the manager.

The script will take every server you have to reduce the security level to minimum, but no more threads or servers then needed.

Prepping – grow to max money

similiar to the security level the money will also be raised to maximum by all available servers but without wasting more threads or servers than needed.

The manager will also anticipate the raise in security level, so the next of your servers will take care of that with another weaken.

Limitation: Remembering past the loop

Currently the script only remembers a servers grow and security level for a loop.

After that, it will recalculate the needed threads and forget about started scripts that did not yet hit.

There will of course be improvements to address this in the future, but for now this will at worst waste one weaken/grow-timeframe per unprepped target, once.

Limitation: Grow and Weaken from the same server

The manager will not use the same server for growing and weakening while prepping the server.

So it may happen that half the servers RAM is used to weaken, then the next server is selected to grow.

I consider this not a big deal, as the server only used to half will be also considered for the next target and also in the next loop (which will happen after 10s by default). So there is no thread or big time waste happening.

The HWGW Batching

The manager will use at least 6 threads for batching. This should be no problem as the used scripts are 1.75GB which times 6 is less than 8GB, which you should have at minimum on your servers anyway.

I chose 6 for good measure, in my test 5 was enough, so you can adjust this to your liking.

There are basically 2 modes:

The lategame mode, which attempts to take 90% of maxmoney of the target (and of course grow and weaken it to compensate).

And, if that fails because you are not that far into the game and augmentations, the casual mode, which will just use all threads on the server and hacks with 1/6th of them.

The other threads for grow and weak are (hopefully) precisely calculated, based on the amount of hackthreads.

If my guess (1/6th of threads for hacking) is totally wrong, and batches are not executed correctly, you will get an Error in your terminal which informs you about that. In that case just raise the minBatchThreads.

Built-in Failsafes

One goal of this script was to make it safe without crashes.

For this all calculations are double checked so you do not get annoying 0 or infinity thread warnings with exec, or other crash reasons.

If you do manage to crash the script in its default configuration or with little tweaks to variables, please let me know so i can improve it.

This is also the reason why you find a lot of Math.floor, Math.ceil, Math.min, Math.max in it, and even more continue commands.

When creating this manager i prefered to be better safe than sorry and may lose a few percent performance for that.

Limitation: HWGW timing

Although i checked my timing calculation of the batch many times, i got rare reports of users who saw the batch not hitting in the desired order (HGWW, which is still okayish and no big loss).

If you find a flaw in my timing, please let me now.

If there seems to be everything alright, you can try to increase the threadDelay and/or batchDelay.

You can check this by editing the one-shot scripts to give you a ns.tprint() with what they are and check the terminal, how the prints arrive. I only recommend this for debugging and improving purposes.

If your servercrawler always scp the files on servers, not only on first discover/cracking, you can just edit and save the scripts on your home and all future scripts on all target servers will update with a delay. This is also true if you copied the bit in the chapter directly into the manager.

FAQ

I will post some questions asked in here for better visibility.

If you have a question or feedback, you will surely not be the only one and I may just have done an error or failed to explain it clearly, so go ahead and leave all your feedback in the comments.

Thank you!

The end

As I have said way too much in this guide, i am happy to get all kinds of feedback.

– You would change a phrasing?

– Got an improvement to the guide or the code?

– You doubt things stated in this guide are correct?

– The guide failed to explain something you need to understand for this script?

– You just want to leave your experience for others?

Just put it all in the comments so others know if this guide was actually helpful or just trash.

Thanks for reading and i hope this will help you on your BitBurner journey.

Apart from giving feedback i also encourage you to rate this guide, that would be very great!

Written by Kupo

This is all we can share for Explanation on how to HWGW Manager running, centralized prep and farm of one or many servers – Bitburner for today. I hope you enjoy the guide! If you have anything to add to this guide or we forget something please let us know via comment! We check each comment! Don’t forget to check XIXGO.COM for MORE!

TAGGED:
Share This Article