Jump to content

Common Pitfalls (mIRC for dummies) Ch. 1


Tutorial

Recommended Posts




Common Pitfalls

A Tutorial by myggan





Table of Contents:
* 1. The Art of Debuging
* 2. The Importance of Error Checking
* 3. Spotting Ambiguous Conditions
* 4. Brackets and Braces
* 5. Double Events
* 6. Timers
* 7. Input Events
* 8. Matchtext
* 9. Using $$
* 10. The $address() Bug
* 11. Flood Protection
* 12. Echoing Numerical Strings and Colors
* 13. Not Reading the Helpfile
* 14. Getting Helped


This tutorial will take you through some of the known problems in mIRC, and how to solve them. Most experienced scripters are probably already aware of these, but this tutorial aims at the average newbie who isn't enlightened in these matters. I really hope you will learn something by reading this, and thus becoming a better scripter, or more important, developing your logical skills. If you have any questions regarding this tutorial or anything else related to scripting, check the contact information at the bottom of the page.

Enjoy!

1. The Art of Debuging

_______________________________________________________________________
What is debuging?
How do I debug my code?
Why should I debug?

People new to programming are very ignorant to the importance of debuging, but what exactly is debuging? Let's look up the word 'debug' in a dictionary:


To search for and eliminate malfunctioning elements or errors.Locate and correct errors in a computer program code; "debug this program"






So, basically it means "finding the error". Fair enough. But how?
Consider this ambiguous code:





alias readfile {
var %file = $1
var %line = $2
var %read = $read($1,$2)
echo %read
}

/readfile myfile.doc

Nothing happens when we execute the command above.






Why? We do not know the cause of this unintended behaviour. Yet. Lets debug it:





alias readfile {
var %file = $1
var %line = $2
var %read = $read($1,$2)

echo Value of % $+ file? %file 
echo Does %file exists? $isfile(%file)
echo Does the line exist? $iif(%line <= $lines(%file),yes,no)
echo Did you even provide me with a line number? $iif($2 == $null,no,yes)

echo %read
}






We execute the command once again and get the following output:





Value of %file? myfile.doc[/left][left]
Does myfile.doc exists? $false
[/color]Does the line exist? no
Did you even provide me with a line number? no

Bingo! Lets go through the results. Checking the value of your variables is an essential part of debuging, you need to know where they are set, where they are altered, or if they are set at all. In this example the variable %file was set correctly as we passed the parameter myfile.doc. But the fact is that myfile.doc does not exist! Only this is an error which causes the code to not work as intended, but lets continue. The line does not exist, and that's very logical considering we didn't pass a second parameter, which in this case should be the line number.

That wasn't too hard to understand, right? But to make the code even more verbose, you should unsilence all your silenced commands to access some more information and possible errors. Consider this:

alias readfile {
.fopen handle mir.ini
var %read = $fread(handle)
.fclose handle
echo %read
}

/readfile



Same problem as the previous code, nothing happens when we execute it (this one requires no parameters). Lets debug shall we?

alias readfile {
fopen handle mir.ini
var -s %read = $fread(handle)
fclose handle
echo %read
}



Now the command produces the following output:

* fopen unable to open 'handle' (C:ProgrammIRCmir.ini)
-
* Set %read to 
-
* fclose closed 'handle' (C:ProgrammIRCmir.ini)



Ding-ding-ding! Problem spotted. mir.ini does not exist and can therefor not be opened nor read from. What I did here was to remove the leading dots (.), which silences commands (well some errors can't be silenced). I also added the -s switch to /var which makes it echo the variable and the value it's being assigned to (in this case nothing).

That's it! Debuging isn't hard at all, once you know the actual meaning of the word. So the next time you decide to ask someone for help, make sure you have debuged your code BEFORE you ask, since this may piss some people off. Well this concludes this chapter.





Chapter Two


2. The Importance of Error Checking

_______________________________________________________________________
Making the proper error checkings prevents a code from being used in the wrong way, and tells the program what terms is in play. Let you control the program, and do not let it run freely without any indication on where to stop. See at it like a teenager. If you do not set limits and boundraries, he or she will eventually end up being a cracker. We do not want a cracker program. So what can we do?

Examine the code below.

 

alias readfile { echo $read(mirc.ini,$1)}/readfile 5 



Everything looks fine, and it will work as long as we supply it with an appropriate parameter. But what if the parameter isn't of the desired type? In this example we are the one who's choosing the parameter, and we know it has to be a number higher than 0. But the program does not know this. Yet. If we pass a parameter that's not a number higher than 0, mIRC will ignore that parameter (because $read works in that way) and a RANDOM line is returned instead. This is not intented and could be hard do spot during the debuging process.

 

alias readfile { if $1 isnum 1- { echo $read(mirc.ini,$1) }} 



Now it requires a number higher than 0 to work. By making our code more strict we are eliminating some potential problems and misintrepretations by the program itself. Next example.


on *:SOCKOPEN:mysocket:{ sockwrite -nt $sockname Hello World echo We want this to echo, but it never will.}/sockopen mysocket localhost 666Produces:* /sockwrite: 'mysocket' not connected (line XX, scriptfile.ext) 



This is generating an error which CANNOT be muted with the dot prefix. The error is also halting further processing, which we may not want. Luckily for us, socket events in mIRC has $sockerr, which will be set to a value higher than 0 when an error has occured. So to correct the previous code we do this:

 

on *:SOCKOPEN:mysocket:{ if $sockerr { echo An error occured! } else { sockwrite -nt $sockname Hello World } echo Now this part will echo.}/sockopen mysocket localhost 666Produces:An error occured!Now this part will echo.



Even though the socket is never opened, we allow the script to continue processing even if there's an error. This is not the only reason to check for errors, you wont have to see your status window filling up with unmutable error messages, which can be very annoying if they are recurring. I hope you realize the point of errorchecking, it is an important part of making your program do what you want, and nothing else. In some occasions it prevents the pain of debuging the code. Anyway, enough of this.



Chapter 3


3. Spotting Ambiguous Conditions

_______________________________________________________________________
The if structure is a fundamental building stone of any langauge, and faulty conditions can and will generate unintended results when mixing operators. I assume you already know the two logical operators && and ||, so I'm not going to explain them to you.

 

alias marriage { if $2 == Lisa && $1 == John || $1 == Mike { echo $1 will marry $2 } else { echo $1 will NOT marry $2 }} 



The point of this script is to decide to whom Lisa will marry (yes, mIRC can also solve love problems). $2 must be Lisa, no argument there. But the intention is that $1 must be either John or Mike. The following inputs will generate some odd results..

 

/marriage John Lisa/marriage Mike Lisa/marriage Mike John/marriage Mike MikeProduces:John will marry LisaMike will marry LisaMike will marry JohnMike will marry Mike 



Two ordinary weddings, one gay wedding, and one single person wedding (poor schizophreniac Mike). To elaborate the processing of this condition, we can illustrate it like this:

Condition: if $2 == Lisa && $1 == John || $1 == Mike Intention: The condition should evaluate to true if a) $2 is Lisa, and $1 is John. B) $2 is Lisa, and $1 is Mike. Reality: This condition will evaluate to true if a) $2 is Lisa and $1 is John B) $1 is Mike.Mike will marry anyone, and this is obviously not what we want. But what do we change? We use () parenthises to tell mIRC how to parse the condition.

 

alias marriage { if $2 == Lisa && ($1 == John || $1 == Mike) { echo $1 will marry $2 } else { echo $1 will NOT marry $2 }} 



And when we try the same inputs again..

 

/marriage John Lisa/marriage Mike Lisa/marriage Mike John/marriage Mike MikeProduces:John will marry LisaMike will marry LisaMike will NOT marry JohnMike will NOT marry Mike 



Mike is both straight and sane! Remember, you do not have to use () parenthises in mirc scripting, but there are certain times like these when you need to enclose partial conditions to ensure the proper parsing and result.


4. Brackets and Braces

_______________________________________________________________________
This is a neverending discussion whether or not to use brackets/braces when scripting for mIRC. This is of course, each individuals opinion and preference, and it is really up to you if you want to use them or not. I will go through the combinations (including abscence) of brackets and braces.

 

if (($me == John) && ($nick == Mike)) { echo whatever }on *:TEXT:hello:#:{ if ($nick isop $chan) { msg $chan Hi $nick } } 



This is standard for most people, it will parse every piece correctly but at the expense of readability. Of course, if you are used to this type of structure it wont be fugly to you. This is also the method of choice for beginners in mIRC scripting.

 

if ($me == John) && ($nick == Mike) echo whateveron *:TEXT:hello:#:if ($nick isop $chan) msg $chan Hi $nick 



This method is also fine and will be parsed correctly, if you use () parenthises you do not need {} braces unless you want to execute several commands in the same if/while branch.

 

if $me == John && $nick == Mike echo whateveron *:TEXT:hello:#:if $nick isop $chan msg $chan Hi $nick 



This MIGHT work, but it can also generate errors because mIRC cannot tell where the start of the command is. This method is NOT recommended.

 

if $me == John && $nick == Mike { echo whatever }on *:TEXT:hello:#:if $nick isop $chan { msg $chan Hi $nick } 



This is my personal preference, it will always be parsed correctly since we are using {} braces to seperate the condition portion from the command(s). But to provide more readability, use several lines.


if $me == John && $nick == Mike { echo whatever}on *:TEXT:hello:#:{ if $nick isop $chan { msg $chan Hi $nick }} 



The reason why I dislike ()parenthises is that it's extra pain when having $identifiers in the condition that has a () scope. This is a common error for both beginners and pro's, altering a condition and fail to spot the imbalance in the () parenthises. In conclusion, you do NOT need BOTH () parenthises and {} brackets. The helpfile claims that using both speeds up processing, which is NOT true, tests have proven that (feel free to try for yourself).




5. Double Events

_______________________________________________________________________
The most common error/bug in mIRC scripting. Could sometimes be tricky to spot but let me explain it to you. When mIRC processes a scriptfile, it checks for events and will only trigger the FIRST of the events in question. To illustrate this behaviour, have a look at this:

 

on *:START:echo This will echo.on *:START:echo This will NOT echo (above event was triggered). 



Fair enough, they are indeed identical. But what if a matchtext field was involved?


on *:TEXT:*:#:echo This will echo.on *:TEXT:!boo:#:echo This will NOT echo (above event was triggered).



Now THIS is where most people scratch their head and wonder why that second event isn't triggering. There are three solutions to this problem.

a) Put the events in seperate scriptfiles and make sure that event is the only one of its kind. B) Change the order of the events, put the !boo event above the * one. c) Integrate them into one single event using if branches to seperate the tasks.

on *:TEXT:!boo:#:echo This will echo.on *:TEXT:*:#:echo This will also echo.or...on *:TEXT:*:#:{ if $1- == !boo { echo First task. } else { echo Second task. }}

Note that if the events have seperate target fields (#,?,@) this problem will not occur.

on *:TEXT:*:?:echo This will echo.on *:TEXT:*:#:echo This will also echo. 

Of course, the most appropriate measures of avoiding this problem is to use different scriptfiles for each script/snippet, and to integrate the tasks in one single event (which will improve performance as well).


6. Timers

_______________________________________________________________________
Timers can cause some problems when using $identifiers/%variables Consider this:

 

alias time.in.titlebar { .timer $+ time 10 1 titlebar $time}/time.in.titlebar 



This will set the time to the same value over and over again. Why? Because $time was evaluated to a static value when the script was called. This can be proven by this:


//time.in.titlebar | echo -a $timer(time).comProduces:titlebar 17:41:19



Luckily timers re-evaluate the $identifiers/%variables passed to it, so to make this script dynamic, we must prevent the variable from being evaluated immediately.

 

alias time.in.titlebar { .timer $+ time 10 1 titlebar $!time}alias time.in.titlebar { .timer $+ time 10 1 titlebar $ $+ time}alias time.in.titlebar { .timer $+ time 10 1 titlebar $eval($time,0)} 



Any of these will work, and $time will only be evaluated when /titlebar is called. This rule also applies to | command seperators.


alias timer.test { .timer 1 1 echo -t First line. | echo -t Second line.}/timer.testProduces:17:58:24 Second line.17:58:25 First line. 



This will not work as intended, watch.

Commands: .timer 1 1 echo First line. | echo Second line. Intention: Set a timer with two commands: a) echo First line. B) echo Second line. Reality: Sets a timer with one command, and performs the next command a) .timer 1 1 echo First line. B) echo Second line.You cannot use {} braces to prevent this either, but there are solutions. a) Prevent | command seperator from being evaluated. $chr(124) and $(|) are two examples. B) Put the commands in an alias and make the timer call the alias instead.

Example:

 

alias timer.test { .timer 1 1 echo -t First line. $chr(124) echo -t Second line.}/timer.testProduces:18:00:29 First line.18:00:29 Second line.



Be aware of this, if something looks like it has been evaluated twice, check your timers. Note that this re-evaluating behaviour also applies to /scid and /scon.




7. Input Events

_______________________________________________________________________
I can't count the number of times I've seen a user who has more than one input event (which sends data to the server). This can be quite tricky if you create or get a script with such input event, while you already have on in another scriptfile. If both events executes /msg for example, you will be messaging the same message twice to the target.

 

on *:INPUT:#:{ if /* !iswm $1 { msg $chan ( $1- ) haltdef }}at the same time in another scriptfile..on *:INPUT:#:{ if /* !iswm $1 { msg $chan [ $1- ] haltdef }} 



Now you will /msg the channel twice, once for each of these scripts (if we'd forget /haltdef we would have four outputs!). There is only one fix to this problem, integrate them into one single event. Another thing you should notice with input events is that they do not use the ^ prefix when you want to halt the default text. If you use it, the event wont work. This is the only event that behaves in this manner.




8. Matchtext

_______________________________________________________________________
The purpose of a matchtext is to filter out unwanted lines, but this has some hidden issues which some aren't really aware of.

 

on *:TEXT:!my*:#:{ msg $chan My $2 is big.} 



This is a poor matchtext for this kind of script. Watch.

 

!mymomisafilthyhoeProduces:My is big. 



First of all, it only checks for a line starting with !my, whatever comes after is irrelevant for the program. We want this to be a space and thus, making a second parameter required (you can't end a line with a space since mIRC strips leading/trailing/repeative spaces).

 

on *:TEXT:!my *:#:{ msg $chan My $2 is big.} 



But what if we do not want that second parameter required? We want to be able to write !my as well as !my string. This cannot be achieved with wildcards, we need to bring in the heavy artillery (regexp). Since this isn't a regexp tutorial, I am not going to explain the fundamentals of regular expressions, I assume you already know them (I will explain what my expression means though).

 

on $*:TEXT:/^!my($| )/i:#:{ msg $chan My $iif($2 != $null,$2,car) is big.} 



Ok, so what does this do? Instead of a wildcard match we are using a regexp which will match !my at the start of the line, followed by either a space or the end of line. This eliminates the possible match on !mymymy but allows !my without anything behind it as well as a parameter (or several). Regular expressions also comes in handy when having several triggers in one event.

 

Inefficient code:on *:TEXT:*:#:{ if $1 == !on { msg $chan Script is on. } elseif $1 == !off { msg $chan Script is off. }}Better code:on $*:TEXT:/^!(on|off)$/i:#:{ if $regml(1) == on { msg $chan Script is on. } else { msg $chan Script is off. }}



This also eliminates the problem of double events in most cases. Moving on..




9. Using $$

_______________________________________________________________________
The extra $ in front of an $identifier means that it MUST return a non $null value to continue. I first thought it just performed a /return if it failed to fill in the parameter, but that assumption was incorrect. $$ will HALT the processing if it cannot return a value. This can lead to problems if you are not aware of this.

 

on ^*:TEXT:*:#:{ haltdef echo $chan $1-3 $bold($4)}alias bold { return $chr(2) $+ $$1 $+ $chr(2)} 



What's wrong with this code you ask? Basically, if $4 does not exist, the default text will be halted, but no custom message will be echoed, because $bold() requires a parameter. Anyway, if your code simply "doesn't do anything", check your $$'s.




10. The $address() Bug

_______________________________________________________________________
This is a bug rare known to beginners (and many experienced scripters as well). As far as I know the bug only appears in the on JOIN event, like the following example:

 

on *:JOIN:#:{ echo Address of $nick : $address($nick,5)} 



Sometimes $address() returns $null, which is a consequense of the IAL not being update properly. Therefor you should avoid this use of $address(). Note that $address() and $address is not the same, $address() refers to the IAL while $address do not (it's taken from the RAW string). So to prevent this odd bug, use $fulladdress/$address in conjuction with $mask().




11. Flood Protection

_______________________________________________________________________
When making scripts that sends data to the server and allowing other users to trigger it, you will need to protect yourself from the infamous excess flood. As you probably know, you cannot send too much information to the server, or it will disconnect you. This is something people love to exploit, but it is easily fixed. There are many types of flood protections, here's some examples:

 

on *:TEXT:!trigger:#:{ msg $chan You have triggered me!} 



Bad code. Lets fix it.

 

on *:TEXT:!trigger:#:{ .timerflood. $+ $nick 1 1 msg $chan You have triggered me!} 



This will limit the triggering to once per second per nick. Another example:

 

on *:TEXT:!trigger:#:{ if %count <= 3 { msg $chan You have triggered me! inc -u3 %count }} 



This will limit triggerings from everyone. The variable increases by one every triggering, and is unset 3 seconds after it's increased (unless another triggering increases it again). As long as the variable is below 3 it will perform the task given, otherwise it will just ignore the trigger until the variable has been unset.

If you do not want a custom flood protection, take a look at: /help /flood






12. Echoing Numerical Strings and Colors

_______________________________________________________________________
Ever had the problem with an * /echo: insufficient parameters error? Can't figure out why it is not echoing? Check if the first parameter is a numerical value, and if so, add a non-numerical string in front of it OR add a switch/target to it. Note that this only applies to /echo WITHOUT switches.

 

//echo $count(myggan,g)



Should echo 2, right? Actually no. If you take a look at the /echo syntax:

/echo [-cdeghiNtsaqlbfnmr] [#channel|[=]nick] Notice the parameter. If you do not supply a switch to /echo, and the string starts with a space delimited numerical, mIRC will treat that parameter as a color and not a part of the string to echo. The previous example will be evaluated to:

 

/echo 2 



This will try to echo a $null string with the color 2 (dark blue by default).

When echoing a string with color ctrl codes, make it a habit to PAD your color index number with a leading 0, if the color index is below 10. Consider this:

 

//var %string = foobar | echo -a $chr(3) $+ 4 $+ %string



Works quite fine. But what if %string started with a number?

 

//var %string = 0foobar | echo -a $chr(3) $+ 4 $+ %string 



No longer working. Why? Because that 0 in %string will be treated as a color index along with the 4, in other words 40, which isn't a valid color index. The fix is easy:


//var %string = 0foobar | echo -a $chr(3) $+ 04 $+ %string



This applies to the background color index as well (,). Always make sure your color indexes contain two numbers, an easy way to do this is:

 

alias fixcolor { var %index = 1, %result while $eval($ $+ %index,2) != $null { %result = $addtok(%result,$base($ifmatch,10,10,2),44) inc %index } return %result}//echo -a $fixcolor(2)//echo -a $fixcolor(5,7)Produces:0205,07 








13. Not Reading the Helpfile

_______________________________________________________________________
This is the deadliest of sins. The help file contains the information you need in 95-100% of the cases. And just because you do not understand what is said in the helpfile, doesn't mean it's not wellwritten, it only means you didn't put enough effort in reading and understanding the text. The /help command is extremly easy to use (can be a bit tiresome to scroll though). But the point is, people get pissed off at people who asks question without reading the manual first. I know this from years of experience in helping people, the longer you have volunteered to help out, the bigger gets the suppressed need of telling people to "stfu and go read the manual". So please do.

However, if you (like me) find me helpfile somewhat tiresome to find stuff in, or if you feel something missing, take a look at these tools:

/rtfm - instant /help by myggan Updated mIRC Helpfile by Teazle mIRC Chm Help 6.16 Anchored by AdrenalinTop

14. Getting Helped

_______________________________________________________________________
Everyone needs help sometimes, and that's fine. Alot of IRC networks and Web forums have volunteering helpers, which will offer you help 100% for free, without anything to gain. If you marsch into a channel or into a forum with a thingyy attitude, you will have one or more of these scenarios ahead of you:

a) People will get pissed off. People will laugh at you. c) People will ignore your need of aid. d) People will think you have a small thingy.If you have a question, do not hesitate to ask. You should not ask for permission to ask a question, which is quite paradoxal if you think about it. Classical questions like these will only annoy people: a) Can anyone help me? I need help! c) My script does not work, what is wrong? d) This is a help channel, help me!Remember, demanding help from a non-profit organization is retarded. Here are three key elements in getting helped quickly and easy:



a) Being straightforward, and provide the helpers with sufficient information (debug/error messages) Being humble, respect the helpers aiding you without any personal gain. c) Speak english if the channel/forum is international.

Also, follow these steps BEFORE asking questions:

1) a) If you are searching for a particular script, search google and the popular mirc scripting sites. If you have a piece of code, test it by yourself and examine the results. c) If you have a question on how to do something, just ask. 2) View debug/error messages and try to correct your code. 3) Read the manual for command syntaxes, use of switches, other notes, etc.. 4) a) Collect debug information and error messages and paste them along with the code in a pastebin. Paste the URL in the channel/forum along with a basic briefing on your problem. c) Wait for someone to answer, and try to solve it on your own while waiting. Pastebins: www.pastebin.com www.nomorepasting.com www.google.com/search?q=pastebin Scripting Sites: www.google.com/search?q=mirc+scripting

This tutorial was written by myggan (11/12-2005)
If you have any questions, feel free to contact me:

IRC: irc.quakenet.org -> #Scripting irc.dal.net -> #Scripting MSN: [email protected] (mail as well) WEB: http://www.mircscripts.org/users/mygganThanks for reading! :)



*Special thanks to myggan for allowing us to use this tutorial.

Link to comment
Share on other sites

×
×
  • Create New...