Perl Developers Guide to Xpertmud

Chapter 3. Perl Developers Guide to Xpertmud

Table 3.0. Functions

addAlias addIncludeDir addKeyBinding
addTimer addTrigger delAlias
delKeyBinding delTimer delTrigger
listAliases listKeyBindings listTimers
listTriggers parse prs
resetWriteRegExps setAliasState setKeyBindingState
setTimerState setTriggerState setWriteRegExp
showKeyNames statusWindow

Table 3.1. Classes

XMWindow XMTextWindow XMTextBufferWindow

Table 3.2. Modules

XM

Table 3.3. Topics

Scripting Basics Scripting Misc

Topic: Scripting Basics

Look here first if you want to do anything with xpertmud...

Getting Started

At first you should check that the perl interpreter is really loaded and active: 
Have a look at the last box in the status line, it displays information about the current scripting environment. If it shows something other than "perl", you should load the perl interpreter via the "Scripting" menu. 

now we are ready to execute some perl code: 

Possibility 1: interactive/immediate 

every line starting with '#' is treated as perl code and immediatly executed, so entering 
# print "Hello world!\n"; 
would print "Hello world" to stdout. 
To display text visible inside xpertmud, we need to tell perl to which window the output should go (xpertmud is a multi window client after all) 
The window visible after startup is called "statusWindow", so 
# statusWindow->print("Hello World!\n"); 
is probably what we want. 

Possibility 2: via the bookmark-editor 

after choosing Connection->connect... from the menu, you should see the bookmark editor containing a textarea labled "Script". After a successfull connect all lines in this box are treated as entered directly into the input-line, which means that you could add commands there as explained above. 

Possibility 3: using an external file 

The bookmark-editor isn't quite suitable for writing complicated scripts, so you probably want to use an external editor. 
The right place to put your script would be '~/.kde(2?)/share/apps/xpertmud/perl'. 
This directory is added to the include path automatically, if it exists while loading the interpreter. So simply restart the interpreter after creating the directory. 
now you can run any file by typing 
'#parse "filename.pl"' 
(or adding this line in the bookmark editor) 


Colored Output

TextWindow objects like 'statusWindow' offer a lot of functionality to change textcolors and styles. 

This example uses 
clear(): clear the window 
setBGColor(): set the background color 
setFGColor(): set the foreground color 
setIntensive(): switch to intensive foreground colors 
setCursor(): position the cursor 
resetAttributes(): reset FG/BG colors to defaults 

details about this functions are available in the online help system. 

now have a look at this example: 
------------------------------------------ 
sub colorTest() { 
statusWindow()->clear(); 
for (my $y=0; $y<16; ++$y) { 
statusWindow()->setBGColor($y); 
for (my $x=0; $x<16; ++$x) { 
statusWindow()->setCursor($x*2,$y); 
if ($x<8) { 
statusWindow()->setFGColor($x); 
statusWindow()->setIntensive(0); 
} else { 
statusWindow()->setFGColor($x-8); 
statusWindow()->setIntensive(1); 
} 
statusWindow()->print("XX"); 
} 
} 
statusWindow()->resetAttributes(); 
} 
------------------------------------------ 
this should draw a box showing different color combinations. 
Instead of using numeric values as colors, you could also use one of the predefined constants: 
sub BLACK { return 0; } 
sub RED { return 1; } 
sub GREEN { return 2; } 
sub YELLOW { return 3; } 
sub BLUE { return 4; } 
sub MAGENTA { return 5; } 
sub CYAN { return 6; } 
sub WHITE { return 7; } 


Aliases

Aliases are client side macros and can be used to create client side shortcuts. 
the alias functions rely on perl regexps, so if you aren't familiar with them, have a short look at 'man perlre'... 

let's start with an example: 
addAlias("INV",'^i$',"inventory"); 

this line adds an alias named "INV" which replaces an 'i' on a line by itself with 'inventory'. 

to see which aliases are currently loaded use 
listAliases 
(rememeber that you have to type '#listAliases') 
the output also shows the execution order of your aliases, new aliases are always added to the top of the list. 

Deleting aliases: 

to get rid of an alias use: 
delAlias('^INV$') 
You can also delete a whole group of aliases at once by supplying a real regexp to delAlias: 
delAlias('^PREFIX_) 
delAlias('[ABC]\w+') 

Disable aliases: 

sometimes it makes sense to deactivate aliases 
instead of deleting them, in case they are needed again later: 
setAliasState('^INV$',1) # enable 
setAliasState('^INV$',0) # disable 

as above, this function can work on a group of aliases as well. 

Recursive Aliases: 

aliases are recursive by default, that means all matching aliases can modify the command. Imagine those two aliases: 
addAlias("INV1",'^inv$',"inventory") 
addAlias("INV2",'^i$',"inv") 
entering 'i' would now send the command "inventory" to the MUD, which might not be what we want. 
by supplying a true value as 4th parameter to addAlias, we tell the scripting engine to stop alias processing after this alias has been executed successfully. 
addAlias("test1",'^test1$','#statusWindow()->print("test1\n")') 
addAlias("test2",'^test2$','#test2',1) 
now 'test1' would print 'test1' onto the status window, but 'test2' would send '#test2' to the mud server 
(this is broken in the 2.0beta1 release) 

Executing perl code in aliases: 

sometimes a simple text replacement is not flexible enough, so you have the possibility to execute arbitrary perl code in an alias. Simply specify a subref instead of the replacement text. 
Your code will be called with the entire line of input as parameter and is expected to return the replacement text (or an empty string) 

(pretty useless) Example: 
addAlias("move",'^move$',sub { 
return qw(n e s w)[rand(4)]; 
}); 

By the way, the perl command evaluation is internally implemented via this alias: 

 addAlias("Eval", "^#", sub { 
my $text = shift; 
eval(substr($text, 1)); 
if($@) { XM::showError($@); } 
return ""; 
}, 1); 


Triggers

We have seen how aliases can be used to modify commands entered by the user. Working with text received from the server is nearly as simple and done with triggers. 
Installing triggers is done with addTrigger: 
addTrigger("KidMode","f..k","*beep*"); 
Triggers in combination with SubRefs can be used to automatically react to events: 
addTrigger("Flee",'\w+ attacks you',sub { 
my $text=shift; 
XM::send("run away fast\n"); 
return $text; 
}); 

Be carefull with '^' and '$' in your regexp, as the complete text received by the server is used for the comparsion and could contain ANSI control codes. 

modifying Triggers: 
------------------- 

List all triggers: 
listTriggers 

Disable all triggers: 
setTriggerState(".?",0); 

Enable all triggers with an 'a' in their name: 
setTriggerState("a",1); 

Delete Trigger "Delete_Me", but not "Dont_Delete_Me": 
delTrigger('^Delete_Me$') 

Recursive Triggers: 
------------------- 

as with aliases, all text is put through any trigger that matches. To stop processing after execution of a trigger, supply a true value as 4th parameter to addTrigger. 

Distribute text between different Windows: 
------------------------------------------ 

It's time for a more complicated example: 
Suppose you want to have a separate window showing your current health in BIG letters. 
The server tells you about your health with a line like: 
Health: 42 
We start by creating a seperate window: 

my $healthWin=new XMTextWindow("Health"); 

Note: if you are trying this in interactive mode, you can't use local "my" variables. Instead type: 

# use vars qw($healthWin); 
# $healthWin=new XMTextWindow("Health"); 

Prepare the window: 
$healthWin->setDefaultBGColor(CYAN); 
$healthWin->setDefaultFGColor(RED); 
$healthWin->setFont("fixed",42); 
$healthWin->resetAttributes; 
$healthWin->resizeChars(5,1); 
$healthWin->show; 
$healthWin->clear; 

Now we have a window with ugly colors and an huge font. Time to get the info into it: 

addTrigger("ShowHealth","Health: ", sub { 
my $text=shift; 
if ($text=~ /Health: (\d+)/) { 
$healthWin->clear; 
$healthWin->print($1); 
return ""; #don't display on statusWindow 
} else { # mismatch 
return $text; #unmodified 
} 
},1); 

Quite easy, huh? 
Things get more difficult if you want to move multiple lines into another window, e.g. your inventory list. 
A possible solution would use three triggers: 
one trigger ("START") that matches the beginning of the inventory list, an "END" Trigger to match its end and one trigger ("OUTPUT") to match anything that is not "END"... 
"END" and "OUTPUT" are inactive at startup. 
"START" clears your output window and activates "OUTPUT" and "END". "OUTPUT" will match any line in between and write it to a seperate window, and "END" would disable itself and "OUTPUT". 

Problems: 
--------- 

Whenever input arrives from the server, it is split into seperate lines which are then feed to the triggers. But what should be done if the last part does not end in a newline? 

Possibility one: 
Assume that this line will be completed later on and store it, till the next "\n" arrives. The part will not be displayed or put through triggers till the line is complete. 

Possibility two (default in xpertmud): 
Assume that this was a prompt and not a part of an longer line. Immediatly run triggers and output it. Should the second part of this line arrive later, do a seperate trigger run. 

Both possibilities have their drawbacks, so xpertmud tries to find a balance between them: 

two global variables, $writeAtOnceRegExp and $dontWriteAtOnceRegExp control which output should be displayed immediatly: 
any block that is matching /$writeAtOnceRegExp/ and not matching /$dontWriteAtOnceRegExp/ will be displayed at once. So if TCP fragmentation is causing your triggers to fail sometimes, try to tweak those. 


KDE Logo