|LIQUID_NiTrO - Wed Mar 01, 2006 10:27 pm
|In my opinion, there are quite a few things the mIRC tutorial does a poor job of explaining, but very few of which are worthy of their own separate tutorial. So I decided to create this one just to get them all out of the way.
This tutorial is mainly for new scripters, but they should have basic knowledge of how to create remotes and aliases. A few more advanced examples use while loops as well, but you should be able to get by somewhat well without knowing them, and there's a small section towards the end that will give you a rudimentary knowledge of them if you don't already have it.
todo: bitwise comparisons, $base
Tokens are a really useful thing that is not very well explained in the mIRC help file, and a lot of scripts I see people asking for help with on here could be much improved with their use. So here we go.
A 'token' is basically just a chunk of text that comes out of a 'string' (a string is just one or more characters). What defines where one token ends and the next one begins is up to you, however, in order to define it you will need to know the ASCII number of the character you want to be your token separator. To get this number just type this in mIRC:
//echo * $asc($?)
Then enter the character you want in the box that pops up. The most common numbers are 44, which is a comma, and 32, which is a space.
Alright, so now that we understand what tokens are, let's take a look at the uses of tokens. Our token identifiers are $gettok, $findtok, $wildtok, $addtok, $remtok, $deltok, $numtok, $istok, $instok, $matchtok, $puttok, and $sorttok. Yes, that's a lot, but most of them are rarely used so not to worry. The mIRC help file does an alright job of explaining their individual uses so I'm not going to go over them.
The main use of tokens is to have more data than normal in a variable or hash value, in order to keep things as clean as possible. For instance, instead of:
We could use tokens to make this only one variable, and just decide that the first token will represent name, the second will represent age, and the third IQ. We would then use $gettok to get the values for each individual item.
We can also allow for easier input this way by the use of $addtok. To output them we can use a combination of $numtok and $gettok.
Getting even more advanced, we can have a complex double-token string, so it's actually possible to store, for instance, multiple account name/password combinations in a single variable. We need two different token separators for this one, we'll use the comma and colon (a colon is an ASCII 58)
Notice in this example we had to use two stacked occurences of $gettok. This is because our variable might look something like this:
so the innermost call to $gettok, which is $gettok(%accounts,%x,44), will return "bob:bobspass" on the first iteration (%x=1). We then use the outer call to $gettok to grab either "bob" ($gettok(..,1,58)) or "bobspass" ($gettok(..,2,58))
If I could have the mIRC help file explain one thing better, it would probably be hash tables. Hash tables are the most efficient method of data storage, hands down; the only reasons I ever use anything but a hash table are for quick, temporary storage or sometimes to increase user-friendliness I'll use a txt file.
Why are hash tables better, you ask? Well, for one thing, when you load a hash table into mIRC it is stored into RAM, as opposed to all other types of data storage (except local variables I believe) which are stored on the hard drive. If you know anything about hardware, you know that your computer can access RAM much quicker than it can the hard drive, thus hash tables are quicker than anything else. They also use a method called "hashing" to find your requested value, whereas all other types offered in mIRC use "linear searching" instead. I won't go into detail about this, but suffice it to say that hashing again at least doubles linear searching in speed and efficiency.
Hash tables really aren't very complicated at all. You first must create a hash table with the /hmake command (or /hadd -m, but if you can it is generally better to use /hmake), then you create individual items in the hash table with /hadd. Let's take a look at the syntax for our hash commands and identifiers to see how to do this:
/hmake <name> [slots] -- Creates the specified hash table. "name" can be anything. Slots is the number of blank items it will create along with the table. I don't understand this exactly, but you should try to make it roughly the number of items you expect to store in the table. So if you think your table will end up holding 30 items, make it 30. The slots parameter is optional.
/hadd [-muN] <table> <item> [data] -- Adds the specified data to a hash table under the specified item name. If the item name supplied already exists, it overwrites the old data with the more recent data (so using the command without specifying any data clears the item's data (but does not delete the item)). The -m switch tells it to create the hash table if it doesn't exist (if you don't specify -m and the table doesn't exist, you'll get an error). The -uN switch will cause the item to be unset after N seconds. There's a few other more advanced switches, /help hash tables to take a look for yourself.
/hdel [-w] <table> <item> -- Deletes specified item from table. The -w switch indicates that "item" is a wildcard and will delete all matching items.
/hfree [-w] <table> -- "Frees" specified hash table, that is, deletes it from your RAM. Again the -w switch indicates that "table" is a wildcard and will free all matching tables. If you have not use the /hsave command prior to this command, you will permanently lose all data in the hash table.
/hload <name> <filename> -- Loads a hash table into RAM from the hard drive. Used in combination with /hsave to retain data stored in hash tables between mIRC sessions.
/hsave [-oa] <name> <filename> -- Saves a hash table to the hard drive. If the specified filename already exists, the -o switch can be used to overwrite it or the -a switch can be used to append to it (thus I would always use one or the other switch in this command). If neither -o nor -a was specified, an error occurs.
$hget(name|N) -- Returns the name of the specified hash table, or the name of the Nth hash table. Usually used to check for a hash table's existence. With the .size property it will return the hash table's size.
$hget(name|N,item) -- Returns the data for the specified item in the specified hash table. With the .unset property it will return the remaining time before the specified item is unset (the item must have been set with the -uN switch to use this property)
$hget(name|N,N).item -- Returns the Nth item name in the table. If N is zero, returns the total number of items in the table. You should avoid using this method if possible because it is slower than the other methods of accessing the table.
$hget(name|N,N).data -- The same as .item, but returns the data associated with the item.
An important thing to note is that unlike any other type of data storage, hash tables are completely cleared from memory on exiting mIRC or using the /hfree command. If you need to keep the data you have stored in hash tables after you have exited mIRC, you MUST use a combination of the /hsave and /hload commands. 99% of the time you'll put these commands in on START and on EXIT events, like so:
This code will load the hash table MyHashTable if it exists every time mIRC starts, and if it does not exist it will create it with 100 empty slots. When mIRC exits it will save the data in MyHashTable to a file in the mIRC directory called MyHashTable.dat.
--PRACTICAL EXAMPLES COMING SOON--
For now, a good example of hash tables is my "Naked Auth System" snippet. It's somewhat complicated, but it incorporates both tokens and hash tables so it's a good review of what we've learned so far.
Raw events are somewhat advanced- they change mIRC's response to a server event. This section will be fairly brief simply because this is supposed to be an introductory-level tutorial, and chances are you won't care much about raw events until you're a pretty advanced scripter. However I do think they deserve explaining, so here we go.
In order to have a raw event, we need to know the numeric for it. A numeric is just a number designated in IRC protocol (RFC1459) that means a certain thing to the client. For example, when the client receives a message starting with 311, it knows that this is the first line of a WHOIS reply (because RFC1459 says so).
Getting the numeric you need is simple; just type debug -p @debug, then do whatever you need to in order to get the server to send you the type of message you want the numeric for, and look in the @debug window for it after you've done that. Ex. if you wanted to find the numeric for the first line of a WHOIS reply (311), you would do something like this:
So you can see here that the numeric is 311.
After you got this, raw events are really easy and they're not much different from any others. The format is like so:
For some reason, the mIRC Programming FAQ sort of attempts to scare you off from experimenting with raw events- really, there's nothing to be afraid of. It's true if you mess up you can get some weird things to happen, and if you really mess up you can get mIRC to not function properly, however I can assure you any problems you accidentally cause will be totally reversible by simply removing the code that caused the problem.
While it is true that mIRC is a powerful enough program to seriously mess with your computer, wipe your hard drive, create virii, and other undesirable things, none of these things will happen to you because you messed up a script. Trust me, it requires deliberate action and a good bit of scripting expertise in order to achieve any of these effects.
There's already another good tutorial over this, however it seemed appropriate to include it, so include it I will, but again this will be another brief section and if you don't understand I recommend you look at the tutorial in this forum specifically over while loops.
A while loop is basically equivilant to a label followed by an if case that contains a goto command for the label preceeding it. I know that just lost you so let me give an example (don't put this in mIRC or it will hang; it's just to show you what I mean):
Both of these aliases are exactly the same in function.
You can pretty easily tell that in the above examples, the condition will always be true, thus you'll get stuck in an infinite loop, causing mIRC to hang. Obviously we need something to make the loops eventually evaluate to false. 90% of the time this something will be a variable that you either increment or decrement with each iteration (an iteration refers to one execution of the commands within the while loop), like so:
This code will echo all of your variables to the screen on execution. The following snippet does the same thing and is slightly more efficient because it calls less identifiers with each iteration. It uses a decrement instead of an increment.
Alright, so you've probably heard somewhere or figured out that the number system we are accustomed to is known as the decimal system, or base-ten system. You know it is base ten because the lowest two-digit number is ten (that's not the real reason, but hey it works for our purposes), and each place value represents a power of ten.
So when we have the number 52395, we are actually working right to left, increasing the power that we raise ten to each time and then multiplying it by the number we're on like so:
So then, when we switch from base 10 to say, base 2 (binary), each digit now represents a power of two. So 101101 is evaluated like this:
But what about bases greater than 10, you ask? Well, since we don't have single digits to represent values greater than ten, we start using letters. A letters numeric value can be determined by taking its position in the alphabet and adding 9 to it. So the letter A is in the 1st position of the alphabet, and 1+9=10, so the value of A is ten. Q is in the 17th position in the alphabet, and 17+9=26, so the value of Q is 26.
So now, looking at $base, we see that the syntax is $base(number,inbase,outbase,zeropad,precision). Number is, obviously, the number you're going to convert, inbase is the base that that number is currently in, outbase is the base that will be returned, zeropad is the minimum number of digits (if it's got fewer digits than that zeroes will be added to the start to make it fit), and I'm not really sure what precision is...if anyone knows feel free to tell me.
Local vs Global variables
The help file does a so-so job of covering the differences between local and global variables, but this is something many people need to see, I believe, and it could be explained better.
Alright, so you probably already know what a global variable is, at least. They are created using the /set command and, unless put on a timer using the -u switch, must be explicitly unset using the /unset command. When a script that has created a global variable terminates, any global variables created by the script are added to the user's variables file, where they stay until they are unset. It is bad coding practice to leave a global variable in a user's variables file when it is not absolutely necessary.
Local variables are what should be used about eighty to ninety percent of the time. They exist only for the duration of the script, and are automatically unset at the script's termination. They are created using the /var command (which has slightly different syntax from /set; you must put an equals sign between the variable name and the value when using the /var command, ex var %unhappy = sad) or the /set -l command, and may be unset in the same fashion as global variables, though this is rarely necessary.
Really the only reason I know of to use a global variable is if data needs to temporarily be stored between script events. This usually happens when one event, such as an alias or an on TEXT, gives a command (usually a command to retrieve info from a sever, such as /names, /whois, etc), and then must wait for a reply from that command before outputting its final information. Since two seperate events will be used, global variables may be necessary to store the input information from the first event.
For example, if you wanted to implement a !userhost command that other people could type in a channel window in order to get you to output requested /userhost information to them in a PM, you might need a global variable to store the person who called the !userhost command's nickname. This is because the on TEXT event will completely terminate before you receive the userhost reply, thus you need a way for your scrip to "remember" the name of the person who gave the !userhost command, in order to send a PM back to them.
Other than for purposes like that (which will usually involve a raw event), local variables will do the trick 99% of the time.
This is the code for an alias, /aretheycool <nick> <window> which displays whether a person is cool or not (according to the script, you must have a letter z in your real name in order to be cool). The script does this by using a /whois command to look up the nickname, then waits for a reply from the server (on numeric 311) before outputting the answer. So in this script, two completely separate events (the alias and raw 311) will work together, therefore a global variable is necessary to store information between when the alias code terminates and the raw event receives the message from the server (in this case the information we are storing is what window to echo the result to).
Notice that when %win is set, I used the -u3 switch to make it automatically unset after 3 seconds. This way, if the client is lagged or not connected to a server at all, the variable will not remain uselessly in the variables file. 3 seconds is ample time for a server to respond to a whois query.
---THIS TUTORIAL IS A WORK IN PROGRESS---
I'll be adding more things here as I think of them. Hope you found this helpful! Any and all suggestions welcome.
Last edited by LIQUID_NiTrO on Mon Feb 23, 2009 2:04 pm; edited 9 times in total