|
Neat Scripting
The first step is to learn how to write the script correctly and
efficiently.
* Don't use excessive Braces { } where you don't need them,
and you only need them when there's more then one line in the command.
The { Brace means "Begin" and the } Brace means
"End" so if there's only one line to the event or command, you don't
need to give the script a beginning and end command.
For example:
ON *:text:*:*:
{ if ($nick = Blah) { do something here } }
Which the scrip reads like this: ON *:text:*:*: Begin-event if ($nick = Blah) Begin-if do
something here End-if End-event
So as you see, you don't need to tell it where to
begin and end if there's only one line.
So this would better:
ON *:text:*:*:
if ($nick = Blah) do something here
Now besides the fact that it's useless to put
excessive Braces, they all use more CPU.
That's because the { Begin
Brace is actually a command that calls a procedure to check for the } End Brace
(and run all the commands between of course).
It doesn't take a lot of CPU of course, but it's about
the same amount as calling an alias (see "Saving CPU/memory usage").
* If you are making aliases that call stuff
so you want have to write a lot of code, then do it right.
Here's an example from an MDX tutorial I've read somewhere:
alias -l mdx { return $+(",$scriptdir,mdx.dll,") }
alias -l Views { return $+($scriptdir,Views.mdx)
}
dll $mdx SetControlMDX Dialog
ID ListView Style > $views
Now as you can see in the end you
still have to use the /dll command with both aliases,
and add the > prefix there.
So if you think about it, you could
add these commands to your aliases and make them call each other
:
Alias -l mdx { dll
$+(",$scriptdir,mdx.dll,")
$1- }
Alias -l views
{ mdx
$1- > $+($scriptdir,views.mdx)
}
views SetControlMDX
Dialog ID ListView Style
This way you can save yourself a lot
of writing, and it will look more organized.
* One $+ is
enough, looks much better and you can add whatever you want with a comma :
notice
$nick <> Account created: ftp:// $+ $nick $+ : $+ $iif($2,$2,%ftp.defaultpass) $+
@ $+ %ftp.server $+ : $+ %ftp.port
$+ / <>
notice $nick <> Account created: $+(ftp://,$nick,:,$iif($2,$2,%ftp.defaultpass),@
,%ftp.server,:,%ftp.port,/ <>)
* Use %vars that have
meaning, so you can return after a few weeks and remember what each %var contains.
Look at the
next tip for a good example… Why not call that %cc variable %result
? or just %channels ?
* Don't use
the | separator ! It can get very confusing if you look back at the script a month from now. Every
command should have its own line:
Alias com-chan { if ($1) { var
%a = $comchan($1,0) | if (%a > 0) { while ($comchan($1,%a)) { .var
%cc = $addtok(%cc, $comchan($1,%a), 44) | dec %a
} echo $nick
Common Channels: %cc } } | else { return } }
This looks like a mess, and in a few weeks
it will be pretty hard to follow.
Here's a better way of scripting it:
Alias com-chan {
if ($1) {
var %a = $comchan($1,0)
If (%a > 0) {
while ($comchan($1,%a)) {
var
%cc = $addtok(%cc, $comchan($1,%a), 44)
dec
%a
}
echo $nick
Common Channels: %cc
}
}
else { return }
}
* Don't use generalized events like ON *:text:*:*: if you don't have to. Try to at least pin point
it to specific match text or location like ON *:text:*:?:
It's a waste
of resources for the event to work and process its lines, where there's no
chance for it to work.
* Use the level prefixes to prevent events from
working where they're not needed:
On me:*:Join:*: Echo –a This
works when you join any channel
On !*:Join:*: Echo –a This works when
anyone but you joins the channel
On @*:Join:*: Echo –a This
works when you're oped on the channel someone just
joined.
Just remember
that Events work like If-Else statements, if you have more then one event of
the same type in the same file, the script will run through them by their
order, and if the first event triggers, it will halt all other events of the
same type.
On *:text:*:*: echo –a yeap
On *:text:*check*:*: echo –a test
The second
event won't run even if the word "Check" is in the line. But if the
second line would come first:
On
*:text:*check*:*: echo –a test
On *:text:*:*: echo –a yeap
The second event
will trigger if the word "Check" doesn't appear in the text.
* Keep your script organized. Keep
event types and aliases together, and try to sort on a way that you will
remember, I would suggest alphabetical order.
The order in a script is very important for you to keep track of what's
going on while the script gets bigger and messier.
Using ;Comments is a good advice to help you
read the script.
For example:
;Raw
raw *:*: echo -s
$numeric $1-
;CTCP
ctcp *:*:*: Echo -a CTCP $1-
;Events
on *:conenct:
on *:kick:#:
on *:open:?:
on *:text:*:*:
on *:quit:
;Dialogs
;Dialog Test
Dialog Test {
}
on *:dialog:Test:init:*:
on
*:dialog:Test:iclose:*:
on *:dialog:Test:sclick:*:
;Aliases
Alias –l Blah
Alias –l Something
Alias –l Test
You see, it can look a lot nicer, and you will be able to go through
your script easily even while it bigger and bigger.
Addon
Tips
When you make an addon, remember that there
are probably other scripts loaded. You need to follow some simple rules, so it
won't interfere with the other scripts.
* Try to avoid using the /halt command in your custom
aliases.
The /halt command
will not only halt the current alias, but the entire parent event that called
it.
Unless you
know what you're doing, and you really intend to stop all the processes of that
event, just use /return command instead.
* You can try using the ! prefix when calling a default command to make sure it calls mIRC's default command and not an alias:
For example:
!join #channel
!notice Nick blah
* Use only local variables with the /var command instead of /set or just %var
= value.
Remember that
if the user has a full script, it might have
used most of the common variable names you would like to use.
Because of
this, remember to create the variable before you do a Boolean check on it,
example:
Alias Blah {
Var %check
If ($1 = blah) var %blah = yeap
If (!%check) return nope
Else yeap
}
You can see
here: the variable %blah in the beginning, so the script will use this local
variable. If I didn't set it up, and $1 wasn't equal to "blah" it
would have checked if there's a global variable called %check.
* If you want to set any global setting that will be
saved for further use, then you can always use .ini
files, to store data, and Hash tables, if it's only temporary.
Saving CPU/
Memory usage
Saving CPU and memory usage is only relevant when you have really long
loops, or have events that you know that are going process a lot of data, like
ON TEXT events.
You won't notice most of these tips if you don't have any time consuming
loops or events.
The best way to check yourself, is to make a
loop that will repeat itself about 10000 times, and in it try all sorts of ways
to do what you want, and then see how many ticks it takes for the loop to
finish. Ex:
Alias Test {
Var %ticks = $ticks
Var %a = 1
While
(%a <
10000) {
;The
method you're checking comes here
}
Echo
–a $calc($ticks - %ticks)
}
Just remember that 1000 ticks are equal to about 1 second.
* When using multiple arguments in an IF sentence, it
would be good to sort them in a logical order.
This is
because the IF sentence evaluates the arguments from left to right.
So, if for
example you have an IF sentence with multiple arguments using the AND
(&&) operator:
IF (Blah isin
%check) && (/msg isin %check)
And you know
that the most important thing to check is if the command /msg
is in the line %check then it's better to put the /msg argument first, and if it doesn't exist the script will
move on:
IF (/msg isin %check)
&& (Blah
isin %check)
This kind of
logic is good for arguments of the same kind, but if you have a more CPU
consuming argument, like a $read(file.txt,w,*blah*)
, you should leave it to the end, even if it's more "important"
logically.
You don't want
the script to run too many CPU consuming $identifiers or aliases if there's
another simpler argument that could rule them out and stop the processing of
the IF statement.
* Don't give the same argument twice in an IF-ELSE
sentence if it can be assumed from the first argument, ex:
If ($4) && ($5)
{ }
Elseif (!$5) { }
In the first
IF sentence you don't need to check if $4 exists, because if $5 exists we can
assume there's a $4.
And in the
second line, you don't have to check if $5 doesn't exist, because if the first
line fails then you can assume that $5 doesn't exist.
So this would
be better scripted like this:
If ($5)
{ }
Else { }
Just remember
that every calculation and every argument uses resources.
* Combine similar IF-Else sentences if you are trying
to evaluate similar sentences or data. Here's an example:
var
%format =
Hi*there*
if (%format
iswm $1-) return 1
var
%format = Hi
my name is*how*everyone*
if (%format
iswm $1-) return 2
var
%format =
Hi*everyone*
if (%format
iswm $1-) return 3
var %format = Hello*everyone*
if (%format
iswm $1-) return 4
You can see
that there are a few words that repeat in these lines, these are: Hi , Hello, and everyone.
So, instead of
making the script go through all the line formats, it would be better to group
them into groups, with similar matches, so it will have less
arguments to calculate.
Remember that
it's easier for the script to check a certain item in the text then searching
for a wildmatch.
If ($1 = Hi) {
var %format = *there*
if (%format
iswm $2-) return 1
var
%format = Hi
my name is*how*everyone*
if (%format
iswm $2-) return 2
var
%format =
Hi*everyone*
if (%format
iswm $2-) return 3
}
var
%format =
Hello*everyone*
if (%format
iswm $1-) return 4
Now the
script does exactly the same thing, but now if the sentence doesn't start with
the word Hi, it won't evaluate all the arguments that have that word.
Notice that
because I've already checked the first word, all the other if sentences try to
match the rest of text from the second word.
* Regular expression lines are much harder for the CPU
then regular Wild Match expressions.
If you are
searching for a certain line or match in an ON TEXT event, it would be better
to use regular "if" sentences with iswm and
isin rather then a $regex
expression.
After you've
found the match, you could start using $regex
expression to get the data you want from the line. But the time and CPU
consuming work is matching the text in the ON TEXT event.
I gave the ON
TEXT event as an example of a loop that works constantly on almost every line
received from the server
* Avoid
putting in unnecessary calculations. Here's an example of a size calculator
that calculates file sizes from Mb, Kb etc. to bytes:
$calc($replace($remove($1,b),k,*
1024,m,* 1024 ^2,g,* 1024 ^3))
Although
this looks very nice and easy, it still needs to calculate 1024 ^2 and 1024 ^3
every time it calculates Mb or Gb
values.
This can
start to show its effects when you have a very long loop, like searching a
directory, or a dialog list.
It is
better to evaluate them yourself, and save the computer some time and effort:
$calc($replace($remove($1,b),k,*
1024,m,* 1048576,g,* 1073741824))
* Sometimes it's better to use a regular loop and a
regular If (V1 iswm V2) sentence, rather than looping
through certain $identifiers that have Wildmatch
options in them.
The best way
to explain this is by an example using $hget to find
certain items in a HASH table.
For example,
look at both these loops:
Var
%a = 1
while
($hfind(HashTable,*wildcard*,%a,w)) {
;Do stuff here
inc %a
}
Var
%a = 1
while
($hget(HashTable,%a).item) {
var
%item = $v1
if (*wildcard* iswm %item) {
;Do stuff here
}
inc %a
}
Although the
first loop looks much neater and exploits the $hfind
identifier to find you all the matching items, it will still take about twice
as much time to resolve.
That happens
because the $hfind identifier tries each time to find
the next matching item, but it starts each time from the beginning of the Hash
table. So if you have a lot of items that match the wildcard it will go over
the the entire Hash table over and over.
But the second
loop will just go over the Hash table once, and find all the matches.
* Try not to use $identifiers in a while
loop's argument. If you know that the $identifier will return a constant value
that won't be changed in the loop's process then it's better to set the
$identifiers value to a %variable, and then use the %variable as the argument.
That's because the loop will evaluate the $identifier on each run to
check if the value hasn't changed.
Just to show you how much CPU you can change:
Alias
test {
var
%ticks = $ticks
var
%a = 1
while (%a < $lines(servers.ini)) {
inc %a
}
echo -a $calc($ticks - %ticks)
}
This little alias will take 1297 ticks
(my servers.ini has 976 lines)
But if I'd put the $lines value in a %variable,
and use the %variable in the argument:
Alias
test {
var %ticks
= $ticks
var %a = 1
var %lines = $lines(servers.ini)
while (%a < %lines) {
inc %a
}
echo -a $calc($ticks - %ticks)
}
This takes only 16 ticks to process. And
that's because it doesn't have to process the $lines identifier 976 times.
* Don't hesitate to store data that you
get from $identifiers into variables. mIRC
$identifiers and aliases are little procedures that calculate things, and
return the data you requested. But each time you call it, it recalculates.
Reading
from files takes a lot of CPU usage too, so if you are reading the same thing,
or different parts of the same line, it would be better to store it in a %var.
For an example, here's something that reads
settings from the mirc.ini file:
if
($gettok($readini(mirc.ini,options,n7),2,44)
= 1) { do stuff }
if
($gettok($readini(mirc.ini,options,n7),3,44)
= 1) { do stuff }
if ($gettok($readini(mirc.ini,options,n7),16,44) = 1) { do stuff }
if ($gettok($readini(mirc.ini,options,n7),19,44) = 1) { do stuff }
Now if it
would be on an On Dialog Init event, that wouldn't be too bad because it only
loads once. But it would still be better to read from the file once, and then evaluate
that line over and over:
Var %line
= $readini(mirc.ini,options,n7)
if ($gettok(%line,2,44) = 1) { do stuff }
if ($gettok(%line,3,44) = 1) { do stuff }
if ($gettok(%line,16,44) = 1) {
do stuff }
if ($gettok(%line,19,44) = 1) { do stuff }
* Dialog lists are very slow. If you have a very large
list that needs to be listed, or filtered, list it in a @window first and then
filter it back to the dialog's list.
Although it
might sound strange because you're actually listing it twice, it's faster.
For an
example, make a dialog called test:
Dialog Test {
Size -1 -1 380
200
Option dbu
List 2, 10 32
120 160, size sort
Button
"OK", 1, 285 180 42 13, ok, default
}
Now try the 2 ways, we'll calculate the amount of
Ticks (1000 ticks = about 1 sec) that pass from the beginning of the alias to
the end…
Try the /did –a line:
alias
test {
var
%ticks = $ticks
var
%b = 1
While (%b < 10000) {
Did -a test 2 Test This $+ %b
Inc %b
}
echo -a
$calc($ticks
- %ticks)
}
Load the dialog (/dialog –m Test Test)
and then type /test … I got 1078 Ticks
Now lets try the other way:
alias
test2 {
var
%ticks = $ticks
window -h @test
var
%b = 1
While (%b < 10000) {
aline
@test Test This $+
%b
Inc %b
}
filter -o @test test 2
close -@ @test
echo -a
$calc($ticks
- %ticks)
}
Load the dialog (/dialog –m Test Test)
and then type /test2 … I got 515 Ticks
So we can clearly see that this method is twice as
fast, although we've opened a hidden window, listed into it, then copied all
that is in that window to the dialog with the /filter command, and then closed
the window.
Now if you want to use /filters with your dialog it
would be the same, and it's actually better to filter
the list to a @window and then back again, than filtering the list to itself.
I'd advise to have a hidden window that has the same
information as the list has, and then filter that window into the dialog.
* You should remember that calling
aliases is also time consuming and can use up your CPU.
I'm not saying you shouldn't use aliases, but you could take in mind
that you don't have to use a lot of little one lined aliases in your script if
you want to reduce the time it takes to process you script.
Here's an example, I'll do exactly the
same thing, but I'll add aliases to call some of the commands:
Alias
test {
var
%ticks = $ticks
var
%a = 1
while (%a < 20000) {
var %check = a
if (%check = a) var
%check = b
inc %a
}
echo -a $calc($ticks - %ticks)
}
This is a simple enough alias, just
loops around a couple of thousands of times. This echoed a value of 1094 ticks
on my PC.
Now if we take out the contents of the
while loop, and put them in a separate alias and then call it, look what
happens:
Alias
test {
var %ticks = $ticks
var %a = 1
while
(%a <
20000) {
test2
inc %a
}
echo
-a $calc($ticks - %ticks)
}
Alias
test2 {
var
%check = a
if (%check = a) var
%check = b
}
Now it echoed a value of 1266 ticks. I
know it's not a lot, but that's a lot for just calling a single alias that does
the exact same thing.
And look what happens if I brake the test2 alias into another alias:
Alias
test {
var %ticks
= $ticks
var %a = 1
while
(%a <
20000) {
test2
inc %a
}
echo
-a $calc($ticks - %ticks)
}
Alias
test2 {
var
%check = a
if (%check = a) test3
}
Alias
test3 {
var %check = b
}
Now it returns 1406 ticks… we lost
another 150 ticks just because we called another alias.
Now we see here that at a 20000 loop
each alias called adds us about 200 ticks (0.2 seconds or 20% in this example).
And that is just for calling the alias, no matter what it contains.
Written by Yochai Timmer Yochai@Dorot.org.il
|