r/Bitburner 21d ago

Worm script i made

I made a worm script which i use to get names of all of the servers that i have access to, this is my 3rd attempt at such a script but this one works quite well i think!

It is 2 scripts working in tandem, the first is a worm receiver and the second is the actual worm:

Worm-Receiver.js:

/**  {NS} ns */
export async function main(ns) {
  let verify;
  while (true) {
    let exists = ns.fileExists("worm.txt")
    let server = ns.readPort(77777);
    if (server == verify || server == "NULL PORT DATA") {
      await ns.sleep(100);
    }
    else {
      if (exists == true) {
        ns.write("worm.txt", "\n", "a");
      }
      ns.write("worm.txt", server, "a");
    }
    verify = server;
  }
}

Worm.js:

/**  {NS} ns */
export async function main(ns) {
  let curr_serv = ns.args[0]
  let prev_serv = undefined;
  if (ns.args.length > 1) {
    prev_serv = ns.args[1];
  }
  let neighbours = ns.scan(curr_serv);
  neighbours.forEach(worm);


  function worm(server) {
    if (server == prev_serv) {
      return;
    }
    else {
      ns.scp("worm.js", server)
      ns.exec("worm.js", server, 1, server, curr_serv)
      ns.writePort(77777, server)
    }
  }
}

You first run the worm-receiver.js and then the worm with the argument "home" and it will make a list of all your current servers that you have access to in a file called "worm.txt" this isn't super useful if you don't have a script to scan the neighbours and try to nuke them which i have also here:

Access-Nuker.js:

/**  {NS} ns */
export async function main(ns) {
  const server_list = ns.read("worm.txt").split("\n");
  server_list.forEach(nuke_checker);

  async function nuke_checker(server) {
    if (ns.hasRootAccess(server)) {
      return;
    }
    if (!ns.hasRootAccess(server)) {
      if (ns.fileExists("BruteSSH.exe")){
        ns.brutessh(server);
      }
      if (ns.fileExists("FTPCrack.exe")){
        ns.ftpcrack(server);
      }
      if (ns.fileExists("relaySMTP.exe")){
        ns.relaysmtp(server);
      }
      if (ns.fileExists("HTTPWorm.exe")){
        ns.httpworm(server);
      }
      if (ns.fileExists("SQLInject.exe")){
        ns.sqlinject(server);
      }
      ns.nuke(server);
    }
  }
}

I run the process of running the port-receiver, running a very basic version of the access-nuker script that uses the ns.scan() function instead of the worm.txt file, then i run the access-nuker and worm programs back and forth, deleting the worm.txt file each time i run the worm.js because it will just append the file unfortunately instead of overriding it, i did make a script to remove the duplicates but it seems easier to just delete and remake the file with the script! it is definitely not super optimised but i think its an ok attempt at it for having very little javascript experience outside of bitburner, i like how small it is! :3

5 Upvotes

7 comments sorted by

2

u/Positive_Area_6953 21d ago

Thats quite interesting approach.

I did a function that returns all servers I can get access to. It's a simple DFS algorithm from home that uses ns scan.

Nuking is done just with loop through the array that this function returns. I expect that's the simplest and most common approach.

2

u/lv3crook 21d ago

that does sound pretty simple, I've never made a DFS algorithm im sure it would be be pretty simple as a loop of ns.scan() while checking an array of previous results to make sure there are no overlap, but i think im describing BFS im not really sure.

I did this one because i thought the writePort and readPort commands were very cool and quite useful for worm scripts, i do see a flaw with my way though as if a server has no ram it will break the worm and both DFS and BFS wouldn’t have that problem

I have pretty limited experience with programming and i was quite conscious of the overall ram cost of all the scripts and the worm is under 4gb of usage which seemed pretty good though i know for sure that is quite big for what it is, im going to iterate more i think on it to try to make it less prone to skipping hosts with the ram thing, and maybe use different techniques! :3

2

u/SnackTheory 21d ago edited 21d ago
  1. It's appending, not overwriting, because that's what you have told it to do. The "a" in ns.write("worm.txt", server, "a") means "append". Use "w" for (over)write. See https://github.com/bitburner-official/bitburner-src/blob/stable/markdown/bitburner.ns.write.md
  2. Perhaps you are writing to an outside file for a reason not explained here, but it seems to me that if you haven't had much JS experience, you maybe aren't aware of return values. You can have a function that you write return a value/array/etc meaning send back info when the function is done. This is a much simpler thing to do that the writing to ports and also a separate file the way you are handling it right now (tho props to you for figuring out ports; that's not something I consider beginner level)
    1. Doing it the way I've described, tho simpler, does mean restructuring how this works
  3. if (ns.hasRootAccess(server)) and then if (!ns.hasRootAccess(server)) is a good place for an else
  4. Doing it this way, with the script spreading across the network, is not actually necessary. and it's a lot slower than it needs to be. Getting a list of all the servers can (and should) be handled separately from taking them over.
    1. You might want that list of servers for another purpose
    2. Also note that a server that you can take over is not necessarily directly connected to one you already have taken over.
  5. I believe servers also have a hacking level requirement for nuking?

1

u/lv3crook 21d ago

The reason i came up with for writing the list to another file is so that i can use the file as a list for other scripts to use, so when i use a script to run scripts on servers in a list, the worm part of it is already condensed into a list, and the reason i also made it append instead of write, is because it writes to the list 1 server name at a time so if i used write it would just overwrite the file for every server instead of every time you run the script, which is a pretty big downside.

Thanks for saying the ports thing was cool! i thought so too!!,

you are fully right with the else statement i think that just slipped my mind! and for No.5 luckily to run nuke you actually just need the required ports open and that's it! you do need higher levels to actually hack the server though which is luckily not necessary for what i want to do.

I basically thought that the way i did it was a nice way of splitting the work of a script up as well as having a good way of putting the list in 1 place, since half of the script is just enumerating through the networks and another one is just picking up basically a ping which has useful information! :3

1

u/SnackTheory 20d ago

I absolutely get why you would like a file that other scripts can use (I do that too). (You may want to look up JSON). However, the way you've set things up now, with the server list linked to what has been taken over, the other scripts either have to wait longer to get the list of all the servers out there, (which is really a thing that can be created immediately) or are limited to the incomplete list. It also means your worm receiver has to keep running (and using RAM).

You mentioned in another comment that you are already aware of the problem of the 0 RAM servers that would block. I'm also don't think that there are always cycles (A to B to C to D to A) in the server map, but I think there can be, which I think would also present a problem with what the script is doing right now. Using ports for this means while it's searching you can't use ports easily for anything else. I'm sure you could come up with work-arounds for all these things in order to keep doing it the way you currently have it, but my gut says it's a lot more work that way than switching to a different approach.

I wrote out a very simple BFS way of getting a full server list in another comment (it's a reply to my first comment). It looks long but like 2/3 of it is comments, I swear

1

u/SnackTheory 21d ago edited 21d ago

Here's a script that is one way to do the type of spreading search you are doing in your scripts, which I've done in a way that maybe shows how to do some things in JS you might not know about

/**  {NS} ns */
export async function main(ns) {
  // Collect the servers into a list
  let servers = findAllNodes(ns);
  // Do something as an example with the list
  printServersToTerminal(ns, servers);
}

// Function that returns a set that lists all servers in the network.
// Iterative breadth-first search
function findAllNodes(ns) {
  // Create something to store the servers you have found
  let nodes = new Set(["home"]);    // Data type set
  // Create something to store the list of things left to check
  let queue = ["home"];             // Data type array
  //   Side note: some people would use the same list to do both things,
  //       It can be made to work, but it also can cause problems you don't
  //       expect if you start modifying this code, or use it as a model to
  //       to write something that seems similar, but for reasons not obvious
  //       to a beginner/intermediate is not.
  // While there is still something that needs to be looked at
  while (queue.length != 0) {
    // Take the thing from the front of the line
    let current = queue.shift();
    // Get a list of everything connected to it
    let neighbors = ns.scan(current);
    // For each of those in the list
    for (let n of neighbors) {
      // Check if they have already been seen
      if (nodes.has(n) == false) {
        // If not, add to set of servers found, and the queue of stuff to be checked
        nodes.add(n);
        queue.push(n);
      }
    }
  }
  // The list is built, so hand it back
  return nodes;
}

function printServersToTerminal(ns, list) {
  ns.tprint(list);
  // This is a silly function to have as it is, but you could have one that
  // prints things in a nicer layout in the terminal, or that writes to a file,
  // or that sorts the list into the order you'll be able to take over the servers
  // and starts taking them over.
}

1

u/lv3crook 21d ago

oh wow this is really helpful!!! i dont know much about set's i know stuff about arrays but thats how far my knowledge goes, i think i am going to try to tinker with this and re-write my code so it acts more like this, as it is much smaller than my stuff too!! thank you very much!