hackmud

hackmud

47 ratings
hackmud scripting info
By Pete Parson's car collection
this guide will teach some basic javascript info, walk you through how to make a script, and how it interacts with hackmud.
5
2
   
Award
Favorite
Favorited
Unfavorite
prelude
hackmud is a game about larceny, theft, and deception... or it is about scripting!
scripting is a fun way to create challenges for yourself in the game, and as a overall goal.

so what are something you can do with it?

say that you are looking for users in a t1 corp. this can be a bit of a process, but you could write a script that finds all the users, and outputs them to the screen, turning possibly 5 minutes into only a couple of seconds.

this guide will walk you through how to do this, and kickstart your scripting experience (and hopefully gain interest in learning programming, not just for hackmud).
what do you need?
to get started programming for js (for the purposes of simplicity, i will refer to javascript as "js"), you will need a few things, discussed here.

  • a valid copy of HackMud (obviously)
  • I would recommend setting your game in borderless windowed mode. this guide explains how to do it: http://steamcommunity.com/sharedfiles/filedetails/?id=772703590
  • you must be outside of the VLan.
  • an IDE(integrated development environment) , this is what you will be programming with, a good option is atom.[atom.io]
  • this guide (previous experience in any programming language is excellent, but not required).
getting started
once in game, type:
#dir
this should open your default file explorer to the folder where all of your scripts will be stored.
yours probably won't have any scripts in it, since this is probably your first time opening it.

next, you must create a new js file. todo this, go back into hackmud, and type:
#edit test , where "test" can be any name, just as long as it doesn't have any spaces.

if you are following this guide with windows, then you will get an error if you dont have a default program set to open .js files. close the popup message, then right click on the new file you have created, click "properties". then click "change".

you will get a new window asking for a program. atom isnt here, so we will have to point towards its .exe. on default, it is located in C:\Users\YOUR_USER\AppData\Local\atom\atom.exe

once that is done, just open your test file. if done right, then the file should read this:

function(context, args) { var caller = context.caller; var l = #s.scripts.lib(); return { ok:false }; }

congrats, now we are ready to begin!
hello world!
in the last section, we created a file called test, and we set atom to open it on default. using #dir ingame, open your test file. your code will display:
function(context, args) { var caller = context.caller; var l = #s.scripts.lib(); return { ok:false }; }
(this also works, you just have to make sure that your code is enclosed in curly brackets)
function(context, args){ var caller = context.caller; var l = #s.scripts.lib(); return { ok:false }; }
for the purposes of starting, we will remove the code included within the brackets, all of the code included will be explained later.
function(context, args){ }
you will first need something to be displayed to the screen(what would the purpose of the script be otherwise?). the way you would do that is by typing:

return {msg:"hello world!"}

it would look like this:
function(context, args){ return {msg:"hello world!"} }
save your script by pressing ctrl-s, and #up test your new script ingame. you can see what scripts you have uploaded by using scripts.user (running scripts.user will also add your autocompletes in your scripts, which will be explained later)

the output of scripts.user should return:
YOUR_USER.test, where YOUR_USER is your username ingame.

running your script should return this to the screen:
{
msg: "hello world!"
}

another type of script output is:
ok:STATUS, where STATUS can be true, or false.
it is simple to add this to our current script. open it in atom, and add:
ok:true to your return statement, like this:
function(context, args){ return {ok:true, msg:"hello world!"} }
your script (after uploading) should display this:
Success
hello world!

replacing ok:false should return this:
Failure
hello world!

there are multiple ways to display text to a screen. another way you can is with variables, which will be shown in the next section.
variables
variables are a way to store a given value. you can store just about anything in it, in this syntax:

var text = "hello world!";
keep in mind that this stores a text in a String, a datatype which consist of text.
if you wrote hello world! without quotes, there would be an error, because it can't be a valid String without quotes.

however, if you put:
var number = 42;
this would work, because this datatype is an int, a type used to store numbers without decimal points (42.0). putting quotes around the 42 would make it a String, and display text. there are times that the datatype matters (for example, a method which works with a boolean datatype will give an error on int datatypes)

a way that this can be implented in out script, is this:

function(context, args){ var text = "hello world!"; return {ok:false, msg:text} }
(note: you MUST declare a variable before you call it. if you did:
function(context, args){ return {ok:false, msg:text} var text = "hello world!"; }
this would only return Failure, because the variable text wasnt defined before the call for it.

you can do math as well.
function(context, args){ var text = "hello world!"; return {ok:false, msg:2+2} }

this would return:
Failure
4

you can also display a variable which has had math done to it.
function(context, args){ var equation = 2+8; return {ok:false, msg:equation} }
would return:
Failure
10
(remember not to wrap 2+8 in quotes, because this would return 2+8 instead of 10)
adding comments
you can add a comment by putting:

// WHATEVERYOURCOMMENTIS

this wouldnt be interpreted as code, and wont be run.
for example:
function(context, args){ // this is my equation :) var equation = 2+8; return {ok:false, msg:equation} }

would still return:
Failure
10

the purpose of a comment is for anyone reading the code to understand what is going on in it.
arguments
so what if you want your code to interact with something ingame, such as to chat with someone (normally done with chats.tell), or to run scripts.user?

well, you can do that... with hardcoded scripts that is.
you CANNOT turn something from a script's output into a callable script!

for example, you can make a script which displays all the npc locs in a npc corp, but you cannot make a script which searches a npc corp for npc locs, and cracks them in the same script. you can, however, make a script for finding npcs, and a script for cracking the locks of that npc, and use those, one after another.

so how do you call a hardcoded script within your own script?

by returning it!

function(context, args){ // gets what the user enters as the arg {target:#s.NPCCORP.NAME} var npcScriptCall = args.target.call(); // returns the result! return npcScriptCall; }

what this will do is get what the user enters, and return it without curly brackets.
if you want to, you can return it with brackets by doing:
var npcScriptCall = args.target.call({});

but what does args.target.call even mean?
if you look at the top of the script, you will see within the parentheses that it says context, args.
args is what the user enters as an argument.
for example, if we are looking for the user to enter an argument called arg, then we would do:

args.arg.call();

.call() is used to enter our own information into the call.

but its a little annoying to have to type out the name of the argument, then the value(expecially if you didnt create the script).
the next section will discuss autocompletes.
autocompletes
autocompletes are very simple to make, and are useful to anyone using the script.
lets look at our code:
function(context, args){ var npcScriptCall = args.target.call(); return npcScriptCall; }
to make an autocomplete looking for the argument, target, we can put this right after the main function:
// target:#s.t1npc.corp
it would look like this:
function(context, args){ // target:#s.t1npc.corp var npcScriptCall = args.target.call(); return npcScriptCall; }
upload it, then run scripts.user to add the autocomplete. while typing it out, you should see the suggested args. using the tab key while this is on screen will autocomplete it for you.
if statements
if statements can be used to test if a certain condition has or hasn't been met. if it is true, then the statement will run. if it hasn't, then the statement will be skipped, and will not run.

so why would you want to use this?

say that a user runs your script without arguments. your script would give an error right?
lets give a messege to the user saying how to use the script.
function(context, args){ // target:#s.t1npc.corp if (!args){ return {ok:false, msg:"usage: YOURUSER.test {target:#s.t1npc.corp}"} } var npcScriptCall = args.target.call(); return npcScriptCall; }

what the ! means is if there are no args. if it was
if (args)
then it would run if the user entered args. we want it to run if the user doesnt use any args, so we use an !.

run your script like this:
YOURUSER.test, where YOURUSER is actually your username.

your return should be:
Failure
usage: YOURUSER.test {target:#s.t1npc.corp}

but if you run it like this:
YOURUSER.test {}
it would give an error.

how do we solve this?

simple; we make it run if the user doesnt add args, or if the user doesnt add the arg for target.
todo this, we use a new operator called a pipe, or ||.

a pipe is used as an or statement, meaning if either of these things happen, then run.
the | is located above the shift key on the right side of an american keyboard.

for example, if we add it to our code like this:
function(context, args){ // target:#s.t1npc.corp if (!args||!args.target){ return {ok:false, msg:"usage: YOURUSER.test {target:#s.t1npc.corp}"} } var npcScriptCall = args.target.call(); return npcScriptCall; }

then if we do YOURUSER.test or YOURUSER.test {}, then it will give the same help messege.

if we add the actually arg for a target, then it will display the front page of the corp.
loops
a loop is a statement which runs the same code, until a condition has been met. this is useful if you dont want to copy-and-paste the same code over-and-over, or if you dont know when something might be.

there are two kinds of loop; for loops, and while loops.

for loops are used for when you know when it will end.
a for loop is written as:
for (var i = 0; i < 10; i++){ code here... }

the first part of the statement is executed before the code block, in this case, a variable is created, called "i", with the value of 0.

the second part is the condition for running the loop, in this case, it will end once i is at 10.

the third part is executed each time after the loop, in this case, after a successful run, i will be added once (thats what i++ means, but if you wanted to increment by 2, you would write it as i+2)

a while loop is a loop which will run through a block of code while a condition is true.
a while loop is written as:
while (eating == true){ code here... }

the statement withing the parentheses is what will be checked each run (be aware that this can go on forever if the condition isnt met, so always make sure that there is a way it can end).
regexp (regular expressions)
regex is a method to search the contents of a string and store it within an array, to then be used later.

an array is a datatype which stores multiple pieces of data in "sections". think of it like a book, the book being the variable, and each of the pages being a piece of data.

it is written as:
/pattern/modifiers;

for example, var regex = /\s(\w+)\s/g;

so how is a regex pattern written?

go to the website regex101[regex101.com], and place it on the javascript flavor.
adding the regex above with the nuutec.public page (without args) with return this:
so what does the statement mean?

well, the first part of the statement (before the ()) is the beggining of what we are looking for; what happens before what we are looking for. in this case, \s means a space.

the second part is what happens during. the capture group (what you are looking for) is always wrapped in (). within, is what it will consist of. in this case it is \w, which means "any letters uppercase and lowercase letters, numbers, and underscores". + is a modifier you can add which means "any length". we know that the word is of any length, so we add a +.

the third part is the ending of what we are looking for, in this case, a space (\s).

the modifier that was chosen is /g, meaning global. the modifier usually doesnt matter, but a global modifier means that the regex searches for all the matches. this is usually the modifier that i use.

but we are looking for " happening | strategy | " in this, and there are 38 matches! how do we get these words specifically?

simple; you look for what is different than the rest of the string. in this case, they always have a | afterwards.

so lets account for that.
\| means a literal |, and it happens after each of the words and one space, so lets add it.
as you can see, the regex has caught the words "happening" and "strategy". in the array, this would look like:
["happening", "strategy"]

so lets put the regex into our test program!
it should look like this:
function(context, args){ // target:#s.t1npc.corp // checks if the user didnt enter args, or if they didnt enter a target if (!args||!args.target){ return {ok:false, msg:"usage: YOURUSER.test {target:#s.t1npc.corp}"} } var response = args.target.call(); // regex for pub pages var regex = /\s(\w+)\s\|/g; return npcScriptCall; }
so how do we use these?

the first task would be to check if the regex caught anything, but we still have to use it.

to use a regex, you have to use a method called .exec. this will execute a regex on a string. it would look like this:
var matches = regex.exec(response);
but running this would only return the first value twice, so we have to create a loop to push these to an array.

here is a modified version of the loop provided by regex101.com, modified to suit our needs:
var resultOfMatch = []; var matches = regex.exec(response); while (matches) { if (matches.index === regex.lastIndex) { regex.lastIndex++; } resultOfMatch.push(matches[1]) matches = regex.exec(response); } return [...new Set(resultOfMatch)];
once implemented, your script will look like this:
function(context, args){ // target:#s.t1npc.corp // checks if the user didnt enter args, or if they didnt enter a target if (!args||!args.target){ return {ok:false, msg:"usage: YOURUSER.test {target:#s.t1npc.corp}"} } var response = args.target.call(); // regex for pub pages var regex = /\s(\w+)\s\|/g; var resultOfMatch = []; var matches = regex.exec(response); while (matches) { if (matches.index === regex.lastIndex) { regex.lastIndex++; } resultOfMatch.push(matches[1]) matches = regex.exec(response); } return [...new Set(resultOfMatch)]; }
running this ingame will give you the two page names on the front page!

(note: if the text your trying to find is a different color than the default blue, then you will have to account for that. todo this, you just place `.(\w+)` to account for the color difference)
functions
our code is now large enough to where it makes sense to make functions for the several task the script has to perform.

so what is a function?

a function is a block of code that can be called multiple times to be run, without having to write the code each time.

an example function would look like this:
function myfunc(p1){ return p1; }

you first define a function by writing function, followed by the name of the function (function names follow the same rules as variable names), then there will always be () afterwards, within is variable names that it can input for its use (if it requires them).

you can invoke a function multiple ways, one way you could is by writing this:
var pubpages = functionName(input);

we can make our regex search a function, like this:
function search(regex, response) { let resultOfMatch = []; let matches = regex.exec(response); while (matches) { if (matches.index === regex.lastIndex) { regex.lastIndex++; } resultOfMatch.push(matches[1] || matches[2] || matches[3]) matches = regex.exec(response); } return [...new Set(resultOfMatch)]; }

using this, your code would look like this:
function(context, args){ // target:#s.t1npc.corp // checks if the user didnt enter args, or if they didnt enter a target if (!args||!args.target){ return {ok:false, msg:"usage: YOURUSER.test {target:#s.t1npc.corp}"} } // searches a page (response) looking for regex matches, and storing them in an array function search(regex, response) { let resultOfMatch = []; let matches = regex.exec(response); while (matches) { if (matches.index === regex.lastIndex) { regex.lastIndex++; } resultOfMatch.push(matches[1] || matches[2] || matches[3]) matches = regex.exec(response); } return [...new Set(resultOfMatch)]; } // main source of output, also used for development purposes function loginController(){ var response = args.target.call(); // regex for pub pages var pubpages = search(/\s(\w+)\s\|/g, response); return pubpages; } return logicController(); }
adding to our script using current knowledge
the next step for our script would be to get the navigation method for the npc script.
to access this info, we would just add empty args to our input.

so if we add on to our login controller function like this:
function logicController(){ var response = args.target.call(); // regex for pub pages var pubpages = search(/\s(\w+)\s\|/g, response); response = args.target.call({}); return response; }
this will output the script, but with empty args ({}).
we now have to regex this page for the navigation arg.
our regex will be /\s(\w+):/g
so we will write:
function logicController(){ var response = args.target.call(); // regex for pub pages var pubpages = search(/\s(\w+)\s\|/g, response); response = args.target.call({}); var navarg = search(/\s(\w+):/g, response); return navarg; }
this will output the navigation arg for the script.

we now have to combine these into something we can call the script with to get to the news page.
arrays
arrays are a datatype which stores multiple pieces of data within one variable.
regexes also store returned strings into arrays, so we cant just add them together like strings. we will have to use a method in order to do that.

we first start by creating an empty object. we do this by creating a variable with empty curly brackets, like this:
var page = {};
arrays can be objects, so we add them to the array page by writing this:
page[navArg] = pubPages[0];

using this, your code will now look like this:
function(context, args){ // target:#s.t1npc.corp // checks if the user didnt enter args, or if they didnt enter a target if (!args||!args.target){ return {ok:false, msg:"usage: YOURUSER.test {target:#s.t1npc.corp}"} } // searches a page (response) looking for regex matches, and storing them in an array function search(regex, response) { let resultOfMatch = []; let matches = regex.exec(response); while (matches) { if (matches.index === regex.lastIndex) { regex.lastIndex++; } resultOfMatch.push(matches[1] || matches[2] || matches[3]) matches = regex.exec(response); } return [...new Set(resultOfMatch)]; } // main source of output, also used for development purposes function logicController(){ var page = {}; var response = args.target.call(); // regex for pub pages var pubPages = search(/\s(\w+)\s\|/g, response); response = args.target.call({}); var navArg = search(/\s(\w+):/g, response); page[navArg] = pubPages[0]; return page; } return logicController(); }
this will hopefully return the whole argument and value string for the news page...
in this case, it worked, but for you there is a chance it wont. that is because the pages have a possibility of being corrupt, and not being caught by the regex. running a few times can fix this, but in a later section, we can fix this bug.
continuing the script
continuing on, we can call the news page by adding this to our code:
response = args.target.call(page);

running it like this(assuming that there wasnt corruption), the news page will open...










success! your script should now look like this:
function(context, args){ // target:#s.t1npc.corp // checks if the user didnt enter args, or if they didnt enter a target if (!args||!args.target){ return {ok:false, msg:"usage: YOURUSER.test {target:#s.t1npc.corp}"} } // searches a page (response) looking for regex matches, and storing them in an array function search(regex, response) { let resultOfMatch = []; let matches = regex.exec(response); while (matches) { if (matches.index === regex.lastIndex) { regex.lastIndex++; } resultOfMatch.push(matches[1] || matches[2] || matches[3]) matches = regex.exec(response); } return [...new Set(resultOfMatch)]; } // main source of output, also used for development purposes function logicController(){ var page = {}; var response = args.target.call(); // regex for pub pages var pubPages = search(/\s(\w+)\s\|/g, response); // get the navigation arg response = args.target.call({}); var navArg = search(/\s(\w+):/g, response); // add the nav arg to the public pages to get the full arg:value string page[navArg] = pubPages[0]; // gets the wall of text on the news page response = args.target.call(page); return response; } return logicController(); }
the next part is to use a few regexes to get the usernames, projects, and the password for the projects area.

to get the usernames, we can use this regex:
/([A-Za-z0-9_-]+)\sof\sproject|-{2}\s(\w+)\s/g

to get the projects, we can use this regex:
/continues\son\s([A-Za-z0-9_()]+)|on\s([A-Za-z0-9_()]+)\sprogress/g

the next step would be to get the password. we can do this by going to the second page, and using a regex. the code for this is:
page[navArg] = pubPages[1]; response = args.target.call(page); let password = search(/\sstrategy\s(\w+)\s/g, response);
we then have to display all of this to the screen. to do this, we can add this code:
return [["usernames", usernames], ["", projects], ["", password]];
your code should now look like this:
function(context, args){ // target:#s.t1npc.corp // checks if the user didnt enter args, or if they didnt enter a target if (!args||!args.target){ return {ok:false, msg:"usage: YOURUSER.test {target:#s.t1npc.corp}"} } // searches a page (response) looking for regex matches, and storing them in an array function search(regex, response) { let resultOfMatch = []; let matches = regex.exec(response); while (matches) { if (matches.index === regex.lastIndex) { regex.lastIndex++; } resultOfMatch.push(matches[1] || matches[2] || matches[3]) matches = regex.exec(response); } return [...new Set(resultOfMatch)]; } // main source of output, also used for development purposes function logicController(){ var page = {}; var response = args.target.call(); // regex for pub pages var pubPages = search(/\s(\w+)\s\|/g, response); // get the navigation arg response = args.target.call({}); var navArg = search(/\s(\w+):/g, response); // add the nav arg to the public pages to get the full arg:value string page[navArg] = pubPages[0]; // gets the wall of text on the news page response = args.target.call(page); // search the page for relevant info var usernames = search(/([A-Za-z0-9_-]+)\sof\sproject|-{2}\s(\w+)\s/g, response); var projects = search(/continues\son\s([A-Za-z0-9_()]+)|on\s([A-Za-z0-9_()]+)\sprogress/g, response); // searches for the password page[navArg] = pubPages[1]; response = args.target.call(page); let password = search(/\sstrategy\s(\w+)\s/g, response); // displays all of the info return [["usernames", usernames], ["projects", projects], ["password", password]]; return response; } return logicController(); }
running it...
success... except its difficult to tell the word "usernames" from the actual usernames.
text color will be covered in the next section!
color
color in HackMud is done by wrapping a string with tacks, or `, which is located above the tab key on an american keyboard. right after the first `, you would add the char which corrisponds to the color you want.

a complete list of colors is located here[hackmud.wikia.com].

one of my favorite colors is orange, so i will make my headers colored with orange, in this case, the char for it is 5, so i will add it to the printout, which looks like this:
return [["`5usernames`", usernames], ["`5projects`", projects], ["`5password`", password]];

doing this makes our output look like this:
deleting and renaming
you have finished your (presumably) first script, which searches a t1 corp, and gives you the relevant information...but what if you dont like the name?

you can delete a uploaded script from the game's server by typing:
#up test delete
just replace the word test with whatever your script is currently called.

you can then rename a script by first renaming the file, then uploading it with the new name.
endnote
congrats, you have (actually) finished a script! you may use it at your leisure, and use your new knowledge of js in order to continue on from here.

whats next? maybe adding a part to your script which gets the locs from all the projects? either way, thanks for using this guide, a like and favorite is appreciated!
8 Comments
Hekate Sketch Jul 5, 2024 @ 4:13pm 
@Kingslayer maybe not, but getting char_space_v1s so far has been quite common, so that hasn't been an issue for myself. Only been playing for like, three ish days? I've got two of em equipped at the moment, instead of a w4rn_message, for the extra 500 space.
Reaper One Sep 20, 2018 @ 3:25am 
Awesome, thank you very much :D Now i understand what happens in there.
Pete Parson's car collection  [author] Sep 19, 2018 @ 8:05pm 
I am sorry for my late response. The regex loop begins by checking if there are matches in the page, from the regex search. if that returns true, then the process of filling the resultOfMatch variable with the resulting matches. The exec method returns its own array, based off of the specified regex string. When the programmer doesn't specify an index value, its value defaults to 0. as the loop progresses, this value is increased by 1 (0, 1, 2, ect.). The push functions pushes (appends, adds to)the specified value to the end of an array, which is also specified.
I appreciate the feedback by the way, its great to see that there are more questions about javascript which can still be answered.
Reaper One Sep 19, 2018 @ 2:46pm 
Yup, I am having a hard time figuring out what the while loop running regex search does and why it works that way and struggling to find good resources explaining that in an easy to understand way.

Otherwise it's an absolutely brilliant guide you wrote. Very beginner friendly, thank you very much for the effort you put into this.
Alex Jul 19, 2018 @ 10:28am 
Awesome!
Kingslayer Jan 29, 2018 @ 7:59am 
Several concerns :
1) It's hardly transparent on how Search function exactly works,
2) this scipt is more then 1000 chatacters long. Not possible to use by fresh blood off VLan.
Pete Parson's car collection  [author] Nov 14, 2017 @ 3:19pm 
thanks :)
Grant Nov 13, 2017 @ 9:23pm 
gr8 guide, 10/10