Debugging (Find The Bug)

From Scriptwiki
Jump to: navigation, search

Why am I Here?

If you've come wandering upon this document it is likely that you are having trouble with a script that you wrote. It is probably fully written, but doesn't seem to work as it should, for one reason or another. The purpose of this document is therefore to teach you how to debug your own scripts, making the coding process easier and more efficient. Don't get discouraged, debugging is not hard. Debugging is a logical process that can be easily learned and applied if you follow the steps involved.

If you happen to already know enough about debugging, but just can't seem to figure out what is going wrong with a script you wrote, you should take a look at section 5, in which some Common Scripting Pitfalls are discussed.

The Basics of Debugging

If a script does not work, the logical process is to find out why, so that it may be fixed. DO NOT take for granted the first step I mentioned, in which you must find out why your script does not work. When first dealing with any bugged script, your frame of mind should never be, "this needs to be fixed". Instead you should first be asking the question "why does this not work?" Once you answer that question you can move on to fixing it and the fix is usually much simpler than the debugging process itself. Think of debugging similar to a medical situation. Before you treat a patient, you must always diagnose him or her first. This concept applies the same way in debugging.

A lot of people asking for help on IRC or in forums are basically just asking for other people to debug their scripts, instead of helping them with something the person asking doesn't understand. Don't expect much respect if you ask people to bugfix for you.

The Steps of Debugging

Practically speaking, to diagnose a bugged script you should follow the set of steps which I will now begin explaining.

Narrow it Down!

"Narrow it down" is probably one of the best phrases to keep in mind when debugging a script. Often a scripter will ask for help with a script that doesn't seem to trigger, but he will paste his entire script, which happens to be 100 or so lines of code. The truth is that it is rarely necessary to look at more than 5-10 lines of code at a time. While debugging you should constantly be narrowing down which lines of code the bug is affecting, or which lines of code simply do not work.

But don't stop there; a line of code can easily become too general still. Continue to narrow down your search as much as possible. Sometimes it will be necessary to narrow things down to the smallest character (this has definitely been the source of many bugs I've seen in the past). Remember that sometimes a bug can be as simple as a typo or spelling mistake.

The question you are probably asking is how exactly you "narrow down" a search for a bug? "What should I be looking for?" There are many things to look for; here are some methods you can use to find out which part of your script is not functioning properly:

Look for Error Messages

Sometimes if mIRC breaks a script errantly it will echo the error information locally. This is usually found in the status window of your active connection id (unless your command explicitly used another connection id). If you get such an error it might inform you of the line it halted execution on. This is very useful to narrow your bug search to a certain line. If no line is mentioned you will most likely get the command, which is also very helpful. You can then go look up each time you used that command in your script.

Be Verbose

Verbose means to be audible. In the context of mIRC scripting it means to /echo data to the screen. Usually a debugger should try to echo as much information as often as possible.

If you have a variable that does not have the right value, it is best to echo that variable right after you set it to find out what value it took on. This can easily narrow your code down to a line in one simple step.

Another possibility is that a line of code isn't being executed at all (how do I know this, you ask? Well if it's a command such as /kick it is very easy to tell if it executes or not, however if it is a silent command, try tacking on a /echo inside the command itself to see if it executes). In such a case it is a good idea to drop in /echo commands along your code flow, regardless of what it actually echoes.

Consider the following example:

alias function_name {
    echo -a Started execution
    if (a == a) {
        echo -a Got to first level if
        if (b == b) {
            ; mIRC should get here, 
            ; but lets find out
            echo -a Got to second level
            ; Imagine this next if is a typo
            if (defcon == defdcon) {
                echo -a Got to third level
    echo -a Ending function

Started execution
Got to first level if
Got to second level
Ending function

From this it should be clear that mIRC did not reach the last nested if-statement. You can now do further tests to find out why your if statement failed. In our case it was a typo, we really meant to say 'defcon' instead of 'defdcon'.

Another way to use echo is echo'ing big or complicated identifiers, or even entire commands, to see if they actually parse into what you want them to be.

Check Brackets and Syntax

This may seem trivial, but it is always important to make sure your script has as many open brackets as close brackets and also follow mIRC's basic syntax rules. I will outline some basic syntax rules that mIRC uses:

Basic Syntax Rules

  • Braces {} must always be spaced out and never touch any other character. There is one exception to this rule, but if you always space your braces out you should not have a problem. This also applies to the pipe | character used for dealing with multiple commands.
  • Multiple commands should generally be surrounded by braces. It is not necessary to use a new line for each command, but keep in mind the multiple command structure:

On one line:

ALIAS/EVENT { command 1 | command 2 | command 3 }

On multiple lines:

    command 1
    command 2
    command 3

On both multiple and single lines:

    command 1 | command 2
    command 3
    command 4

Note that the opening brace can never be on its own on a line. Consider the following:


Some languages allow this syntax. This is not valid in mIRC. The opening brace can never begin a line.

  • The If control structure is command based in mIRC. As such, it must be properly spaced just like all commands. Some languages allow the use of:


This is also not valid in mIRC. In such a case, you would actually be performing the command of /if(condition), which most likely does not exist. The condition (including the brackets) in mIRC if statements is a parameter, and must follow the if statement with a space.

As far as braces go (the { } characters), mIRC can check this automatically in the script editor by clicking the "{ }" button. If mIRC says you have a mismatch, fix it before going further; it is likely that this is the cause of your bug.

When dealing with if statements it is always important to make sure you have the same amount of opening brackets as closing brackets. Sometimes mIRC will not report this error and will automatically fail the if statement and continue execution. If you read section 3.3 you may have used the echo method to find out which if statement is not being triggers. In such a case, the first thing you should do is count the brackets and look at general syntax as described above.

Simplify Complicated and Suspicious Code

If you were unsuccessful in tracking down your bug using any of the other methods mentioned above, try looking for suspicious code. If you wrote a line of code that you just aren't sure about, try looking closer at that line. Similarly, if you have a very complex line of code in your script, try looking at that one first. A good way to handle a situation like this is to change the line to something very simple, and see if it works. If it does then you know that your problem lies in the code that you changed. If not then you can be certain that the bug you're looking for does not lie in your complex code. (Although be aware that you shouldn't now automatically trust it completely, as there may still be issues with it which won't be apparent until the current bug is fixed, for example.)

Final Notes

Remember that when you're debugging a script it is more important to find out why your script is failing than to fix it. An mIRC support channel can easily aid a scripter solve his problem when it is clear what the problem is. In essence, if you cannot solve a problem it is important that you at least understand what problem it is that you're having, that way you can get the help you need from an outside source. That is the purpose of debugging a script; not to directly fix, but to set yourself on a course to implement such a fix.

Common Scripting Pitfalls

Conflicting Event Matching

A Scripter will often put the following line of code in his Remotes and it will not trigger:

on 1:TEXT:!trigger *:#: { commands }

There is nothing wrong with this code. It should trigger fine. However in his script (higher up in the file) he also has the following line:

on 1:TEXT:*:#: { commands }

The reason his !trigger is not working is that mIRC goes downwards through a script file finding the first event that matches. Since '*' is more global than '!trigger *', mIRC will not reach his !trigger event. To fix this the scripter must either reverse the order which they appear in the file, or put the !trigger in a new script file, so that mIRC will execute both.

  • Note: this also occurs for any other events that match for the same input

Using " " In Filenames

Always use " " when dealing with files and directories, to make sure mIRC understands directories with spaces in the name. It's a good habit to always use them, even when you aren't expecting any spaces.

/rename c:\Program Files\Half-life\hl.exe c:\Program Files\Half-Life\hl2.exe

This won't work.

/rename "c:\Program Files\Half-life\hl.exe" "c:\Program Files\Half-Life\hl2.exe"

This will.

'=' Is Not '=='

Remember to always use == when you are comparing objects in if's or while loops. This counts for every programming language. == is for comparing, = is for assigning.

alias istest {
    if (100 == 100) {
        var %pie = 8

Remember That Some Commands Have Delays

Not all commands are completed the moment you start them. Some command require you to wait. this pretty much counts for every command that requires a reply from the server or from other clients or connections. For example, this won't work:

on *:DEOP:#:{
    if ($opnick == $me) {
        ;check if we are being deopped
        msg BOTNICK op $chan
        ;requesting op
        kick $chan $nick Don't deop me!
        ;kick the person that deopped you

It won't work because when you ask for op status, there is a delay between the time that you ask it and the time that you actually get it. Even if it's just a few milliseconds, the script goes too fast for it. There are two ways to solve it; either put the kick command in a timer or use a separate on op event for the kick.

Using $read With The s Flag

When you use $read with the s flag to scan for a line starting with a certain word, keep in mind that the result will NOT include the word you were searching for itself! It returns everything after the word you scanned with.

So if you had a textfile with a line that said "ParaSite` is the greatest" and you used $read(textfile.txt,s,ParaSite`), it would return "is the greatest".