.. _netscriptjs:

NS2
===
The improved version of Netscript that
allows users to write full-fledged Javascript code in their scripts, while
still being able to access the Netscript functions.

ns2 was developed primarily by `Github user jaguilar <https://github.com/jaguilar>`_

On top of having almost all of the features and capabilities of JavaScript, ns2 is also
significantly faster than ns1.

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

Browser compatibility
---------------------
As of the time of writing this, a few browsers do not support `dynamic import <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import>`_ functionality and therefore cannot run ns2 scripts. These browsers will thus only be capable of using ns1.

How to use ns2
----------------------
Working with ns2 scripts is the same as ns1 scripts. The only difference
is that ns2 scripts use the ".js" extension rather than ".script". E.g.::

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

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

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

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

    export async function main(ns) {
        ns.print("Starting script here");
        await ns.hack("foodnstuff"); //Use Netscript hack function
        ns.print(ns.args);           //The script arguments must be prefaced with ns as well
    }

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

* Write :code:`await` before any call to the following Netscript functions:

    * hack
    * grow
    * weaken
    * sleep
    * prompt
    * wget
    * scp
    * write
    * writePort

* Any function that contains :code:`await` must be declared as :code:`async`

* Always :code:`await` any function that is marked as :code:`async`

* Any functions that you want to be visible from other scripts must be marked with :code:`export`.

* **Do not write any infinite loops without using a** :code:`sleep` **or one of the timed Netscript functions like** :code:`hack`. Doing so will freeze your game.

* Any global variable declared in a ns2 script is shared between all instances of that
  script. For example, assume you write a script *foo.js* and declared a global variable like so::

      //foo.js
      let globalVariable;

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

  Then, you ran multiple instances of *foo.js*::

      $ run foo.js 1
      $ run foo.js 1 2 3
      $ run foo.js 1 2 3 4 5

  Then all three instances of foo.js will share the same instance of :code:`globalVariable`.
  (In this example, the value of :code:`globalVariable` will be set to 5 because the
  last instance of *foo.js* 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 <https://www.tutorialspoint.com/cplusplus/cpp_static_members.htm>`_,
  where a ns2 script is a class and a global variable is a static member within that class.

Example
-------

early-hack-template.script

.. code-block:: javascript

    var target = args[0];
    var moneyThresh = getServerMaxMoney(target) * 0.75;
    var securityThresh = getServerMinSecurityLevel(target) + 5;
    if (fileExists("BruteSSH.exe", "home")) {
        brutessh(target);
    }
    nuke(target);
    while(true) {
        if (getServerSecurityLevel(target) > securityThresh) {
            weaken(target);
        } else if (getServerMoneyAvailable(target) < moneyThresh) {
            grow(target);
        } else {
            hack(target);
        }
    }

early-hack-template.js

.. code-block:: javascript

    export async function main(ns) {
        var target = ns.args[0];
        var moneyThresh = ns.getServerMaxMoney(target) * 0.75;
        var securityThresh = ns.getServerMinSecurityLevel(target) + 5;
        if (ns.fileExists("BruteSSH.exe", "home")) {
            ns.brutessh(target);
        }
        ns.nuke(target);
        while(true) {
            if (ns.getServerSecurityLevel(target) > securityThresh) {
                await ns.weaken(target);
            } else if (ns.getServerMoneyAvailable(target) < moneyThresh) {
                await ns.grow(target);
            } else {
                await ns.hack(target);
            }
        }
    }

What's with the weird comment
-----------------------------

You may have noticed that every new ns2 file will contains the following comment.

.. code-block:: javascript

    /**
    * @param {NS} ns
    **/

This comment is used to help the text editor autocomplete functions in the Netscript API. You can enable it by pressing ctrl+space after `ns.`

.. image:: autocomplete.png

The comment can be safely removed but it is recommended to keep it as it will help you.
