mIRC ScriptBox
The Art of Debugging

Written by Pasmal

Table of Content

  1. Prerequisites
  2. Our First Debug
  3. Our Second Debug
  4. Our Last Debug
  5. Freedom at last!


Introduction

Debugging is an extremely important part to scripting. It is the method of which you find and track down errors (bugs) in your code. To many, at first, debugging seems like a tedious subject. Well I'm afraid it is. Yes its time consuming, yes its a hassle, yes it can get frustrating.

So why do it? The first and foremost reason to do it is to get the darn thing working. You should also find after using debugging methods for a while you will have a better understanding on how scripts work. You will learn from your mistakes, avoid future errors from lessons learnt in the past. The good news from this is: Less errors mean less time spent debugging!


1. Prerequistes  Back to Top

To debug you only need three things:
  mIRC
  Your Script
  Your Brain
Once you have them (It should be no biggy), the first thing to do is go over the basic tips outlined in the Troubleshooting Guide page.

Now we can begin. The three most common problems found when debugging:

  The script is not evaluating correctly
  The loop is not working as it should
  The if statement is not working as it should


2. Our First Debug  Back to Top

To debug you need to go through your script LINE by LINE by LINE. It helps if you have your script spread out over many lines instead of using pipes (the "|") to seperate commands. What you are trying to do by going through your script line by line is to check where the script stops working. To do this, place an ECHO line before each original line in the script. Run the script, watch what ECHO does not appear. Goto that part of the script where the ECHO is, and check that particular line of the script.

Another way (quicker, not as complete) to find out errors, especially in loops, is to see what your variables are being set to. Now you could do this with an ECHO command, but I prefer to use the -s switch in the set command. The -s switch should echo what that variable was set to (If it does not echo, then you know that the script never made it to the set command).

Script To Debug:

on *:TEXT:#:*hello*: msg $chan Hello yourself!

Now in the above script there is only one line, so we only need to do one echo. What I would do is place echo -a infront of the msg command:

on *:TEXT:#:*hello*: echo -a msg $chan Hello yourself!

Try type hello, or get someone else to type hello, from a different mIRC from the one that you placed that remote in. You should find that there is no echo what so ever. This means that the event isn't even reacting! We know this because the echo is the first command in the event, so it should always be displayed. Because the event isn't reacting we need to look at the events format - to make sure everything is correct. My advise is to type /help on EVENT - in this case /help on TEXT. Hmm the help file gives the on TEXT format as:

on <level>:TEXT:<matchtext>:<*><?><#[,#]>:<commands>

Now we need to look back to our on TEXT's format. Whats different?

Doh it looks like we messed up the order for :<matchtext>:<*><?><#,[,#]>: - we had it the other way around so the channel came first then the text. Lets try it now with the correct format:

on *:TEXT:*hello*:#: msg $chan Hello yourself!

Yay it works :) Now onto a harder example!


3. Our Second Debug  Back to Top

Lets try something a bit harder - there are three problems in this one. Crud:
on *:TOPIC: {
SET %cur.topic. [ $+ [ $chan ] ] $topic
SET %old.topic. [ $+ [ $chan ] ] %cur.topic. [ $+ [ $chan ] ]
echo $colour(topic) $chan $timestamp [TOPIC Change] $nick changed topic from %old.topic. [ $+ [ $chan ] ] to %cur.topic. [ $+ [ $chan ] ]
HALTDEF
}

OK the idea behind this script is when anyone changes the topic, it will store the old topic to %old.topic.#channel, and will store the current topic to %cur.topic.#channnel. It should then echo in that channel that the topic was changed, and stop the usual topic message from displaying.

Some people may have problems copy pasting this code due to the length of the echo line. If your copy's text is word wrapped, the 4th line ends at the last set of ] ]. The 5th line starts at haltdef.

Lets test it:
Change the topic in a channel. Does anything happen? Alas nothing happens. Since we have a set command, why don't we use the set -s switch to see if that variable is actually being set. Change:

set %current.topic. [ $+ [ chan ] ] $topic

to

set -s %current.topic. [ $+ [ $chan ] ] $topic

Run the script again. Do you get something like *** Set %current.topic.#somechannel to Some Topic?

No still? This means, like in the first debug, that our format for the event is wrong. Lets look up /help on TOPIC. Its format is:

on <level>:TOPIC:<#[,#]>:<commands>

Now look at our script again. Is there anything wrong with our event's format? Yeup - the <#[,#]> section is missing. Guess we had better fix it:


on *:TOPIC:#: 
{
SET -s %cur.topic. [ $+ [ $chan ] ] $topic
SET %old.topic. [ $+ [ $chan ] ] %cur.topic. [ $+ [ $chan ] ]
echo $colour(topic) $chan $timestamp [TOPIC Change] $nick changed topic from %old.topic.[ $+ [ $chan ] ] to %cur.topic. [ $+ [ $chan ] ]
HALTDEF
}

Now try change the topic again, this time with the updated code in your remotes. Yay it worked - almost just as we wanted it.
There are still two problems:

a) The script did not display what the topic was set to, nor did it show the old topic.
b) The script did not stop the default topic message from being displayed.

Look in your status window. You should see something like:

*** Set %cur.topic.#somechannel to ""

That variable should be set to the current topic. There must be a problem with what we are using to represent the topic text. Lets look up /help on TOPIC again and see if it has any more help. Read the last line:

The $1- parameters hold the actual text of the new topic.

What have we been using? $topic. Oops thats wrong, we really should have been using $1-. Lets change the code:

on *:TOPIC:#: 
{
SET -s %cur.topic. [ $+ [ $chan ] ] $1-
SET %old.topic. [ $+ [ $chan ] ] %cur.topic. [ $+ [ $chan ] ]
echo $colour(topic) $chan $timestamp [TOPIC Change] $nick changed topic from %old.topic.[ $+ [ $chan ] ] to %cur.topic. [ $+ [ $chan ] ]
HALTDEF
}

Retest it. Good, %cur.topic. [ $+ [ $chan ] ] is now setting the correct topic. But oddly, the echo thinks the old topic is also the current topic. Weird.

Lets look at the line we use to set the old topic:

SET %old.topic. [ $+ [ $chan ] ] %cur.topic. [ $+ [ $chan ] ]

Well that looks ok - it should get the value from the previous current topic and store it as the old topic. Ah but look - we change the current topic variable before we change the old topic variable. Maybe if we flipped the two around it would work:


on *:TOPIC:#: 
{
SET %old.topic. [ $+ [ $chan ] ] %cur.topic. [ $+ [ $chan ] ]
SET -s %cur.topic. [ $+ [ $chan ] ] $1-
echo $colour(topic) $chan $timestamp [TOPIC Change] $nick changed topic from %old.topic.[ $+ [ $chan ] ] to %cur.topic. [ $+ [ $chan ] ]
HALTDEF
}

Once again change the topic. Great, it remembered which was the old topic and which was the new. I guess we can remove the -s from the set command. No need to annoy users with little debug messages like that :) One problem left: The default topic message is still being displayed. We have the haltdef command - this should stop the default message from displaying. We must have missed something. Lets look up the haltdef command: /help /haltdef.

The ^ event prefix

I knew I forgot something! Well at least it stood out on the page so we didn't have to look too far. It looks like we have to have this ^ in order to make haltdef work. We need to change the code again:


on ^*:TOPIC:#: 
{
SET %old.topic. [ $+ [ $chan ] ] %cur.topic. [ $+ [ $chan ] ]
SET %cur.topic.[ $+ [ $chan ] ] $1-
echo $colour(topic) $chan $timestamp [TOPIC Change] $nick changed the topic from %old.topic.[ $+ [ $chan ] ] to %cur.topic. [ $+ [ $chan ] ]
HALTDEF
}
Woohoo it works! Ready for more?


4. Our Last Debug  Back to Top

This is the final script to debug in this document. It has a several errors, and involves loops.

Example:

alias colour.ops {
  SET %looptimes $opnick($chan,0)
  :loop
  SET %loopedtimes $calc(%looptimes - 1)
  cline 12 $chan $opnick($chan,%loopedtimes)
  IF (%loopedtimes  > 1) { GOTO loop }
}

Now if you don't know already, this script is ment to colour each op in the active channel using the cline command. Run the script yourself.
Note: If it locks up, press CTRL and BREAK until it stops.

The first thing you will observe is that it only colours ONE nickname. This is our first indicator of what the problem is: The loop is not going through all the nicknames.

To find out why, we can use the echo method or the set method. Seeing as we know that the problem is with the loop (Reminder: The script only coloured one op, not all the ops), I think we can concentrate on the loop part itself. So, lets rewrite the alias:

alias colour.ops {
  SET -s  %looptimes $opnick($chan,0)
  :loop 
  SET -s %loopedtimes $calc(%looptimes - 1)
  echo -a cline 12 $chan $opnick($chan,%loopedtimes)
  cline 12 $chan $opnick($chan,%loopedtimes)
  echo -a if %loopedtimes > 1 goto loop
  IF (%loopedtimes > 1) { GOTO loop }
}

Note: you will still need to press CTRL and BREAK to stop it. Run the alias. Something similar to the below should appear:


	*** Set %looptimes to 7
	*** Set %loopedtimes to 6
	cline 12 #hdtest zazBit
	if 6 > 1 goto loop
	*** Set %loopedtimes to 6
	cline 12 #hdtest zazBit
	if 6 > 1 goto loop
	*** Set %loopedtimes to 6
	* Break: command halted! (line 5, debug.mrc)

Another Note: zazBit was the last op in #HDTest, and there were seven(7) ops in total in #HDtest.

Now what have we learned from this test?
First, it seems that the script is setting the correct amount of times to loop (%looptimes), but its not correctly decreasing %loopedtimes before it colours a nickname. So we need to look at why this variable is not decreasing once every loop. The code that we thought was ment to decrease it was:

SET -s %loopedtimes $calc(%looptimes - 1)

We need to look at this line closely. To some the error will be aparent. We should be decreasing %loopedtimes value by 1, NOT %looptimes value.
Silly spelling mistake :) Lets correct it:

SET -s  %loopedtimes $calc(%loopedtimes - 1)
or better yet:
DEC -s %loopedtimes
Change the script.
Reset %looptimes and %loopedtimes (type /unset %loopedtimes %looptimes).

Part then rejoin the channel (To clear the nicklist colouring) and retry the script.
Oh dear, seems like the script still dosn't want to work. You should get something like:

*** SET %looptimes to 7
*** DEX %loopedtimes to -1
cline 12 #hdtest
* /cline: insufficient parameters (line 6, debug.mrc)

From that output we can clearly see that the script dosn't know who to cline (note the cline line lacks a nickname).

Why is this? Lets look at the line in question (in my case line 6 of debug.mrc):

cline 12 $chan $opnick($chan,%loopedtimes)

Now look at the part that is ment to be the nickname. Why? Because its the part thats 'missing' when we run the script. Its $opnick($chan,%loopedtimes). Now look to what %loopedtimes was set to. It was set to -1. So naturally $opnick(#,-1) won't work as there is no -1 op. It looks like the problem is once again in %loopedtimes. It seems we must set it to the total number of ops in the channel.

Part then rejoin the channel (To clear the nicklist colouring) and retry the script:

alias colour.ops {
SET -s %looptimes $opnick($chan,0)
SET -s %loopedtimes %looptimes
:loop 
DEC -s %loopedtimes
echo -a cline 12 $chan $opnick($chan,%loopedtimes)
cline 12 $chan $opnick($chan,%loopedtimes)
echo -a IF %loopedtimes > 1 { GOTO  loop }
IF (%loopedtimes > 1) { GOTO loop }
}

Grrrr it seems that the last op's colour has not been changed. But at least the alias doesn't lock up :)

Lets look at the debug stuff again:

*** Set %looptimes to 7
*** SET %loopedtimes to 7
*** DEC %loopedtimes to 6 
cline 12 #hdtest zazBit
if 6 > 1 goto loop
*** DEC %loopedtimes to 5 
cline 12 #hdtest PsiBorg 
IF 5 > 1 goto loop
*** Dec %loopedtimes to 4

Note: I didn't copy all the debug output, only the first part of it.

Looking at that output, it should be very easy to spot why its not doing the last op. Why? Look at the first three lines. It seems that we are decreasing the variable before its value's nickname is coloured. Perhaps we should decrease the variable AFTER we had coloured the nickname?alias colour.ops { SET -s %looptimes $opnick($chan,0) SET -s %loopedtimes %looptimes :loop echo -a cline 12 $chan $opnick($chan,%loopedtimes) cline 12 $chan $opnick($chan,%loopedtimes) ; we have now moved the dec command to AFTER the cline command. DEC -s %loopedtimes echo -a IF %loopedtimes > 1 { GOTO loop } IF (%loopedtimes > 1) { GOTO loop } }

Yay it works! Now you can go ahead and remove the ECHO lines, and remove the -s from the SET and DEC commands.


5. Freedom at last!  Back to Top

Well this is the end of the document. Do remember that debugging isn't the easiest scripting topic - but it can be done. After time it will become an easy process that you don't have to spend much effort thinking about.
Happy debugging!