+ +
+

NetscriptJS (Netscript 2.0)

+

Netscript 2.0, or Netscript JS, is the new and improved version of Netscript that +allows users to write (almost) full-fledged Javascript code in their scripts, while +still being able to access the Netscript functions.

+

NetscriptJS was developed primarily by Github user jaguilar

+

On top of having almost all of the features and capabilities of Javascript, NetscriptJS is also +significantly faster than Netscript 1.0.

+

This documentation will not go over any of the additional features of NetscriptJS, since +there is plenty of documentation on Javascript available on the web.

+
+

NetscriptJS in Mozilla Firefox

+

As of the time of writing this, the Mozilla Firefox browser does not support +dynamic import functionality and therefore cannot run NetscriptJS scripts.

+

(This may be some option/method for enabling this in Firefox, but I don't know +what is it)

+
+
+

How to use NetscriptJS

+

Working with NetscriptJS scripts is the same as Netscript 1.0 scripts. The only difference +is that NetscriptJS scripts use the ".ns" or ".js" extension rather than ".script". E.g.:

+
$ nano foo.ns
+$ run foo.ns -t 100 arg1 arg2 arg3
+exec("foo.ns", "purchasedServer1", "100", "randomArg");
+
+
+

The caveat when using NetscriptJS to write scripts is that your code must be +asynchronous. Furthermore, instead of using the global scope and executing your code +sequentially, NetscriptJS uses a main() function as an entry point.

+

Furthermore, the "Netscript environment" must be passed into a NetscriptJS script through +the main function. This environment includes all of the pre-defined Netscript functions +(hack(), exec, etc.) as well as the arguments you pass to the script.

+

Therefore, the signature of the main() function must be:

+
export async function main(ns) {
+    ns.print("Starting script here");
+}
+
+
+

Here is a summary of all rules you need to follow when writing Netscript JS code:

+
    +
  • Write await before any call to the following Netscript functions:

    +
    +
      +
    • hack
    • +
    • grow
    • +
    • weaken
    • +
    • sleep
    • +
    • run
    • +
    • exec
    • +
    • prompt
    • +
    +
    +
  • +
  • Any function that contains await must be declared as async

    +
  • +
  • Always await any function that is marked as async

    +
  • +
  • Any functions that you want to be visible from other scripts must be marked with export.

    +
  • +
  • Do not write any infinite loops without using a sleep or one of the timed Netscript functions like hack. Doing so will crash your game.

    +
  • +
  • Any global variable declared in a NetscriptJS script is shared between all instances of that +script. For example, assume you write a script foo.ns and declared a global variable like so:

    +
    //foo.ns
    +let globalVariable;
    +
    +export async function main(ns) {
    +    globalVariable = ns.args.length;
    +    while(true) {
    +        tprint(globalVariable);
    +        await sleep(3000);
    +    }
    +}
    +
    +
    +

    Then, you ran multiple instances of foo.ns:

    +
    $ run foo.ns 1
    +$ run foo.ns 1 2 3
    +$ run foo.ns 1 2 3 4 5
    +
    +
    +

    Then all three instances of foo.ns will share the same instance of globalVariable. +(In this example, the value of globalVariable will be set to 5 because the +last instance of foo.ns to run has 5 arguments. This means that all three instances of +the script will repeatedly print the value 5).

    +

    These global variables can be thought of as C++ static class members, +where a NetscriptJS script is a class and a global variable is a static member within that class.

    +
  • +
+
+
+

Warnings

+

The NetscriptJS evaluation engine works by converting your code into a blob URL and then +using a dynamic import to load your code as a module. Every unique NetscriptJS script +is loaded as its own module. This means that +making a small edit to a NetscriptJS script results in a new module being generated.

+

At this point, we have been unable to find a method for deleting modules from browsers so that +they get garbage collected.

+

The result is that these modules from NetscriptJS scripts accumulate in your browser, +using memory that never gets released. Over time, this results in a memory-leak type +situation that can slow down your computer.

+

Therefore, there are two recommendations for those who decide to use NetscriptJS:

+

1. Every now and then, close and re-open the game. This will clear all of the modules. +To be safe, I recommend completely closing the game's tab and then re-opening it. +Depending on your browser, a refresh or page reload does not always clear the modules.

+

2. Only use NetscriptJS scripts when needed. It is very unlikely that NetscriptJS +is needed for very simple scripts. By doing this, you will reduce the number of modules +that are loaded.

+
+
+

Examples

+

DOM Manipulation (tprintColored.ns)

+

Directly alter the game's terminal and print colored text:

+
export function tprintColored(txt, color) {
+    let terminalInput   = document.getElementById("terminal-input");
+    let rowElement      = document.createElement("tr");
+    let cellElement     = document.createElement("td");
+
+    rowElement.classList.add("posted");
+    cellElement.classList.add("terminal-line");
+    cellElement.style.color = color;
+    cellElement.innerText = txt;
+
+    rowElement.appendChild(cellElement);
+    terminalInput.before(rowElement);
+}
+
+export async function main(ns) {
+    tprintColored("Red Text!", "red");
+    tprintColored("Blue Text!", "blue");
+    tprintColored("Use Hex Codes!", "#3087E3");
+}
+
+
+

Script Scheduler (scriptScheduler.ns)

+

This script shows some of the new functionality that is available in NetscriptJS, +including objects and object constructors, changing an object's prototype, and +importing other NetscriptJS scripts:

+
import {tprintColored} from "tprintColored.ns"; //Importing from other NetscriptJS scripts works!
+
+function ScriptJob(params) {
+    if (params.fn == null) {
+        throw new Error("No Filename (fn) passed into ScriptJob ctor");
+    }
+
+    this.fn         = params.fn;
+    this.threads    = params.threads ? params.threads : 1;
+    this.args       = params.args    ? params.args : [];
+}
+
+ScriptJob.prototype.run = async function(ns) {
+    let runArgs = [this.fn, this.threads].concat(this.args);
+    await ns.run.apply(this, runArgs);
+    tprintColored("Running " + this.fn + " on " + ns.getHostname(), "blue");
+}
+
+ScriptJob.prototype.exec = async function(ns, target) {
+    ns.scp(this.fn, target);
+
+    let execArgs = [this.fn, target, this.threads].concat(this.args);
+    await ns.exec.apply(this, execArgs);
+
+    tprintColored("Executing " + this.fn + " on " + target, "blue");
+}
+
+export async function main(ns) {
+    tprintColored("Starting scriptScheduler.ns", "red");
+    try {
+        let job = new ScriptJob({
+            fn:         "test.js",
+            threads:    1,
+            args:       ["foodnstuff"]
+        });
+        await job.run(ns);
+        await job.exec(ns, "foodnstuff");
+    } catch (e) {
+        ns.tprint("Exception thrown in scriptScheduler.ns: " + e);
+    }
+}
+
+
+
+
+

Final Note

+

NetscriptJS opens up a lot of possibilities when scripting. I look forward to seeing +the scripts that people come up with. Just remember that the power and capabilities of +NetscriptJS come with risks. Please backup your save if you're going to experiment with +NetscriptJS and report any serious exploits.

+

With great power comes great responsibility

+

Happy hacking

+
+
+ + +