Skip to content

hackmud-unofficial/documentation

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

25 Commits
 
 
 
 
 
 

Repository files navigation

Hackmud - Documentation

Credits

Originally created by @ciastex_ and @i20k, slightly edited and updated by @ryo
Special thanks to @dtr/@sudo, @ada and @soron for their expertise.

This documentation is a mirror of the community's Scripting Tutorial (Google Docs). If this document becomes out of date, check there for more recent information.

If you have any questions, don't hesitate to hit us on Discord: https://discord.gg/sc6gVse

Index

Architect Commands

Creating a script

Command: #edit <filename>
This command will create or edit a script, opening it up with your default text editor.
If you are on Windows this may crash, as the default .js command on Windows is the system built-in compiler.
To fix this issue, associate your text editor with the .js file extension. If you don’t have a text editor, get one like Notepad++.
Scripts will be created with a default template.


Opening the script directory

Command: #dir
This command simply opens up your script directory.
You can create new scripts here and them upload them ingame.


Uploading a script

Command: #up <filename>
This command will upload your created script to the server, so you can execute it.
Possible arguments AFTER the filename:

  • delete: will delete your script from the server, but leave it locally.
  • public: will make your script public - assuming you have the public slot upgrade installed and loaded within your system.
  • private: will explicitly mark a script as private (useful to un-public a script while debugging, for example)
  • shift: will 'shift' your script. This is necessary if the security level changes, or you make it public for the first time. Takes approximately 15 minutes, during which you cannot interact with the script.

Downloading a script

Command: #down <filename>
This command will download an already uploaded script from the server.
You could use this to download a script to another machine.


Deleting a script localy

Command: #delete <filename>
This command will remove your script from your computer’s file system, which means you won’t be able to access it from your editor anymore.
Be careful around this command, though - it runs without any confirmation.


Listing all local and uploaded scripts

Command: #scripts or simply #
This command will list all your local and uploaded scripts.
To see your currently uploaded scripts, run scripts.user


The help command

Command: #help
This command will print the in-game architect commands help.

Scripting

Scripts in hackmud are JavaScript (ES6 strict mode) files consisting of a single function with 2 arguments passed by the game:

  • context - This is a context the script is run from, i.e. if a user called noob ran your script, then any command executed from context will be treated as executed by the user who executed it, just like he/she would write them in their command line. Context has the following subkeys:
    • context.caller - The name of the user who is calling the script (i.e. n00b)
    • context.this_script - The name of this script
    • context.calling_script - The name of the script that directly called this script, or null if called on the command line
    • context.cols - The number of columns in the caller's terminal, if reported by the client.
  • args - This is a dictionary containing all the arguments a user passed to your script.
    If the script was called without any arguments (i.e. foo.bar), args will be null. If called with empty arguments (i.e. foo.bar {}), args will be an empty JS object.

Character counts

Hackmud does not count whitespace characters (space, tab, carriage return, newline, vertical tab, and possibly a few others), and does not count // comments. All other forms of comments are counted.

Hackmud ignores // comments by replacing // through the end of a line with the empty string. The parser is not smart enough to know that // inside a string isn’t a comment.

Thus,

var x="http://google.com"

will get mangled -- hackmud will truncate the line to

var x="http:

which will cause a syntax error. The easiest fix is to use // anywhere you want // to appear.

Scriptors

Scriptors are one of the hackmud specific features.
They allow you to call an in-game script from your script. That allows you to parametrize your script’s behavior.
The scriptor syntax is as follows:
#s.user.script

The above can be then passed to your script as an argument, like the following (remember to upload the script!):
crk_ez21 { target:#s.user.command }

To call a command the scriptor points to, there’s a scriptor-specific method which optionally accepts your arguments that will be passed to the called command:

args.target.call({/* optional arguments for the called scriptor */})

Subscripts

If you want to call a hard-coded script (ed note: this isn’t technically a scriptor, it is just a script call), you can do so without using a scriptor, as follows.
Be aware, you cannot store a script to a variable like this:

var x = #fs.user.name

As #s is really a preprocessing directive. #fs.user.name must be used immediately, in the form

#fs.user.name({key:value})

If you want to hard-code a script call that you can reuse, define a wrapper function, like:

function foo(args) {
    return #fs.user.name(args);
}

As of 20/10/2017, using the syntax #s.user.script will result in an error while uploading. The syntax for scriptors is still #s.user.script, however.
The new syntax is as follows: #fs.user.script OR #4s.user.script
Sec levels go from 0 (NULLSEC) to 4 (FULLSEC) or ns, ls, ms, hs and fs. These guarantee that the calling script is at least of that sec level.

Converting a string (like "foo.bar") into a callable. (NOT POSSIBLE!)

Many people want to take a string, like a loc from an NPC corp, and call it directly inside another script.
This is, deliberately, impossible in hackmud.
If you could convert a string into a callable in any way, the entire security level system would fall apart (because any string in any dependency could possibly be a nullsec script. And those strings could come from the database).
If you want to do something with those locs (or similar cases), you will have to pass them in as a scriptor or hard-code them in the file. You cannot call the string directly.

Returning a result

A called script can return basically anything - an array, a string, an object, or even null.
Most scripts in the game however simply return a string.

  • Your script itself generally returns both {ok:true, msg:"string"}
  • The contents of string will automatically be printed to your terminal
  • Both of these arguments are optional, and while you may get an error message if you return nothing from a script, it will still work fine.

Autocomplete

To add autocomplete args to your script, on the first line, after the function boilerplate, add a comment with a list of args and values, like this:

function(context, args) { // arg1:val1, arg2:val2, arg3:#s.an.example, arg4:”example”
    var myArg = args.arg1;
    
}

After uploading the script, you might need to run scripts.user to update your autocomplete, and then it should work.

Special Script Commands

#D(ob) -- Debug Log

If #D is called in a script you own, the return value of the top level script is suppressed and instead an array of every #D’d entry is printed. This lets you use #D kind of like console.log. #D in scripts not owned by you are not shown. #D returns its argument unchanged, so you can do things like return #D(ob) to return the object when the caller isn’t you, and debug-log it when it is you (allowing you to “keep” your returns with other debug logs). #D’d items are returned even if the script times out or errors.

#FMCL -- Function Multi-Call Lock

#FMCL is what escrow.charge uses to ensure it is only called once per script execution. The first time (in each script) that #FMCL is encountered, it returns falsey, and every time thereafter it returns truthy. A common usage pattern is

if(#FMCL)
    return "error"
// do work

The first time that block of code is hit, it will do work, and every time after it will return the error (this applies even if (and specifically for the case where) your script is called multiple times in the same execution)

#G -- Global

#G is a per-script global object. It starts out blank, and you can add whatever properties you want to it. If your script is called multiple times in a single overall script run, its #G is persisted between those calls (but each script sees its own #G). This is useful to cache db lookups that won’t change in scripts that expect to be called many times. Sample usage:

if(!#G.my_db_entry)
    #G.my_db_entry=#db.f({whatever:true}).first();
// use #G.my_db_entry in code below

Special Script Variables

_START

This contains a JS timestamp (not Date) set immediately before your code begins running. You can see how much time remains by doing Date.now()-_START

_TIMEOUT

This contains the number of milliseconds a script is allowed to run for. Effectively always just 5000, except when a trust script is called on the command line and its value is, presumably, 8000.

Macros

Macros are fairly simple, and very useful in hackmud. This is not strictly coding related, but they are not that widely known. Example:

/macroname = test{target:"canhavefixedarguments"}

/hl = kernel.hardline /dc = kernel.hardline{dc:true}

Running /macroname or /hl or /dc will run exactly that command.

Macro with arguments

/fullsec = scripts.fullsec{{start:{0}}}

Notice that you need {{}} (double brackets), call like /fullsec 128 You need to replace every "real" {} with {{}}

Database

Each users’ database in hackmud is a MongoDB collection, in which data is stored as JSON documents.

Query Objects:
Query Objects are a regular JSON object containing keys and values you want to search against.
Projections:
Projections allow you to fetch specific subfields in a #db object. These speed things up quite a bit if your document is large.
Check https://docs.mongodb.com/v3.0/tutorial/project-fields-from-query-results/ for more information.

Inserting

#db.i()
Mongodb documentation on inserting
This command creates new #db documents.
Called like #db.i(<JSON object or array of JSON objects>);
Ex: #db.i({ SID:”scriptname” }) Inserts a document with key “SID” and value “scriptname”

Removing

#db.r()
Mongodb documentation on removing
This command deletes #db documents matching your query.
Called like #db.r({query});
Ex: #db.r({ SID:”scriptname” }) removes all documents where key “SID” contains the value “scriptname”.

Finding

#db.f()
Mongodb documentation on finding
This command returns any documents matching your query.
Called like #db.f({query}, {projection}).command() where “command” is either “first” or “array”/
Ex: #db.f({ SID:”scriptname” }).array() returns an array of documents where key “SID” contains the value “scriptname”.
Ex: #db.f({ SID:”scriptname” }, { field:1, _id:0 }).first() returns the value for the key “field” inside the first document it finds where key “SID” contains the value “scriptname”.

Updating

#db.u()
Mongodb documentation on updating
This command updates any pre-existing documents matching the query.
Called like #db.u({query}, { updateOper:{updatedfields} }) applies “update” to any documents matching the query.
Ex: #db.u({ SID:”scriptname” }, { $set:{field:”new value”} }) sets key field to “new value” in any documents where key “SID” contains the value “scriptname”.
This can be a very complex operation. It is HIGHLY recommended you follow the aforementioned hyperlink.\

Chat API Wrapper

The chat api wrapper allows you to read and send messages through the users you own. You will need to get the chat_pass from the game. (running chat_pass).

The API contains different classes:

Account Class

var API = require('./chat.js')
var acct = new API.Account();

acct.login("token or pass").then((data) => {
    // data is a account object
    // means it contains the users, token, last and the methods...
    var username = data.users.username;
})
Method name Arguments Description
Account last=null The Account constructor
login pass Method used to login
update token Updates the token
poll ext={} Polls the chat
print Prints the account details.
Properties Description
users Array object of the users you own.
token String storing your token.
last

User Class

Note: the Account class generates the users using this class.

Method name Arguments Description
User (account, name, dat) The User constructor
tell (to, msg) Sends a private message to the user "to"
print Prints the user details.
Properties Description
account Object that stores the account of the user.
name Name of the user.
channels Object Array containing the channels the user is in.

Channel Class

Method name Arguments Description
Channel (user, name, users) Channel constructor.
send (msg) Sends the "msg" to the channel
print Prints the channel details
Properties Description
user Your user
name The name of the channel
users Array containing the users of the channel.

Examples

Login

var API = require('./chat.js');
var acct = new API.Account();

acct.login("token or pass").then((data) => {
    ...
})

Sending a message to a channel

var API = require('./chat.js');
var acct = new API.Account();

acct.login("token or pass").then((data) => {
    var channel = data.users.username.channels["0000"];
    channel.send("Hello world!");
})

Reading the chat

var API = require('./chat.js')
var acct = new API.Account();

acct.login("token or pass").then((data) => {
    data.poll().then(o => {
        console.log(o.chats.user); // Replace user with your username
    })
})

Scripts.lib

This is a code library containing useful helper functions you can use in your scripts.

Function name Arguments Returns Description Example (var l = #fs.scripts.lib();)
ok none {ok:true} Helper method to save chars. return l.ok();
not_impl none {ok:false, msg:"not implemented"} Helper method to save chars. return l.not_impl();
log (message) nothing Pushes a string representation of a value onto an array of log messages. You have to use the method below to print it. l.log("hackmud is the best hacking game!")
get_log none Array containing the stored logs Gets the stored logs, Does not clone or clear the array afterwards; it's a direct reference to the same array. var log_arr = l.get_log();
rand_int (min, max) integer Returns a random integer between min/max var r = l.rand_int(0, 10);
are_ids_eq (id1, id2) boolean Tests whether id1 and id2 values are equal. Apparently buggy at the moment.
is_obj (what) boolean Returns true if the what is a object. (Arrays are objs too) var is_obj = l.is_str({name:"ryo", gc:"1TGC"});
is_num (what) boolean Returns true if the what is a Number. (This treats NaN (not a number) as not a number, even though in JS, typeof NaN == "number".) var b = l.is_num(2);
is_int (what) boolean Returns true if what is is both a Number (via is_num), and also an integer. var b = l.is_int(3);
is_neg (what) boolean Returns true if what is is both a Number (via is_num), and also negative (i.e. <0) var b = l.is_neg(-3);
is_arr (what) boolean Returns true if what is an array var b = l.is_arr([2,3,4]);
is_arr (what) boolean Returns true if what is a function var b = l.is_func(my_func);
is_def (what) boolean Returns true if what is defined so, NOT undefined. Note: null != undefined var b = l.is_def(my_var);
is_valid_name (what) boolean Returns true if what is a valid user/script name. var b = l.is_func(my_func);
dump (obj) string Returns a string representation of the obj argument. return l.dump(my_obj);
clone (obj) object Returns a clone of the obj argument (meaning references are broken). var cln_obj = l.clone(org_obj);
merge (obj1, obj2) nothing Merges the contents of obj2 into obj1. This can be useful for combining defaults with user-specified values, but it is not quite secure on its own (i.e. don’t trust it to secure DB filters).
get_values (obj) array Returns an array containing the values of the object properties. var value_arr = l.get_values(my_obj);
hash_code (string) number Returns a number calculated based on the string argument. var hash = l.hash_code("test");
to_gc_str (num) string Converts raw num number to a GC currency representation. var gc_string = l.to_gc_str(100);
to_gc_num (str) number Converts raw num number to a GC currency representation. var gc_num = l.to_gc_num("10MGC");
to_game_timestr (date) string Converts a Date object specified via date parameter to a game-styled time string. var date_str = l.to_game_timestr(new Date())
cap_str_len (string, length) strring Truncates the given string to the given length if it's longer than that.
each (array, fn) array Runs fn on each array element. var new_arr = l.each(old_arr, (key, val) => {...});
select (array, fn) array Returns a collection of values from array that matches the fn predicate. If the predicate returns true, the select function adds the key:value pair currently processed to the returned collection of values. var new_arr = l.select(old_arr, (key, val) => val % 2 == 0 ? true : false);
count (array, fn) number Returns a number of items from array that matches the fn predicate. If the predicate returns true, the count function increments the returned number by one.
select_one (array, fn) array Same as the select function, but returns the first value that matches the predicate.
map (array, fn) array Creates a new array with the results of calling a provided function on every element in the calling array. var new_arr = l.map(old_arr, x => x*2);
shuffle (array) array Shuffles an array and returns it.
sort_asc (one, two) array If one > two, returns 1. If two is greater than one, return -1. Else return 0. Looks like a sorting function
sort_desc (one, two) array Returns the opposite of the above, ie -1 on one > two, and 1 on two > one
num_sort_asc (...?)
num_sort_desc (...?)
add_time (date, add_ms) date Gets the date of date + add_ms (milliseconds) return l.add_time(new Date(), 4000);
security_level_names It's not a function, array containing the security levels (NULLSEC, LOWSEC, MIDSEC, HIGHSEC, FULLSEC) var nullsec = l.security_level_names[0];
get_security_level_name (level) string Takes a parameter between 0 and 4 (inclusive), returns the corresponding security from NULLSEC (0) to FULLSEC (4)
create_rand_string (len) string Returns a random string consisting of lowercase alphanumeric characters.
get_user_from_script (script_name) string Returns the user from a script name. Ie me.target returns me
u_sort_num_arr_desc
can_continue_execution (ms) boolean Returns true whether the script can still be on execution in the provided time. l.can_continue_execution(1000);
can_continue_execution_error
can_continue_execution_error
date
get_date date Gets the current date
get_date_utcsecs number Gets the current time from the date (ie Date.getTime())