Warning, /games/kmuddy/Scripting-HOWTO is written in an unsupported language. File is not indexed.

0001 KMuddy Scripting HOWTO
0002 ----------------------
0003 
0004 This document will try to teach you the basics of external scripting for KMuddy.
0005 It assumes no programming experience, though it would be helpful...
0006 
0007 KMuddy scripts can be written in many different languages, both compiled
0008 and interpreted ones. For this document, I've chosen to use the Perl
0009 language, because it's easy at the beginnings, yet very powerful once you get
0010 more into it.
0011 
0012 1. Basic script structure
0013 -------------------------
0014 
0015 There are more script types possible, but one that you'll probably want to
0016 use most is a script that reads output from the MUD line after line, looking
0017 for certain patterns that you instruct it of, and executing some commands
0018 after a certain text comes.
0019 This document will describe this type of script, because it's probably the
0020 most useful one.
0021 
0022 The basic structure of such script looks as follows:
0023 
0024 #!/usr/bin/perl
0025 $| = 1;
0026 
0027 #initialization goes here
0028 
0029 while (<STDIN>)
0030 {
0031   #process line here
0032 
0033 }
0034 
0035    
0036 The first line informs the OS, that the script should be processed by perl.
0037 The second line switches output into unbuffered mode, which is needed to prevent
0038 commands from being sent to the MUD too late.
0039 Lines beginning with a #, are comments (first one is a bit special).
0040 Next, we can do some initialization (nothing for now), and the while-cycle
0041 waits for output from the MUD, reads it line after line, and processes it.
0042 (Note that this simple script never ends. More on this later.)
0043 
0044 The script should be saved in a file somewhere. Preferably to a directory
0045 dedicated for your scripts. Then, set the script to be executable. This can be done
0046 using your favourite file manager, or from the console by typing
0047 "chmod +x scriptfile" (without quotes, replacing scriptfile with the name
0048 of your script). Then add the new script into KMuddy, setting name to something
0049 reasonable, executable path should point to your script.
0050 Don't modify any of the options, you won't need those in the beginning.
0051 
0052 The script can be executed by typing
0053 /exec scriptname
0054 where scriptname is the name given to the script in the Edit script dialog.
0055 You can execute scripts from the input line, or from an alias/trigger, as
0056 you want.
0057 
0058 
0059 2. Pattern matching and command execution
0060 -----------------------------------------
0061 
0062 The most important feature of a script is the ability to parse text coming
0063 from the MUD. The most common way of doing this is by using regular
0064 expressions. I'm not giong to explain regexps here, refer to KMuddy
0065 documentation for a documentation about regexps and back-references. You may
0066 also want to read some more advanced regexp documentation somewhere, once
0067 the basics described in KMuddy's manual are not sufficient for you, but these
0068 basics should be sufficient for now.
0069 
0070 As a simple example, let's say that you want to send "eat bread", whenever
0071 "You are hungry." comes from the MUD. This could be done via triggers, but
0072 this can also be the basis of more advanced scripting. :)
0073 
0074 #!/usr/bin/perl
0075 $| = 1;
0076 while (<STDIN>)
0077 {
0078   if (/You are hungry\./)
0079   {
0080     print "eat bread\n";
0081   }
0082 }
0083 
0084 So what does this do?
0085 The first part is the same as the basic script mentioned above.
0086 The line beginning with 'if', takes the line received from the MUD and compares it with
0087 the regular expression given in between the '//' characters (note that the 'period' has to
0088 be protected by a backslash, because a 'period' is a special character of regular
0089 expressions).
0090 When the corresponding text arrives, the command after 'if' is executed - in our
0091 case it's the print command. This command prints something to the standard
0092 output of the script, which is caught by KMuddy and sent to the MUD.
0093 The \n sequence is very important here, it represents a newline. It is
0094 equivalent to pressing the ENTER key by the script, so it needs to be
0095 given after every command that your script wants to send.
0096 The {} sequence needs to be written after every if-command (C programmers beware)
0097 Finally, all commands need to be terminated with a semicolon ;. ('if' and
0098 'while' and the '#!/usr/bin/perl' lines are not commands, but structural blocks)
0099 
0100 Note that multiple commands can be executed as well:
0101 
0102   if (/You are hungry\./)
0103   {
0104     print "eat bread\n";
0105     print "say I ate bread.\n";
0106   }
0107 
0108 The indentation is not required, perl will understand it anyway, but it makes
0109 your script more readable.
0110 
0111 If you've read the regexp docs, you know that the pattern is not good, because
0112 it looks for that line everywhere, including the middle of the sentence, and
0113 that is not good. So, we modify it a bit:
0114 
0115   if (/^You are hungry\.$/)
0116 
0117 Now it's correct :)
0118 
0119 
0120 3. Multiple matching blocks
0121 ---------------------------
0122 
0123 The first script was nice, but not very useful. A trigger can do the same thing.
0124 Now, instead of emulating one trigger though, we'll learn how to emulate two 
0125 or more of them. Also, we'll learn how to make it so that only some matching 
0126 occurs if some other script was successful and/or unsuccessful.
0127 
0128 So, let's assume that you want to write an idiotical (read: example) script
0129 that will make you say nuts whenever a word "nuts" comes from the MUD, or say
0130 "apples" when the word apples comes, but only if that word wasn't said by
0131 another player (to prevent the infinite loop).
0132 So, the script looks like this (the basic structure is omitted, only
0133 things inside while() are written:
0134 
0135   if (/say/)
0136   {
0137     # do nothing in this case
0138   }
0139   else
0140   {
0141     if (/apples/)
0142     {
0143       print "say apples\n";
0144     }
0145     if (/nuts/)
0146     {
0147       print "say nuts\n";
0148     }
0149   }
0150 
0151 Execution happens from top to the bottom, so the apples and nuts get
0152 looked for in this order, and both matching occurs regardless on their
0153 results. The print commands only get executed if their respective test was
0154 successful, obviously.
0155 
0156 New construction in this example is the "else" part. This gets executed if
0157 the condition tested in its associated "if" is false. In our case, it's
0158 false if the matching says no, it's not there.
0159 
0160 Since the {} block is required after if/else even if there's only one command,
0161 you won't have the problems with a misplaced 'else'.
0162 
0163 Another thing to remember is that if you want to put another condition immediately
0164 after else, you can't use if(){} else if (){}. You need to use either:
0165 
0166 if (foo) {x;}
0167 else {
0168   if (foo2)
0169   {x;}
0170 }
0171 
0172 or use the "elsif" statement:
0173 
0174 if (foo1) {x1;}
0175 elsif (foo2) {x2;}
0176 
0177 and so on...
0178 
0179 
0180 3.  Script termination
0181 ----------------------
0182 
0183 Tired of having to terminate your scripts by hand? Well, there's a better
0184 way - the exit statement.
0185 Example tells it all (only contents of while are included)
0186 
0187   if (/text1/)
0188   {
0189     print "oh yeah\n";
0190   }
0191   if (/this text quits the script/)
0192   {
0193     print "say script terminated\n";
0194     exit;
0195   }
0196 
0197 
0198 4. Variables
0199 ------------
0200 If you know variables in KMuddy, you know the standard variables in perl as
0201 well (but there's more than just those). So, instead of explaining variables
0202 again here, I refer you to the appropriate section of KMuddy Handbook.
0203 
0204 An important thing to remember is, that while the 'principles' of variables are
0205 the same for KMuddy variables and script variables, they are not the same.
0206 Variables defined in one script cannot be used in another script, and they
0207 cannot be used in KMuddy either. Variables defined in KMuddy cannot be used
0208 as you would use normal script variables. KMuddy's variables are accessible
0209 via the variable server, but there's no client part for it written yet that
0210 could be used in perl scripts - only C/C++ client exist as of now. So, until
0211 this changes, there's no way you could access KMuddy's variables off your
0212 scripts (only that you can send /set commands and friends).
0213 
0214 Okay, now that you understand what variables are, let's have a look at how to
0215 use them in perl.
0216 
0217 Example (whole script this time):
0218 
0219 #!/usr/bin/perl
0220 $ != 1;
0221 my $var = 0;
0222 print "search herbs\n";
0223 while (<STDIN>)
0224 {
0225   if (/^You have found some herbs.$/)
0226   {
0227     $var = 0;
0228     print "search herbs\n";
0229   }
0230   else
0231   {
0232     if (/^You have found nothing.$/)
0233     {
0234       $var = $var + 1;
0235     }
0236   }
0237   if ($var == 3) {
0238     exit;
0239   }
0240 }
0241 
0242 We're getting into more interesting stuff here, this script is already quite
0243 usable. :-) Explanation of some lines:
0244 
0245 my $var = 0;
0246 This statement defines a new variable called $var and assigns zero to it.
0247 It's best to declare all variables before the "while" cycle for now. After
0248 you become more skilled, you may want to declare them elsewhere, but there are
0249 some things that one must be aware of, so better stay with this method for now.
0250 
0251 $var = 0;
0252 Here we take an existing variable of $var and assign zero to it.
0253 (Note that this line (without the "my"), could have been used in the above
0254 described line as well, but it just isn't a good programming practice :)
0255 
0256 $var = $var + 1;
0257 Write this in your Math course and you're in for trouble. :) But it's okay
0258 here, since it's not an equation. What this line does is that it takes
0259 everything to the right of the "=" sign, evaluates it (in our case, it takes
0260 what's stored in $var and adds 1), then it stores the result in the variable
0261 on the left, $var in our case.
0262 Note that typing $var2 = $var + 1; won't modify the $var variable. :)
0263 
0264 if ($var == 3)
0265 Until now, we've only used the // operator (if (/blah/)), but the if statement
0266 can do much more. In this case, we are checking whether $var 'is equal to' 3. If
0267 it is, the command (or all commands inside these {}) will be executed, just as it
0268 was when the pattern matched.
0269 Note that we're comparing with "==", not plain "=", to avoid confusion, as the
0270 interpreter doesn't know whether we want to assign or to compare (okay, it's
0271 obvious in this case, but it wouldn't be in more complex expressions).
0272 In addition to "==", you can also use <, <=, >, >= and != for comparison.
0273 The last one '!=' means not-equal-to.
0274 
0275 Oh, in case you haven't figured it out by now, the script calls the
0276 "search herbs" command again and again, until it fails three times in a row.
0277 Remember this structure, you'll probably use it quite often :)
0278 
0279 6. Finite state machines
0280 ------------------------
0281 We are now getting to the topic that is of great importance for any bigger
0282 script(s). Without getting into too much detail, a finite state machine (FSM)
0283 is a device that has an input and a set of states. One of these states is
0284 initial, some of them (maybe none) are terminating states. What the machine
0285 does is that it reads input, and depending on what it gets and on the current
0286 state, it may change its state.
0287 Actually, our scripts are more powerful than this, because we can have
0288 multiple states and we also write some output, but this abstraction will do
0289 for now.
0290 
0291 A bigger example of how all this can be used:
0292 
0293 Let's assume that you want to write a script that converts all your money
0294 to more valuable currencies. Let the MUD's commands/output be as follows:
0295 
0296 money - command to view your money
0297 You are broke. - when ya have nothing
0298 You have 54 bronze, 17 silver, 20 gold and 4 platinum coins in your pockets.
0299   - output of the 'money' command when you aren't broke.
0300 convert XX bronze to silver - command convert bronze to silver.
0301 Next, say that the coin values are as follows:
0302 1 platinum = 5 gold
0303 1 gold = 6 silver
0304 1 silver = 10 bronze
0305 And assume that you need to provide exact numbers for conversion, as the
0306 bank doesn't return change.
0307 Also say that you don't want to exchange more than 100 coins a once,
0308 because you'd have to pay a fee for larger amounts.
0309 
0310 Of course, this is just an example that would probably work nowhere "as is",
0311 as every MUD is different. But it's sufficient to show you how things work.
0312 
0313 So, we'll have the following states:
0314 
0315 1. exchanging bronze -> silver
0316 2. exchanging silver -> gold
0317 3. exchanging gold -> platinum
0318 4. done
0319 
0320 Here we'll be proceeding in linear order, 1->2->3->4. More complex order
0321 is possible too, including unpredictable ones (changing state to more
0322 possible ones depending on what the MUD sends), depending on what you want.
0323 
0324 So, the script should look like this (couldn't test anywhere, so it may
0325     contain bugs...)
0326 
0327 #!/usr/bin/perl
0328 $| = 1;
0329 my $state = 1;
0330 my $amount;
0331 #start the loop
0332 print "money\n";
0333 while (<STDIN>)
0334 {
0335   #dot needs to be protected with \, remember? :)
0336   if (/^You are broke\.$/)
0337   {
0338     exit;
0339   }
0340   #exchanged something - start another round
0341   if (/^You exchange/)
0342   {
0343     print "money\n";
0344   }
0345   #only parse the lines with correct prefix/suffix
0346   if (/^You have .* coins in your pockets\.$/)
0347   {
0348     if ($state == 1)
0349     {
0350       #how much bronze? stores number in back-reference
0351       #note that we don't have to check if the line really contains the
0352       #correct prefix/suffix, as we did that already
0353       if (/(\d+) bronze/)
0354       {
0355         #closest smaller number divisible by 10
0356         $amount = $1 - $1 % 10;
0357         if ($amount > 100) { $amount = 100; }
0358         #too few coins, proceed with silver
0359         if ($amount == 0) {
0360           $state = 2;
0361         }
0362         else
0363         {
0364           print "exchange $amount bronze for silver\n";
0365         }
0366       }
0367       else {
0368         #none, proceed with silver
0369         $state = 2;
0370       }
0371     }
0372 #IMPORTANT: we'll immediately process first silver, if there's not anough
0373 #bronze (because $state is now 2). This behaviour may not always be correct.
0374 #In such case, use "else" statement to prevent this behaviour. e.g.
0375 #if ($state == 1) {...} else if ($state == 2) {...} else if ($state == 3) {...}
0376     if ($state == 2)
0377     {
0378       if (/(\d+) silver/)
0379       {
0380         $amount = $1 - $1 % 6;
0381         if ($amount > 100) { $amount = 100; }
0382         if ($amount == 0) {
0383           $state = 3;
0384         }
0385         else
0386         {
0387           print "exchange $amount silver for gold\n";
0388         }
0389       }
0390       else
0391       {
0392         $state = 3;
0393       }
0394     }
0395     if ($state == 3)
0396     {
0397       if (/(\d+) gold/)
0398       {
0399         $amount = $1 - $1 % 5;
0400         if ($amount > 100) { $amount = 100; }
0401         if ($amount == 0) {
0402           $state = 4;
0403         }
0404         else
0405         {
0406           print "exchange $amount gold for platinum\n";
0407         }
0408       }
0409       else {
0410         $state = 4;
0411       }
0412     }
0413     #state 4 - terminate the script
0414     if ($state == 4)
0415     {
0416       exit;
0417     }
0418   }
0419 }
0420 
0421 
0422 Kinda long, isn't it? :)  I believe it shouldn't be difficult to understand
0423 how this script works.
0424 
0425 7. Future reading
0426 -----------------
0427 After you gain some experience with all this stuff, you may want to go for
0428 some more advanced stuff. I would then recommend reading
0429 man perlintro
0430 which contains almost everything mentioned here, and much more.
0431 Then, you may want to go to
0432 man perl
0433 which contains a list of everything that could be of interest :)
0434 
0435 
0436 So, this is the end of this HOWTO. I hope you liked it, and wish you happy
0437 scripting :)
0438 
0439 
0440 / Tomas Mecir
0441   kmuddy@kmuddy.net
0442 
0443