├── .gitignore ├── toast-stunt-programmers-guide └── toaststunt-programmers-guide.md ├── CONTRIBUTORS.md ├── NOTES.md ├── patches ├── README.md └── fileio-1.5p3 │ └── README.md ├── LICENSE ├── TODO.md ├── tutorials ├── lambda-moo-background.md ├── src │ ├── lambda-moo-background-non-html5.html │ ├── hacking-lambda-moo-server-non-html5.html │ ├── lambda-moo-programming-tutorial-nodak-edu-non-html5.html │ ├── mud_el_tutorial.txt │ ├── zompost-moo-help-non-html5.html │ └── winding-duck-non-html5.html ├── hacking-lambda-moo-server.md ├── lambda-moo-nodak-edu.md ├── mud_moo_el_tutorial.md ├── zompost-moo-help.md └── winding-duck.md ├── CHANGELOG.md ├── code ├── LocalEditing.md └── CodeScanner.md └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_STORE 2 | .project 3 | -------------------------------------------------------------------------------- /toast-stunt-programmers-guide/toaststunt-programmers-guide.md: -------------------------------------------------------------------------------- 1 | This guide has moved into the [ToastStunt Documentation](https://github.com/lisdude/toaststunt-documentation) repository. 2 | -------------------------------------------------------------------------------- /CONTRIBUTORS.md: -------------------------------------------------------------------------------- 1 | # Contributors 2 | 3 | * [Brendan Butts](https://www.github.com/sevenecks) 4 | * [amnsia](https://github.com/amnsia) 5 | * [EvanKnowles](https://github.com/EvanKnowles) 6 | * [stevenjowens](https://github.com/stevenjowens) 7 | -------------------------------------------------------------------------------- /NOTES.md: -------------------------------------------------------------------------------- 1 | # Notes 2 | 1. This is a work in progress 3 | 2. The stylesheet for the HTML5 version can be swapped out with a normal bootstrap style sheet. 4 | 3. I've tried to format the functions in a more modern way (following PHP.net styles) 5 | 4. Estimated Hours on this repo: 150 6 | -------------------------------------------------------------------------------- /patches/README.md: -------------------------------------------------------------------------------- 1 | This is a collection of LambdaMOO server patches. I found many of them at https://sourceforge.net/p/lambdamoo/ but I have no faith that the original sourceforce lambdamoo will remain online. I am archiving here for the future. 2 | 3 | # Contributing 4 | 5 | If you have patches, throw a pull request in with them! 6 | -------------------------------------------------------------------------------- /patches/fileio-1.5p3/README.md: -------------------------------------------------------------------------------- 1 | # Info 2 | This adds file input / output to the MOO. The typical patch is 1.5p1 but this is an updated version which makes it return an actual error E_FILE instead of "E_FILE" among other fixes. 3 | 4 | ## Disclaimer 5 | I pulled this from a Github Gist by Todd Sundsted, the defacto maintainer of the FileIO extension. This version fixes some stuff. I had to hack this into my dev MOO manually because I already had fileio installed and so the patch wouldn't work. It's definitely possible to get it working with some manual patch work if you already have the existing patch. 6 | 7 | Some of it is just formatting changes but there is some stuff in versions.h or versions.c that I missed at first because I thought it was just a comment. Don't make that mistake. 8 | 9 | Original: https://gist.githubusercontent.com/toddsundsted/1126787/raw/99676d66a1ceacf4fade389d5c10c1f52393f44d/fileio-1.5p3.patch 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016-2021 Brendan Butts 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /TODO.md: -------------------------------------------------------------------------------- 1 | # TODO 2 | 3 | 1. ~~Finish full HTML5 conversiona~~ 4 | 2. ~~Finish applying proper styles to elements~~ 5 | 3. Create an index / TOC 6 | 4. ~~Link to relevant GitHub Repositories for LambdaMOO DBs~~ 7 | 5. Add more advanced programming examples 8 | 6. Add a section on benchmarking 9 | 7. ~~Add a section on LambdaMOO plugins / extensions~~ 10 | 8. ~~Current editing progress: DONE~~ 11 | 9. Confirm all function-is-member id's are set to function names 12 | 10. Confirm all Hx elements are properly set 13 | 11. ~~Convert all secondary files to markdown or HTML5.~~ 14 | 12. ~~Consider converting HTML5 moo guide to markdown for easy github viewing~~ 15 | 13. ~~Deploy guide to a website for easy HTML5 viewing~~ 16 | 14. ~~Finish converting all built-in functions to HTML5~~ 17 | 15. ~~Add way easy progrmaming examples http://snowfall.tripod.com/Way_Easy_Examples.latest.html#chalkboard~~ 18 | 16. Add all the server patches I can find 19 | 17. ~~Add more code examples~~ 20 | 18. Cross link to forked GammmaMOO and LambdaMOO and Stunt servers 21 | 19. Add installation guides 22 | 20. Add where to start guide for servers/dbs 23 | 21. Add dbs/cores and guide to them 24 | 22. Add History of LambdaMOO (link to that prototype video history I found, discuss prototype langages, Xerox Parc) 25 | 23. document how to best write moo comments 26 | 24. o in {list} and ismember() being the same and also O(n) 27 | -------------------------------------------------------------------------------- /tutorials/lambda-moo-background.md: -------------------------------------------------------------------------------- 1 | 2 | # Background 3 | 4 | ## Environment 5 | 6 | C++ is a programming language. There is no seperate operating system, 7 | or user environment associated with C++. For LambdaMOO, like Java, there 8 | is a blurring of the distinction. LambdaMOO refers to the system running 9 | (in this case) on a UNIX machine, using an interpreted language normally referred 10 | to as either "The LambdaMOO programming language" or LambdaMOO. Is very 11 | much like the problem of discussing Java the language, Java the virtual 12 | machine, or Java the operating environment. 13 | 14 | For the curious, I myself have no idea what the internal representation 15 | of the code for the LambdaMOO system is. All I prove from my current knowledge 16 | is that there are functions built into the system for changing the code 17 | used in verbs, and for changing every attribute of objects in the 18 | system, including changing the inheritence tree. 19 | 20 | ## Classes and Objects 21 | 22 | In C++, the difference between a class and an object is a concrete one. 23 | Objects are instances of classes, and in general one manipulates objects. 24 | In the LambaMOO system, there is very little (if any) distinction between 25 | a class and an object. It is possible to create an object which inherits 26 | from another object, add properties and verbs to that object, and then create 27 | other objects which inherit from that newly created object. Essentially, 28 | LambdaMOO allows for the dynamic creation and changing of classes, rendering 29 | them indistinguishable from objects. 30 | 31 | ## Functions and Verbs 32 | 33 | In C++, an object is said to have member functions. In LambdaMOO, an object 34 | is said to have verbs. As a side effect of the interactive nature of 35 | LambdaMOO, verbs can be invoked either by the built-in parser, or by 36 | other verbs. LambdaMOO's interpreter does have certain functions, which 37 | can be called inside of verbs, but these functions have been compiled 38 | directly into the interpreter. 39 | 40 | ## new and delete 41 | 42 | C++ uses the new and delete operators for dynamically creating objects 43 | during program execution. The equivalents for LambdaMOO are the built-in 44 | functions create() and delete(). 45 | 46 | ## Types 47 | 48 | C++ has static type checking. With the partial exception of virtual functions 49 | (since the compiler still checks that the variables can be converted), all variable 50 | variables and members are constrained in type at compile time. LambdaMOO, as 51 | an interpreted system, does run time type checking. As such, it also has 52 | provisions for checking the type of a variable. 53 | 54 | ## Using variables 55 | In C++, a variable cannot be declared after it is used. Or rather, in C++ 56 | a variable in scope before it is used, either declared inside a function, 57 | as a member of a class, or otherwise in scope to be used. For LambdaMOO, 58 | variables aren't declared. Variables which haven't been assigned to essentially 59 | have no type, and as such any attempt (other than assignment) will result in 60 | the expression evaluating to an error (variable type ERR). 61 | 62 | In C++, global variables are simply variables declared outside of all other 63 | scopes. In LambdaMOO, the properties of the System Object (Object \#0) are 64 | the closest thing to truly global variables, and special access rights are 65 | required to create or change those values. 66 | 67 | ### Variable Scope 68 | 69 | In C++, variables have lexical scope, meaning it is possible to use 70 | variables which have not been declared inside a function, but which an expression 71 | can "see". By the scoping rules of C++, it is possible to use the same 72 | notation, or lack thereof, to refer to a variable which is local to function, 73 | a class data member, or a global variable, a named contant, et al. 74 | 75 | For LambaMOO, the scoping rules are much simpler. Or rather, the notation 76 | is much more distinct. $GlobalVariable is a global variable named 77 | GlobalVariable. this:MemberVariable is a member variable of the object 78 | 'this', and OtherVariable is a local variable named OtherVariable. 79 | Member variables follow the reasonable approach to maintaining their 80 | values, namely that the values of properties inherited from other objects 81 | becomes distinct upon creating the derived object. 82 | 83 | If you have experienced a language like Smalltalk, LambdaMOO doesn't seem much 84 | like an object oriented system. In LambdaMOO, you have objects, and then you 85 | have primitive types. Objects persist, everything else either gets stored on 86 | an object or else eventually gets garbage collected into oblivion. 87 | Aside from this issue, LambdaMOO is just as object oriented as C++, or Java. 88 | 89 | # The source code for this file was taken from [http://lions.cs.ndsu.nodak.edu/~vender/LambdaMOO/background.html][0] and is included in this repository for posterity. It is not HTML5\. 90 | 91 | [0]: http://lions.cs.ndsu.nodak.edu/~vender/LambdaMOO/background.html 92 | -------------------------------------------------------------------------------- /tutorials/src/lambda-moo-background-non-html5.html: -------------------------------------------------------------------------------- 1 | 2 | Lamba MOO Programming Tutorial--Background 3 | 4 |

The source code for this file was taken from http://lions.cs.ndsu.nodak.edu/~vender/LambdaMOO/background.html and is included in this repository for posterity. It is not HTML5.

5 |

Background

6 |

Environment

7 |

C++ is a programming language. There is no seperate operating system, 8 | or user environment associated with C++. For LambdaMOO, like Java, there 9 | is a blurring of the distinction. LambdaMOO refers to the system running 10 | (in this case) on a UNIX machine, using an interpreted language normally referred 11 | to as either "The LambdaMOO programming language" or LambdaMOO. Is very 12 | much like the problem of discussing Java the language, Java the virtual 13 | machine, or Java the operating environment.

14 |

For the curious, I myself have no idea what the internal representation 15 | of the code for the LambdaMOO system is. All I prove from my current knowledge 16 | is that there are functions built into the system for changing the code 17 | used in verbs, and for changing every attribute of objects in the 18 | system, including changing the inheritence tree.

19 |

Classes and Objects

20 |

In C++, the difference between a class and an object is a concrete one. 21 | Objects are instances of classes, and in general one manipulates objects. 22 | In the LambaMOO system, there is very little (if any) distinction between 23 | a class and an object. It is possible to create an object which inherits 24 | from another object, add properties and verbs to that object, and then create 25 | other objects which inherit from that newly created object. Essentially, 26 | LambdaMOO allows for the dynamic creation and changing of classes, rendering 27 | them indistinguishable from objects. 28 |

Functions and Verbs

29 |

In C++, an object is said to have member functions. In LambdaMOO, an object 30 | is said to have verbs. As a side effect of the interactive nature of 31 | LambdaMOO, verbs can be invoked either by the built-in parser, or by 32 | other verbs. LambdaMOO's interpreter does have certain functions, which 33 | can be called inside of verbs, but these functions have been compiled 34 | directly into the interpreter.

35 |

new and delete

36 |

C++ uses the new and delete operators for dynamically creating objects 37 | during program execution. The equivalents for LambdaMOO are the built-in 38 | functions create() and delete().

39 |

Types

40 |

C++ has static type checking. With the partial exception of virtual functions 41 | (since the compiler still checks that the variables can be converted), all variable 42 | variables and members are constrained in type at compile time. LambdaMOO, as 43 | an interpreted system, does run time type checking. As such, it also has 44 | provisions for checking the type of a variable.

45 |

Using variables

46 | In C++, a variable cannot be declared after it is used. Or rather, in C++ 47 | a variable in scope before it is used, either declared inside a function, 48 | as a member of a class, or otherwise in scope to be used. For LambdaMOO, 49 | variables aren't declared. Variables which haven't been assigned to essentially 50 | have no type, and as such any attempt (other than assignment) will result in 51 | the expression evaluating to an error (variable type ERR).

52 | In C++, global variables are simply variables declared outside of all other 53 | scopes. In LambdaMOO, the properties of the System Object (Object #0) are 54 | the closest thing to truly global variables, and special access rights are 55 | required to create or change those values. 56 |

Variable Scope

57 |

In C++, variables have lexical scope, meaning it is possible to use 58 | variables which have not been declared inside a function, but which an expression 59 | can "see". By the scoping rules of C++, it is possible to use the same 60 | notation, or lack thereof, to refer to a variable which is local to function, 61 | a class data member, or a global variable, a named contant, et al.

62 |

For LambaMOO, the scoping rules are much simpler. Or rather, the notation 63 | is much more distinct. $GlobalVariable is a global variable named 64 | GlobalVariable. this:MemberVariable is a member variable of the object 65 | 'this', and OtherVariable is a local variable named OtherVariable. 66 | Member variables follow the reasonable approach to maintaining their 67 | values, namely that the values of properties inherited from other objects 68 | becomes distinct upon creating the derived object. 69 |

70 | If you have experienced a language like Smalltalk, LambdaMOO doesn't seem much 71 | like an object oriented system. In LambdaMOO, you have objects, and then you 72 | have primitive types. Objects persist, everything else either gets stored on 73 | an object or else eventually gets garbage collected into oblivion. 74 | Aside from this issue, LambdaMOO is just as object oriented as C++, or Java. 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog for LambdaMOO Programming Repository 2 | 3 | ## [1.1.12] - 2022.01/14 4:04PM 4 | * fixed some typos in the guide 5 | * Moved ToastStunt Programmers Guide to https://github.com/lisdude/toaststunt-documentation 6 | 7 | ## [1.1.11] - 2022.01.08 2:29PM 8 | * Updated ToastStunt Programmers Guide (see toast-stunt-programmers-guide/CHANGELOG.md for more info) 9 | * Moved ToastStunt changelog to new directory 10 | * Cleaned up Changelog 11 | * Updated README to point to new guide location 12 | 13 | ## [1.1.10] - 2022.01.07 12:22PM 14 | * Updated ToastStunt Programmers Guide (see toast-stunt-programmers-guide/CHANGELOG.md for more info) 15 | 16 | ## [1.1.9] - 2022.01.06 9:49PM 17 | * Updated ToastStunt Programmers Guide (see toast-stunt-programmers-guide/CHANGELOG.md for more info) 18 | 19 | ## [1.1.8] - 2022.01.06 5:43PM 20 | * Updated ToastStunt Programmers Guide (see toast-stunt-programmers-guide/CHANGELOG.md for more info) 21 | 22 | ## [1.1.7] - 2022.01.05 8:20pm 23 | * Added SindomeScheduler to Moo Code directory 24 | * Updated ToastStunt Programmers Guide (see toast-stunt-programmers-guide/CHANGELOG.md for more info) 25 | 26 | ## [1.1.6] - 2022.01.05 12:10pm 27 | * Updated ToastStunt Programmers Guide (see toast-stunt-programmers-guide/CHANGELOG.md for more info) 28 | 29 | ## [1.1.5] - 2022.01.04 2:46PM 30 | * Updated ToastStunt Programmers Guide (see toast-stunt-programmers-guide/CHANGELOG.md for more info) 31 | 32 | ## [1.1.4] - 2022.01.03 10:01PM 33 | * Updated ToastStunt Programmers Guide (see toast-stunt-programmers-guide/CHANGELOG.md for more info) 34 | 35 | ## [1.1.3] - 2022.01.03 12:22PM 36 | * Updated ToastStunt Programmers Guide (see toast-stunt-programmers-guide/CHANGELOG.md for more info) 37 | 38 | ## [1.1.2] - 2022.01.02 2:26PM 39 | * Updated ToastStunt Programmers Guide (see toast-stunt-programmers-guide/CHANGELOG.md for more info) 40 | 41 | ## [1.1.1] - 2022.01.01 10:17pm 42 | * Updated ToastStunt Programmers Guide (see toast-stunt-programmers-guide/CHANGELOG.md for more info) 43 | * Updated README.md 44 | * Updated ChangeLog 45 | * Removed a few files that were WIP for the ToastStunt manual 46 | 47 | ## [1.1] - 2021.04.20 4:29pm 48 | * Added new fork on documentation for ToastStunt 49 | 50 | ## [1.0.2A] - 2020.07.20 6:29pm 51 | * Merged in a PR from stevenjowens that fixed formatting in their guide 52 | 53 | ## [1.0.2] - 2019.06.02 6:14PM 54 | * Fixed broken link to CHANGELOG.md 55 | 56 | ## [1.0.1] - 2019.06.02 6:09PM 57 | * Fixed typo 58 | 59 | ## [1.0] - 2019.06.02 1:25PM 60 | * Finished editing the Updated lambdamoo programming manual. All HTML converted, all errors resolved. Releasing as initial major version. 61 | * Rendered new version of updated moo programming manual in markdown 62 | * Updated README to include example of how to contribute 63 | * Updated TODOs 64 | * Updated years in license 65 | 66 | ## [0.14] - 2019.01.08 10:31PM 67 | * Added code scanner to useful moo code section 68 | * Updated readme 69 | * Other updates happened prior to this, I forgot to update the change log. 70 | 71 | ## [0.13] - 2017.07.19 6:24PM 72 | * Updated the CSS on the HTML5 version of the updated moo programming guide 73 | * Added link to this repo to the html5 version 74 | * Updated the remote Sindome MOO-manual.html reference to the new version 75 | 76 | ## [0.12] - 2018.07.10 2:35PM 77 | * Added server setup section to readme 78 | * Added automated local environment setup (Stunt & LambdaCore) section to readme 79 | 80 | ## [0.11] - 2018.06.21 9:54AM 81 | * Fixed local instruction code 82 | 83 | ## [0.10] - 2018.06.21 9:46AM 84 | * Removed goblin extension at authors request 85 | 86 | ## [0.9] - 2018.06.20 10:53PM 87 | * Fixed markdown links 88 | * Added code to readme 89 | * Added patches to readme 90 | * Added webclient section to readme 91 | * Added public webclient section to readme 92 | 93 | ## [0.8] - 2018.06.20 10:38PM 94 | * Added Patche for goblin extension (probably only works on gammamoo) 95 | * Added patch for fileio-1.5p3 (Todd's version) 96 | * Added 'Code' directory and a code example for getting local editing (in a pop up) working form Dome-Client 97 | 98 | ## [0.7] - 2018.03.16 3:12PM 99 | * Added genesismud tutorial by Puff 100 | * Added emacs mud.el tutorial by Puff 101 | * Updated README 102 | 103 | ## [0.6] - 2018.03.07 2:49PM 104 | * Updated README with markdown conversion instructions 105 | * Updated Steven Owen guide with new version (md and html) 106 | 107 | ## [0.5] - 2018.01.31 4:30PM 108 | 109 | ### Updated 110 | * Naming of various files 111 | * Updated License year 112 | 113 | ### Added 114 | * Markdown versions of all files 115 | * HTML for yibs was run through an HTML tidier 116 | * removed from yibs tutorial as it was breaking markdown conversion 117 | * Updates to README (index, better information on repo) 118 | * Repository renamed to lambda-moo-programming 119 | * Added directory structure with tutorials and tutorials/src 120 | * Organized files 121 | 122 | ## [0.4] - 2018.01.26 5:28PM 123 | 124 | ### Added 125 | * Finished adding all functions to moo.html 126 | 127 | ## [0.3] - 2018.01.25 1:56PM 128 | 129 | ### Added 130 | * Added dark-sleep tutorial 131 | * Added hacking lambda moo server tutorial 132 | * Added LambdaMOO Background doc 133 | * Added Way Easy MOO Programming Guide 134 | * Added Winding Duck tutorial 135 | * Added Yib's pet rock tutorial 136 | * Added Zompost moo help doc 137 | * Updated README 138 | 139 | ## [0.2] - 2018.01.22 5:00PM 140 | 141 | ### Added 142 | * lisdude web resource 143 | * changelog.md 144 | * Changed LambdaMOO sourcecode github reference to SevenEcks (includes patches and stuff) 145 | * Added MOOTalk Mailing list to resources 146 | * Replaced two spaces after periods with one space after periods (736 occurances) 147 | * Dome Client resources link 148 | * New Note on MOO Clients 149 | * Added new notes on extensions, and extensions throwing their own errors 150 | 151 | ## [0.1] - 2016.09.13 5:35PM 152 | 153 | ### Added 154 | * Initial Release 155 | * HTML5 Conversion 156 | * Table of Contents 157 | * Additional Comments 158 | * Clarifications 159 | * Updated Resources 160 | * Code Sdamples 161 | -------------------------------------------------------------------------------- /tutorials/hacking-lambda-moo-server.md: -------------------------------------------------------------------------------- 1 | Okay, I got annoyed trying to write parts of the automatic core updater 2 | utility, so I started digging through the server code and have discovered 3 | some things: 4 | 5 | # What happens when a connection is made to the LambdaMOO server 6 | 7 | The another program connects to a port the server is listening on, 8 | is accepts the connection after some minimal amount of error checking 9 | or black list checking, and then creates a task with the following 10 | associates: a unique identifier for that connection (a negative object 11 | reference), and the object responsible for responding to that port. 12 | In the default case, this object will be \#0 (the system object). 13 | To determine what objects are responsible for which ports, the built in 14 | function listeners() can be used. 15 | 16 | After creating the previously mentioned association and task, the server pushes 17 | an empty string into the input queue of that task. The interesting thing 18 | at this point is that this empty string is what causes the welcome message 19 | in the LambdaCore database to be generated. What happens is this 20 | 21 | * Server calls the managing objects "do\_login\_command" with the 22 | input string 23 | * * The input string fails to match any known verbs 24 | * do\_login\_command defaults to a verb named "?" to handle the input 25 | * * "?" displays the contents of $login.welcome\_message, along with a command 26 | summary 27 | * If you don't believe this, start up a copy of your favorite MOO core, connect 28 | as a wizard, and @rmprop $login.welcome\_message and then login in again. 29 | You should receive a fairly bizarre trace back error. 30 | 31 | In the normal flow of events, one of the verbs called by "do\_login\_command" 32 | will return an object reference, or end the connection by calling boot\_player. 33 | Assuming that an object reference is returned, that task becomes promoted 34 | to command task, and the rules for dealing with user input changes radically. 35 | 36 | For input tasks, the flow of execution is as follows: 37 | 38 | * parse input into basic tokens delimited by spaces 39 | * * Check for input and output prefixes 40 | * If input or output prefixes aren't detected, try executing 41 | "do\_command" on the object associated with that connection from before 42 | * If "do\_command" doesn't exist, or returns a false value, perform the 43 | normal verb matching proceedure and execute the located verb as an 44 | input task. 45 | * If all else fails, call "huh", or as a last resort report 46 | "I didn't understand that." to the connection 47 | 48 | 1\. It is possible to completely redefine how the server deals with 49 | user input by defining a "do\_command" verb on the object the player 50 | has logged into on. do\_command is called before the normal processing 51 | of input lines into psuedo-english phrases. However, doing macro 52 | processing (as Mr. Fish was wondering about) would require doing all of the 53 | normal text processing functions in LambdaMOO code, since it is not possible 54 | since there are no destructive list maninpulation functions. 55 | 56 | Each port the server is listening on is associated with an object. The 57 | port bound by the server on start up is associated with object \#0 58 | (The system object). do\_command gets passed the input string after 59 | it has been broken up into white space delimited tokens. So, if for example 60 | \#0:do\_command looks like 61 | 62 | 63 | 64 | player:tell(@args); 65 | if (length(args)==3) 66 | return 1; 67 | else 68 | return 0; 69 | endif; 70 | 71 | 72 | 73 | then a player connected through \#0 who typed 74 | 75 | 76 | 77 | l at me 78 | 79 | 80 | 81 | would see the text 82 | 83 | 84 | 85 | lame 86 | 87 | 88 | 89 | echoed back to them, and that command would never receive normal processing. 90 | Interestingly enough, the command ".pr\*ogram" in the form 91 | 92 | 93 | .program Object:verb 94 | 95 | 96 | is hard coded into the server for programmer players before the check for 97 | "do\_command". Interestingly enough, do\_command isn't defined for either 98 | the Sell Game, or Planet Oit. 99 | 100 | 2\. Server options 101 | 102 | Yes, the server options to work. Essentially, the documentation is unclear 103 | about where the properties are supposed to be found. The server code 104 | expects them to be on $server\_options, or rather that $server\_options is 105 | expected to be an object reference which defines the properties. 106 | 107 | 3\. Forking tasks 108 | 109 | Granted that it looks like the server code for dealing with queued tasks 110 | is fairly complicated, it doesn't look like the overhead for forking a task 111 | is too much more that the overhead for invoking a verb on an object. 112 | 113 | 4\. Server startup scripts 114 | 115 | The verb \#0:server\_started is run when the server starts up, after the 116 | database is loaded and the network code initialized. This would be the 117 | place to put things like the http server initializations for the Sell Game 118 | and Planet Oit. 119 | 120 | 5\. I've been looking through the server code for the MudOS LPC style 121 | mud server, and managed to locate the main loop of the server. My major 122 | reaction to the MudOS code is that it makes the LambdaMOO server code 123 | (even the confusing parts) look blindingly straight forward. Essentially, 124 | LPC muds implement a large amount of swapping objects out of memory, 125 | calling reset functions every so often, and maintaining a circular list of 126 | objects to call a heart\_beat function periodically, all while trying to take 127 | up more disk space than memory. Of course, LambdaMOO servers try to do 128 | the exact opposite by trying to take up more virtual memory than disk space. 129 | In any event, I honestly don't see why (if periodic resets and heart\_beat 130 | functions were desired) these two functions could be implemented by carefully 131 | written LambdaMOO code. 132 | 133 | 6\. I still haven't found a good way of introducing a good destructive list 134 | append function into the Lambda server. At the moment, it appears that 135 | this would involve adding a built in function, and as such the naming 136 | convention would be 137 | static package bf\_ItsName(...) 138 | and then call 139 | register\_function("ItsName",?,?,bf\_ItsName,ArgType,ArgType,ArgType,...); 140 | The notation in functions.c says 141 | 142 | 143 | /***************************************************************************** 144 | * This is the table of procedures that register MOO built-in functions. To 145 | * add new built-in functions to the server, add to the list below the name of 146 | * a C function that will register your new MOO built-ins; your C function will 147 | * be called exactly once, during server initialization. Also add a 148 | * declaration of that C function to `bf_register.h' and add the necessary .c 149 | * files to the `CSRCS' line in the Makefile. 150 | ****************************************************************************/ 151 | 152 | 153 | Which is interesting, and I'll have to give it a try soon. 154 | 155 | Conceivably, adding file io would not be necessarily very involved, once 156 | the appropriate interfaces were defined. For adding destructive set 157 | maninpulations, all that would be necessary would be to figure out how to 158 | dissasociate the previous copy of the variable from the reference counting 159 | scheme, modify it, and then reintroduce it to the ref counters. 160 | 161 | # The source code for this file was taken from [http://lions.cs.ndsu.nodak.edu/~vender/LambdaMOO/hacking.html][0] and is stored in this repository for posterity. It is not HTML5\. 162 | 163 | 164 | [0]: http://lions.cs.ndsu.nodak.edu/~vender/LambdaMOO/hacking.html 165 | -------------------------------------------------------------------------------- /tutorials/src/hacking-lambda-moo-server-non-html5.html: -------------------------------------------------------------------------------- 1 | 2 | Hacking LambdaMOO 3 |

The source code for this file was taken from http://lions.cs.ndsu.nodak.edu/~vender/LambdaMOO/hacking.html and is stored in this repository for posterity. It is not HTML5.

4 |

Okay, I got annoyed trying to write parts of the automatic core updater 5 | utility, so I started digging through the server code and have discovered 6 | some things: 7 |

8 |

What happens when a connection is made to the LambdaMOO server

9 |

The another program connects to a port the server is listening on, 10 | is accepts the connection after some minimal amount of error checking 11 | or black list checking, and then creates a task with the following 12 | associates: a unique identifier for that connection (a negative object 13 | reference), and the object responsible for responding to that port. 14 | In the default case, this object will be #0 (the system object). 15 | To determine what objects are responsible for which ports, the built in 16 | function listeners() can be used. 17 |

18 | After creating the previously mentioned association and task, the server pushes 19 | an empty string into the input queue of that task. The interesting thing 20 | at this point is that this empty string is what causes the welcome message 21 | in the LambdaCore database to be generated. What happens is this

22 |

29 | If you don't believe this, start up a copy of your favorite MOO core, connect 30 | as a wizard, and @rmprop $login.welcome_message and then login in again. 31 | You should receive a fairly bizarre trace back error. 32 |

33 | In the normal flow of events, one of the verbs called by "do_login_command" 34 | will return an object reference, or end the connection by calling boot_player. 35 | Assuming that an object reference is returned, that task becomes promoted 36 | to command task, and the rules for dealing with user input changes radically. 37 |

38 | For input tasks, the flow of execution is as follows: 39 |

49 | 50 |

1. It is possible to completely redefine how the server deals with 51 | user input by defining a "do_command" verb on the object the player 52 | has logged into on. do_command is called before the normal processing 53 | of input lines into psuedo-english phrases. However, doing macro 54 | processing (as Mr. Fish was wondering about) would require doing all of the 55 | normal text processing functions in LambdaMOO code, since it is not possible 56 | since there are no destructive list maninpulation functions. 57 |

58 | Each port the server is listening on is associated with an object. The 59 | port bound by the server on start up is associated with object #0 60 | (The system object). do_command gets passed the input string after 61 | it has been broken up into white space delimited tokens. So, if for example 62 | #0:do_command looks like 63 |

 64 | 
 65 |   player:tell(@args);
 66 |   if (length(args)==3)
 67 |     return 1;
 68 |   else
 69 |     return 0;
 70 |   endif;
 71 | 
 72 | 
73 | then a player connected through #0 who typed 74 |
 75 | 
 76 |  l at me
 77 | 
 78 | 
79 | would see the text 80 |
 81 | 
 82 |  lame
 83 | 
 84 | 
85 | echoed back to them, and that command would never receive normal processing. 86 | Interestingly enough, the command ".pr*ogram" in the form 87 |
 88 |  .program Object:verb
 89 | 
90 | is hard coded into the server for programmer players before the check for 91 | "do_command". Interestingly enough, do_command isn't defined for either 92 | the Sell Game, or Planet Oit. 93 |

94 | 2. Server options 95 |

96 | Yes, the server options to work. Essentially, the documentation is unclear 97 | about where the properties are supposed to be found. The server code 98 | expects them to be on $server_options, or rather that $server_options is 99 | expected to be an object reference which defines the properties. 100 |

101 | 3. Forking tasks 102 |

103 | Granted that it looks like the server code for dealing with queued tasks 104 | is fairly complicated, it doesn't look like the overhead for forking a task 105 | is too much more that the overhead for invoking a verb on an object. 106 |

107 | 4. Server startup scripts 108 |

109 | The verb #0:server_started is run when the server starts up, after the 110 | database is loaded and the network code initialized. This would be the 111 | place to put things like the http server initializations for the Sell Game 112 | and Planet Oit. 113 |

114 | 5. I've been looking through the server code for the MudOS LPC style 115 | mud server, and managed to locate the main loop of the server. My major 116 | reaction to the MudOS code is that it makes the LambdaMOO server code 117 | (even the confusing parts) look blindingly straight forward. Essentially, 118 | LPC muds implement a large amount of swapping objects out of memory, 119 | calling reset functions every so often, and maintaining a circular list of 120 | objects to call a heart_beat function periodically, all while trying to take 121 | up more disk space than memory. Of course, LambdaMOO servers try to do 122 | the exact opposite by trying to take up more virtual memory than disk space. 123 | In any event, I honestly don't see why (if periodic resets and heart_beat 124 | functions were desired) these two functions could be implemented by carefully 125 | written LambdaMOO code. 126 |

127 | 6. I still haven't found a good way of introducing a good destructive list 128 | append function into the Lambda server. At the moment, it appears that 129 | this would involve adding a built in function, and as such the naming 130 | convention would be 131 | static package bf_ItsName(...) 132 | and then call 133 | register_function("ItsName",?,?,bf_ItsName,ArgType,ArgType,ArgType,...); 134 | The notation in functions.c says 135 |

136 | /*****************************************************************************
137 |  * This is the table of procedures that register MOO built-in functions.  To
138 |  * add new built-in functions to the server, add to the list below the name of
139 |  * a C function that will register your new MOO built-ins; your C function will
140 |  * be called exactly once, during server initialization.  Also add a
141 |  * declaration of that C function to `bf_register.h' and add the necessary .c
142 |  * files to the `CSRCS' line in the Makefile.
143 |  ****************************************************************************/
144 | 
145 | Which is interesting, and I'll have to give it a try soon. 146 |

147 | Conceivably, adding file io would not be necessarily very involved, once 148 | the appropriate interfaces were defined. For adding destructive set 149 | maninpulations, all that would be necessary would be to figure out how to 150 | dissasociate the previous copy of the variable from the reference counting 151 | scheme, modify it, and then reintroduce it to the ref counters. 152 |

153 |

154 | 155 | -------------------------------------------------------------------------------- /code/LocalEditing.md: -------------------------------------------------------------------------------- 1 | # Local Editing (in browser or VMOO or other supported client) 2 | 3 | ## Update 4 | 5 | If you are using a ToastCore or even LambdaCore based database, you may be able to just do `@edit-options +local` and have local editing working without needing to do really any of the stuff in this doc. 6 | 7 | ## Contributing 8 | 9 | The code scanner covers some of our best practices for MOO coding on [Sindome](https://www.sindome.org/). If you have your own that you think would be useful to the community please feel free to put in a pull request! 10 | 11 | ## Requirements 12 | This code was tested on stock LambdaMOO 1.8.1 running the lastest LambdaCore.db. If your DB is based off of that it should work with no changes needed. 13 | 14 | This code will allow you to support Local Editing via the Dome-Client webclient, and also probably VMOO and other clients that support local editing (though it may take some massaging). 15 | 16 | ## Installation 17 | 18 | You may need to change the obj# from #480 to whatever yours is when you create the web client package. This should be a generic package, but it doesn't really need to be. Any object will work. 19 | 20 | This code actually works best if it's on $verb_editor but I've broken it out into it's own package and copied a lot of the $verb_editor verbs that were needed over to the new package, since some of the stock LambdaCore verb editor verbs require $verb_editor to be the caller, and otherwise you'd have to modify $verb_editor which you might not have permission to do. 21 | 22 | When all of this code is in place you should be able to use the dome-client (http://pubclient.sindome.org/) to edit verbs in a modal window use @edit obj:verb 23 | 24 | This can also be modified to support editing properties if you want. 25 | 26 | ``` 27 | ;;#480.("key") = 0 28 | ;;#480.("aliases") = {"Webclient Package"} 29 | ;;#480.("description") = "This is a placeholder parent for all the $..._utils packages, to more easily find them and manipulate them. At present this object defines no useful verbs or properties. (Filfre.)" 30 | ;;#480.("object_size") = {0, 0} 31 | 32 | @args #480:"local_editing_info" this none this 33 | @program #480:local_editing_info 34 | {object, vname, code} = args; 35 | if (typeof(vname) == LIST) 36 | vargs = tostr(" ", vname[2], " ", $code_utils:short_prep(vname[3]), " ", vname[4]); 37 | vname = vname[1]; 38 | else 39 | vargs = ""; 40 | endif 41 | name = tostr(object:name(), ":", vname); 42 | "... so the next 2 lines are actually wrong, since verb_info won't"; 43 | "... necessarily retrieve the correct verb if we have more than one"; 44 | "... matching the given same name; anyway, if parse_invoke understood vname,"; 45 | "... so will @program. I suspect these were put here because in the"; 46 | "... old scheme of things, vname was always a number."; 47 | "vname = strsub($string_utils:explode(verb_info(object, vname)[3])[1], \"*\", \"\")"; 48 | "vargs = verb_args(object, vname)"; 49 | ""; 50 | return {name, code, tostr("@program ", object, ":", vname, vargs)}; 51 | "[Modified by Slither (#479) Wed Jun 20 19:37:16 2018 WEST]."; 52 | . 53 | 54 | @args #480:"invoke" this none this 55 | @program #480:invoke 56 | ":invoke(...)"; 57 | "to find out what arguments this verb expects,"; 58 | "see this editor's parse_invoke verb."; 59 | new = args[1]; 60 | spec = this:parse_invoke(@args); 61 | if (typeof(spec) == LIST) 62 | if ($object_utils:has_verb(this, "local_editing_info") && (info = this:local_editing_info(@spec))) 63 | player:tell("Invoking local editor"); 64 | this:invoke_local_editor(@info); 65 | else 66 | player:tell("This is for editing in a web client, if you don't wanna do that, use a different verb."); 67 | endif 68 | endif 69 | "[Modified by Slither (#479) Wed Jun 20 20:03:23 2018 WEST]."; 70 | . 71 | 72 | @args #480:"parse_invoke" this none this 73 | @program #480:parse_invoke 74 | ":parse_invoke(string,v)"; 75 | " string is the commandline string to parse to obtain the obj:verb to edit"; 76 | " v is the actual command verb used to invoke the editor"; 77 | " => {object, verbname, verb_code} or error"; 78 | vref = $string_utils:words(args[1]); 79 | if ((!vref) || (!(spec = $code_utils:parse_verbref(vref[1])))) 80 | player:tell("Usage: ", args[2], " object:verb"); 81 | return; 82 | endif 83 | if (argspec = listdelete(vref, 1)) 84 | if (typeof(pas = $code_utils:parse_argspec(@argspec)) == LIST) 85 | if (pas[2]) 86 | player:tell("I don't understand \"", $string_utils:from_list(pas[2], " "), "\""); 87 | return; 88 | endif 89 | argspec = {@pas[1], "none", "none"}[1..3]; 90 | argspec[2] = $code_utils:full_prep(argspec[2]) || argspec[2]; 91 | else 92 | player:tell(pas); 93 | return; 94 | endif 95 | endif 96 | player:tell(toliteral(argspec)); 97 | if (!$command_utils:object_match_failed(object = player:my_match_object(spec[1], $verb_editor:get_room(player)), spec[1])) 98 | vnum = $code_utils:find_verb_named(object, vname = spec[2]); 99 | if (argspec) 100 | while (vnum && (verb_args(object, vnum) != argspec)) 101 | vnum = $code_utils:find_verb_named(object, vname, vnum + 1); 102 | endwhile 103 | endif 104 | if (vnum) 105 | code = this:fetch_verb_code(object, vnum); 106 | else 107 | code = E_VERBNF; 108 | endif 109 | if (typeof(code) == ERR) 110 | player:tell((code != E_VERBNF) ? code | "That object does not define that verb", argspec ? " with those args." | "."); 111 | return code; 112 | else 113 | return {object, argspec ? {vname, @argspec} | vname, code}; 114 | endif 115 | endif 116 | return 0; 117 | "[Modified by Slither (#479) Wed Jun 20 19:58:14 2018 WEST]."; 118 | . 119 | 120 | @args #480:"fetch_verb_code" this none this 121 | @program #480:fetch_verb_code 122 | set_task_perms(player); 123 | return `verb_code(args[1], args[2], !player:edit_option("no_parens")) ! ANY'; 124 | "[Modified by Slither (#479) Wed Jun 20 19:57:52 2018 WEST]."; 125 | . 126 | 127 | @args #480:"invoke_local_editor" this none this 128 | @program #480:invoke_local_editor 129 | ":invoke_local_editor(name, text, upload)"; 130 | "Spits out the magic text that invokes the local editor in the player's client."; 131 | "NAME is a good human-readable name for the local editor to use for this particular piece of text."; 132 | "TEXT is a string or list of strings, the initial body of the text being edited."; 133 | "UPLOAD, a string, is a MOO command that the local editor can use to save the text when the user is done editing. The local editor is going to send that command on a line by itself, followed by the new text lines, followed by a line containing only `.'. The UPLOAD command should therefore call $command_utils:read_lines() to get the new text as a list of strings."; 134 | if (caller != this) 135 | return; 136 | endif 137 | {name, text, upload} = args; 138 | if (typeof(text) == STR) 139 | text = {text}; 140 | endif 141 | this:local_instruction(name, upload); 142 | ":dump_lines() takes care of the final `.' ..."; 143 | for line in ($command_utils:dump_lines(text)) 144 | notify(player, line); 145 | endfor 146 | "[Modified by Slither (#479) Wed Jun 20 20:00:34 2018 WEST]."; 147 | . 148 | 149 | @args #480:"local_instruction" this none this 150 | @program #480:local_instruction 151 | {label, ?upload = "none"} = args; 152 | player:tell("#$# edit name: ", label, " upload: ", upload); 153 | . 154 | 155 | ``` 156 | 157 | To access local editing you need a verb on yourself (or on a parent Programmer bit) 158 | 159 | ``` 160 | @program #479:@edit any any any 161 | "Calls the verb editor on verbs, the note editor on properties, and on anything else assumes it's an object for which you want to edit the .description."; 162 | len = player.linelen; 163 | "if (player:edit_option(\"local\"))"; 164 | "player.linelen = abs(len) * -1;"; 165 | "endif"; 166 | if (!args) 167 | (player in $note_editor.active ? $note_editor | $verb_editor):invoke(dobjstr, verb); 168 | elseif ($code_utils:parse_verbref(args[1])) 169 | if (player.programmer) 170 | #480:invoke(argstr, verb); 171 | player:tell("invoke done"); 172 | else 173 | player:notify("You need to be a programmer to do this."); 174 | player:notify("If you want to become a programmer, talk to a wizard."); 175 | return; 176 | endif 177 | elseif ($list_editor:is_valid(dobjstr)) 178 | $list_editor:invoke(dobjstr, verb); 179 | else 180 | $note_editor:invoke(dobjstr, verb); 181 | endif 182 | "player.linelen = len;"; 183 | "[Modified by Slither (#479) Wed Jun 20 20:03:49 2018 WEST]."; 184 | . 185 | ``` 186 | -------------------------------------------------------------------------------- /tutorials/lambda-moo-nodak-edu.md: -------------------------------------------------------------------------------- 1 | 2 | # Programming 3 | 4 | First and foremost, the most important thing to realize about the LambdaMOO 5 | system is that there really isn't anything like a program to deal with. 6 | In C++, every program has a main function. For LambdaMOO, any implicit main 7 | program is safely concealed inside the internals of the system, and programmers 8 | have no say in the matter unless they wish to start hacking away at server code. 9 | For LambdaMOO, a programmer is restricted to constructing objects and their verbs. 10 | Like any object oriented system, a programmer could argue that the execution paths 11 | through the various verbs constitues a program, but unfortunately that argument tends 12 | to break down quickly since the entire point of having functions on an object is so that 13 | other objects can interact with each other. That's the point of object oriented 14 | programming. 15 | 16 | Unless otherwise noted, when specifying commands for performing some action, 17 | any text enclosed in square brackets is optional. E.g. _FunctionName(Arg1 \[,Arg2)_ 18 | indicates that Arg2 is optional when calling the function Arg1\. 19 | 20 | Having said that, the next issue is how one constructs objects to do meaningful 21 | things in LambdaMOO. The major issues at this point are 22 | 23 | * [Data Types][1] 24 | * [Creating an object][2] 25 | * [Creating properties][3] 26 | * [Creating verbs][4] 27 | * [Inside Verbs][5] 28 | * [Fun with lists and strings in LambdaMOO][6] 29 | 30 | --- 31 | 32 | Data Types in LambdaMOO 33 | 34 | The data types for LambdaMOO are 35 | * **Integers** Signed whole numbers. 36 | * **Object** Objects in the LambdaMOO database are referred to 37 | by their object number. \#ObjectNumber denotes an object number 38 | (for example \#0 is object number 0). The object number is 39 | essentially a pointer to an object, since there is no such 40 | notion as passing an object by value for the LambdaMOO system. 41 | toobj(...) will convert either strings like "\#42" or 42 to objects, 42 | and both result in \#42\. 43 | * **Strings** Characters in LambdaMOO are strings of length 1\. 44 | Like lists, LambdaMOO strings are immutable. 45 | * **Error** Various run time errors, such as security violations, 46 | type mismatches, unassigned variables, etc. Consult the 47 | programmer's manual for more information. 48 | * **Lists** In LambdaMOO, lists are a the fundamental 49 | data type for constructing things like arrays and sets. 50 | The elements in a list can be assigned to destructively, 51 | but in order to change the length of a list it is necessary to 52 | do something like 53 | 54 | MyList={@MyList,NewElement} 55 | 56 | Elements of a list can be of any type allowed for variables in 57 | LambdaMOO, including lists themselves. 58 | See the section on [Lists for more details.][6] 59 | * **Floating point numbers** Signed real numbers. 60 | It is important to note that in LambdaMOO, 61 | 33 _is not equal_ to 33.0\. 62 | Data Typetypeof(...)==Conversion Function\>Comments 63 | 64 | IntegersINT or 0tonum(...) or toint(...)Signed whole numbers 65 | 66 | ObjectOBJ or 1toobj(...)Reference number for an object 67 | 68 | StringsSTR or 2tostr(...)String or character 69 | 70 | ErrorERR or 3noneError condition. Used for various runtime errors 71 | 72 | ListsLIST or 4noneImmutable list. Used for arrays and sets 73 | 74 | Floating point numberFLOAT or 9tofloat(...)Signed floating point number 75 | 76 | Creating Objects 77 | 78 | The three major ways of creating objects in LambdaMOO are 79 | * 80 | @create ParentObject named "Object Names" 81 | 82 | This first method is used by human beings to create objects, 83 | and is something which would be entered on the command line. 84 | For example: **@create $thing named "my very own object"** 85 | 86 | will create an object derived from $thing (Generic Thing) 87 | and set the new object's name to _my very own thing_ 88 | * 89 | create(ParentObject \[,OwnerObject\]) 90 | 91 | This method is intended for programmatically creating new 92 | objects by invoking the interpreter's built in **create** 93 | function. Both ParentObject and OwnerObject are required to 94 | by [object references][7]. 95 | If OwnerObject is not specified, then the object will own itself 96 | (see [object ownership 97 | in the security section][8]). The return type of create will 98 | be the object reference of the newly created object, or else 99 | the appropriate error code. 100 | 101 | For example: 102 | 103 | **create($npc,player)** 104 | 105 | will create an object derived from $npc and owned by the 106 | player object which caused this verb to be executed. 107 | * 108 | $recycler:\_create(Parent, \[,OwnerObject) 109 | 110 | This method is also intended for programmatically creating new objects 111 | but in this case it differs from the built in function in a subtle 112 | manner. When create() is used, the object reference will always be 113 | unique. $recycler:\_create attempts to objects discarded with 114 | $recycler:\_recycle(NoLongerNeededObject) by reconstructing a previously 115 | used object into its new role. In a perfect world this verb is 116 | indistinguishable from the create function, except that its friendlier to 117 | longer lived systems which create many temporary objects. 118 | 119 | Creating properties on an object 120 | 121 | The major ways to add properties to an object in LambdaMOO are 122 | * 123 | @property ObjectName.PropertyName \[PropertyValue\] 124 | 125 | This first method is intended for use by human beings when constructing 126 | objects before they are put into use. If PropertyValue is omitted, 127 | the property is initialized to 0\. 128 | * 129 | add\_property(TargetObject,PropertyName,InitialValue,Info) 130 | 131 | This method invokes the built in add\_property function to add 132 | the property PropertyName to the object TargetObject. For more 133 | information, please consult the online help using 134 | **@help add\_property** 135 | * 136 | Use tkMOOlite or MacMOOSE 137 | 138 | Andrew Wilson's tkMOOlite or the MacMOOSE programs each have 139 | nice interactive object browser's which allow a programmer to 140 | interactively add properties and verbs to an object. 141 | Needless to say, it is generally horribly complicated to add properties 142 | to objects from within a verb, and for most purposes there isn't really 143 | any need to. I'd personally recommend staying true to the rule of thumb that 144 | say "If you think you need to add a property to an object from within a verb 145 | you're probably wrong." To change the value (or type for that matter) 146 | of a property, the easiest way is simply to assign it a new value. 147 | Programmatically, this is simply 148 | 149 | 150 | ObjectReferece.Property = NewValue; 151 | 152 | 153 | and to change a value at the command line, the only difference is to add 154 | a semicolon (to tell the command line interpreter that you are typing in 155 | an expression and not a command), like so: 156 | 157 | 158 | ;ObjectReference.Property = NewValue; 159 | 160 | 161 | Creating verbs on an object 162 | 163 | The major ways of adding verbs to an object in LambdaMOO are 164 | Fun with lists and strings 165 | 166 | ##### Accessing and modifying elements of lists and strings 167 | 168 | To access a particular element of a list or string, the square braces 169 | are used. Elements are numbered sequentially from 1 to n, where n is the 170 | length of the string or list. 171 | 172 | Assigning to an element of a list or string modifies the corresponding 173 | element of the list or string. 174 | 175 | For example: 176 | 177 | 178 | Test="George"; // Our example string 179 | Test[3]="4"; // Test is now "Ge4rge" 180 | Test[5..6]="56"; // Test is now "Ge456e" 181 | Test={"A","B","C"}; // Our example list 182 | Test[2]="BC"; // Test is now {"A","BC","C"}; 183 | Test[2]="BC","DE"; // Error. Invalid statement. 184 | Test[2]={"BC","DE"}; // Test is now {"A",{"BC","DE"},"C"}; 185 | 186 | 187 | As shown in the example, the elements selected from a list or string can 188 | be more than one element long. However, those elements have to be 189 | continuous. For example 190 | 191 | 192 | "George"[3..3] // "o" 193 | "George"[3..5] // "org" 194 | "George"[3..5,7..9] // syntax error 195 | 196 | 197 | Strings can be costructed using the + operator to append two strings together. 198 | Unfortunately, this modifies neither of the two original strings. 199 | 200 | 201 | OriginalName="George"; 202 | LastName=" The Great"; 203 | NewName=OriginalName+LastName; 204 | 205 | 206 | Changes neither OriginalName or LastName. There is absolutely nothing 207 | wrong with assigning like 208 | 209 | 210 | OriginalName="George"; 211 | LastName=" The Great"; 212 | OriginalName=OriginalName+LastName; // OriginalName = "George The Great" 213 | OriginalName="George"+LastName; // Same result 214 | OriginalName="George"+" The Great"; // Same result 215 | 216 | 217 | In fact, this way of appending to strings in absolutely necessary. 218 | In LambdaMOO (as in Java), a string is only as long as it absolutely 219 | has to be, and to append to a string (such as changing a string stored 220 | in a property) requires such a construct. 221 | 222 | Lists are constructed in a similar manner. The ampersand operator, 223 | when applied to the front of a list name, expands out to the contents 224 | of that list. For example: 225 | 226 | 227 | List={1,2,3,4,5}; 228 | List={@List}; // Identity operation 229 | List={@List,6}; // List = {1,2,3,4,5,6}; 230 | Counter=1; 231 | while(Counter 232 | 233 | # The source code for this file was taken from [http://lions.cs.ndsu.nodak.edu/~vender/LambdaMOO/programming.html][0] and is included here for posterity. It is not HTML5\. 234 | 235 | [0]: http://lions.cs.ndsu.nodak.edu/~vender/LambdaMOO/programming.html 236 | [1]: #data_types 237 | [2]: #create_object 238 | [3]: #create_properties 239 | [4]: #create_verbs 240 | [5]: #inside_verbs 241 | [6]: #lists 242 | [7]: syntax.html#object_reference 243 | [8]: security.html#object_ownership 244 | -------------------------------------------------------------------------------- /code/CodeScanner.md: -------------------------------------------------------------------------------- 1 | # Code Scanner 2 | This package will scan verb code for common issues and warn you about them. You can edit/add/remove some checks if you don't want them, or update them to better fit your coding standards. 3 | 4 | ## MOO Package Manager 5 | 6 | THIS PACKAGE IS AVAILABLE FOR INSTALL USING THE MOO PACKAGE MANAGER (FOR TOASTSTUNT). THIS IS THE PREFERRED METHOD OF INSTALL IF YOU ARE USING TOASTSTUNT. IT IS PROBABLY A MORE UPDATED VERSION AS WELL. SEE: [MOO Package Manager](https://github.com/sevenecks/moo-package-manager) 7 | 8 | ## Contributing 9 | 10 | The code scanner covers some of our best practices for MOO coding on [Sindome](https://www.sindome.org/). If you have your own that you think would be useful to the community please feel free to put in a pull request! 11 | 12 | ## Requirements 13 | This code was tested on stock LambdaMOO 1.8.1 running the lastest LambdaCore.db. AND on ToastStunt running a modified LambdaCore.db. If your DB is based off of that it should work with no changes needed. 14 | 15 | ## What's Scanned For? 16 | 17 | 1. Comment at the top of the verb for non-player facing verbs (good practice!) 18 | 2. Nesting of for/if/while > MAX_NESTING (a variable you can define in your code). 19 | 3. Argument scatter in player facing verbs 20 | 4. Object Numbers in code (you should corify things for clarity (and later your own sanity)) 21 | 5. tostr usage inside a :tell (this is already tostr'd on #1) 22 | 6. .location assignment instead of comparison (so you don't loop over a bunch of objects and move them instead of checking their location) 23 | 7. assignment in if statements (doing assignment in if statements is valid in moo, and useful, but it's good to be warned of in general so you don't assign instead of compare by accident) 24 | 8. forking (forks are useful but writing a $scheduler that lets you $schedule things to be run at specific times is better) 25 | 9. verb length > MAX_LENGTH (a variable you can change to suit your needs) 26 | 27 | ## Installation 28 | Create an object, we're using #79 as the parent, but it really doesn't rely on the parent anything but itself, so use your own discresion. 29 | 30 | ``` 31 | @create #78 named Code Scanner 32 | ``` 33 | 34 | Now you'll want to corify the reference to the Code Scanner, replace #97 with your new object number. 35 | 36 | ``` 37 | @corify #97 as code_scanner 38 | ``` 39 | 40 | This allows you to reference the object as $code_scanner. 41 | 42 | Now, copy the code below into a text editor and change the obj# form #97 to whatever the obj# of your newly created Code Scanner object. 43 | ``` 44 | ;#97.description = {"MOO Code Scanner 1.1 by Brendan Butts ", "", "Github: https://github.com/SevenEcks/lambda-moo-programming", "", "Usage: $code_scanner:scan_for_issues(OBJ, verbname)", "Usage: $code_scanner:display_issues($code_scanner:scan_for_issues(OBJ, verbname))", "", "If you integrate this with your @Program verb I recommend you make a copy of it first and test on that just in case! But you can always use .program if you mess up!"} 45 | @create #12654 named Code Scanner Utils:Code,Scanner,Utils,Code Scanner Utils 46 | ;;#97.("aliases") = {"Code", "Scanner", "Utils", "Code Scanner Utils"} 47 | ;;#97.("description") = "This is a placeholder parent for all the $..._utils packages, to more easily find them and manipulate them. At present this object defines no useful verbs or properties. (Filfre.)" 48 | ;;#97.("object_size") = {19212, 1612285023} 49 | ;;#97.("instance_id") = "#12654-1541212667.02364" 50 | 51 | @verb #97:"scan_for_issues" this none this 52 | @program #97:scan_for_issues 53 | {object, verbname, ?options = {}, ?code = {}} = args; 54 | if (!code) 55 | code = verb_code(object, verbname); 56 | endif 57 | verb_args = verb_args(object, verbname); 58 | "max length before we start warning it's too long"; 59 | MAX_LENGTH_WARNING = 40; 60 | "max nesting before we start warning"; 61 | MAX_NESTING_WARNING = 2; 62 | "what do the args of an internal variable look like?"; 63 | internal_args = {"this", "none", "this"}; 64 | warnings = {}; 65 | max_nest = 0; 66 | open_ifs = 0; 67 | open_fors = 0; 68 | open_whiles = 0; 69 | forks = 0; 70 | "first real line of code, not a comment"; 71 | first_real_line = 0; 72 | "count of what line we are on"; 73 | count = 0; 74 | "is this an internal (tnt) verb?"; 75 | internal = (internal_args == verb_args) ? 1 | 0; 76 | for line in (code) 77 | count = count + 1; 78 | "check for opening comments"; 79 | if (((count == 1) && internal) && (!$code_scanner:match_comment(line))) 80 | warnings = {@warnings, {"You did not include a comment on the first line describing the use and args of your verb.", count}}; 81 | endif 82 | "check if we have found the first real line of code or if it's just a comment"; 83 | if ((!first_real_line) && (!$code_scanner:match_comment(line))) 84 | first_real_line = count; 85 | endif 86 | "check for an argument scatter in a player facing verb"; 87 | if ((!internal) && $code_scanner:match_arg_scatter(line)) 88 | warnings = {@warnings, {"You have used an argument scatter in a verb that is not 'this none this'. This is not how it should work.", count}}; 89 | endif 90 | "find nesting"; 91 | if ($code_scanner:match_if(line)) 92 | open_ifs = open_ifs + 1; 93 | elseif ($code_scanner:match_for(line)) 94 | open_fors = open_fors + 1; 95 | elseif ($code_scanner:match_while(line)) 96 | open_whiles = open_whiles + 1; 97 | elseif ($code_scanner:match_endif(line)) 98 | open_ifs = open_ifs - 1; 99 | elseif ($code_scanner:match_endfor(line)) 100 | open_fors = open_fors - 1; 101 | elseif ($code_scanner:match_endwhile(line)) 102 | open_whiles = open_whiles - 1; 103 | elseif ($code_scanner:match_fork(line)) 104 | forks = forks + 1; 105 | elseif ($code_scanner:match_object(line)) 106 | warnings = {@warnings, {"You may have an object number in your code. This should be avoided. Use corified references ($) instead.", count}}; 107 | endif 108 | "check for tostr usage in :tell verbs"; 109 | if ($code_scanner:match_tell_tostr(line)) 110 | warnings = {@warnings, {"tostr usage found inside a :tell call. This is undeeded as :tell will call tostr on all it's args.", count}}; 111 | endif 112 | if ($code_scanner:match_location_assignment(line)) 113 | warnings = {@warnings, {"IMPORTANT!!!!!!!!!!!!!!!! You may have included a .location assignment INSTEAD of a comparison (= instead of ==)!", count}}; 114 | endif 115 | if ($code_scanner:match_if_assignment(line)) 116 | warnings = {@warnings, {"You are doing an assignment '=' operation in an if statement, please confirm you didn't mean to do an equality check '=='.", count}}; 117 | endif 118 | if ($code_scanner:match_recycler_valid(line)) 119 | warnings = {@warnings, {"You are doing an if($recycler:valid()) operation without a ! in front of it, are you SURE that you don't need the bang (!)?", count}}; 120 | endif 121 | if ((current_nest = (open_fors + open_ifs) + open_whiles) > max_nest) 122 | max_nest = current_nest; 123 | endif 124 | endfor 125 | if (forks) 126 | warnings = {@warnings, {"There is a fork() in this code. Please do not do this unless you know what you are doing. Consider using the $scheduler (or something with a heartbeat) instead.", 0}}; 127 | endif 128 | if (max_nest > MAX_NESTING_WARNING) 129 | warnings = {@warnings, {tostr("Max nesting of if/for/while's is ", max_nest, ". Try refactoring or extracting pieces to a new verb to get your max nesting to 2 or below."), 0}}; 130 | endif 131 | length_of_verb = length(code); 132 | if (length_of_verb > MAX_LENGTH_WARNING) 133 | warnings = {@warnings, {tostr("This verb is ", length_of_verb, " lines long. Consider refactoring or extracting to a new verb to get your max nesting to ", MAX_LENGTH_WARNING, " or below."), 0}}; 134 | endif 135 | return warnings; 136 | . 137 | 138 | @verb #97:"display_issues" this none this 139 | @program #97:display_issues 140 | ":display_issues(LIST warnings) => none"; 141 | "takes the output of :scan_for_issues and displays it"; 142 | {warnings} = args; 143 | for warning_set in (warnings) 144 | {warning, line_number} = warning_set; 145 | if (line_number) 146 | player:tell(tostr("Warning on line ", line_number), ": ", warning); 147 | else 148 | player:tell("Warning", ": ", warning); 149 | endif 150 | endfor 151 | "VMS NOTE display messages #22664 07/17/18 1:16"; 152 | "VMS VERSION 1.01"; 153 | "Last modified by Fengshui (#22664) on Tue Jul 17 13:16:35 2018 PDT"; 154 | . 155 | 156 | @verb #97:"match_if" this none this 157 | @program #97:match_if 158 | ":match_if(STR line) => bool"; 159 | {line} = args; 160 | return match(line, "^[ ]*if "); 161 | . 162 | 163 | @verb #97:"match_for" this none this 164 | @program #97:match_for 165 | ":match_for(STR line) => bool"; 166 | {line} = args; 167 | return match(line, "^[ ]*for "); 168 | . 169 | 170 | @verb #97:"match_while" this none this 171 | @program #97:match_while 172 | ":match_while(STR line) => bool"; 173 | {line} = args; 174 | return match(line, "^[ ]*while "); 175 | . 176 | 177 | @verb #97:"match_endif" this none this 178 | @program #97:match_endif 179 | ":match_endif(STR line) => bool"; 180 | {line} = args; 181 | return match(line, "^[ ]*endif$"); 182 | . 183 | 184 | @verb #97:"match_endfor" this none this 185 | @program #97:match_endfor 186 | ":match_if(STR line) => bool"; 187 | {line} = args; 188 | return match(line, "^[ ]*endfor$"); 189 | . 190 | 191 | @verb #97:"match_endwhile" this none this 192 | @program #97:match_endwhile 193 | ":match_endwhile(STR line) => bool"; 194 | {line} = args; 195 | return match(line, "^[ ]*endwhile"); 196 | . 197 | 198 | @verb #97:"match_fork" this none this 199 | @program #97:match_fork 200 | ":match_fork(STR line) => bool"; 201 | {line} = args; 202 | return match(line, "^[ ]*fork"); 203 | . 204 | 205 | @verb #97:"match_object" this none this 206 | @program #97:match_object 207 | ":match_object(STR line) => bool"; 208 | {line} = args; 209 | return match(line, "#[0-9]+"); 210 | . 211 | 212 | @verb #97:"match_tell_tostr" this none this 213 | @program #97:match_tell_tostr 214 | ":match_tell_tostr(STR line) => bool"; 215 | {line} = args; 216 | return match(line, ".*:tell(.*tostr("); 217 | . 218 | 219 | @verb #97:"match_comment" this none this 220 | @program #97:match_comment 221 | ":match_comment(STR line) => bool"; 222 | {line} = args; 223 | return match(line, "^[ ]*\""); 224 | . 225 | 226 | @verb #97:"match_arg_scatter" this none this 227 | @program #97:match_arg_scatter 228 | ":match_arg_scatter(STR line) => bool"; 229 | {line} = args; 230 | return match(line, "{.+} = args;"); 231 | . 232 | 233 | @verb #97:"match_location_assignment" this none this 234 | @program #97:match_location_assignment 235 | ":match_if(STR line) => bool"; 236 | {line} = args; 237 | return match(line, "[ ]*if (.+.location = .+)"); 238 | . 239 | 240 | @verb #97:"match_if_assignment" this none this 241 | @program #97:match_if_assignment 242 | ":match_if_assignment(STR line) => bool"; 243 | "looks for assignment operators in if statements"; 244 | {line} = args; 245 | return match(line, "^[ ]*if (.+ = .+)"); 246 | . 247 | 248 | @verb #97:"match_corified_verb_anywhere" this none this 249 | @program #97:match_corified_verb_anywhere 250 | ":match_corified(STR line) => bool"; 251 | "this matches to a corified verb reference ANYWHERE in the string"; 252 | {line} = args; 253 | return match(line, "%$[a-z]+:[a-z]+[^(]"); 254 | . 255 | 256 | @verb #97:"match_recycler_valid" this none this 257 | @program #97:match_recycler_valid 258 | ":match_recycler_valid(STR line) => bool"; 259 | {line} = args; 260 | return match(line, "^[ ]*if (%$recycler:valid"); 261 | . 262 | ``` 263 | 264 | ## Usage 265 | 266 | ``` 267 | ;$code_scanner:display_issues($code_scanner:scan_for_issues($code_scanner, "scan_for_issues")) 268 | ``` 269 | 270 | ## Colorizing 271 | If you have ANSI support you can edit :display_issues to add some colorizing. 272 | 273 | ## Integrating with @program 274 | It's great to have these warnings show up automatically after you @program a verb. However you must be careful when integrating the code scanner as you don't want to break your @program verb to the point where you can't @program a fix for it. 275 | 276 | I recommend two things: 277 | 278 | 1. Create a copy of your @program verb and test integration with that. 279 | 2. Put the $code_scanner code inside a try/except just to be on the safe side. 280 | 281 | ### Where do I put the code inside @program? 282 | 283 | Put it somewhere after the set_verb_code() builtin is executed. 284 | 285 | ## Forks 286 | 287 | Sindome has a $scheduler which is an alternative to forking, that lets you schedule things to happen at specific times, as opposed to forking in your code. Other MOO's have similar things. If you do not, you can remove the fork() check, if you don't find it useful. 288 | -------------------------------------------------------------------------------- /tutorials/src/lambda-moo-programming-tutorial-nodak-edu-non-html5.html: -------------------------------------------------------------------------------- 1 | 2 | Lamba MOO Programming Tutorial--Programming 3 | 4 |

The source code for this file was taken from http://lions.cs.ndsu.nodak.edu/~vender/LambdaMOO/programming.html and is included here for posterity. It is not HTML5.

5 |

Programming

6 |

First and foremost, the most important thing to realize about the LambdaMOO 7 | system is that there really isn't anything like a program to deal with. 8 | In C++, every program has a main function. For LambdaMOO, any implicit main 9 | program is safely concealed inside the internals of the system, and programmers 10 | have no say in the matter unless they wish to start hacking away at server code. 11 | For LambdaMOO, a programmer is restricted to constructing objects and their verbs. 12 | Like any object oriented system, a programmer could argue that the execution paths 13 | through the various verbs constitues a program, but unfortunately that argument tends 14 | to break down quickly since the entire point of having functions on an object is so that 15 | other objects can interact with each other. That's the point of object oriented 16 | programming.

17 |

Unless otherwise noted, when specifying commands for performing some action, 18 | any text enclosed in square brackets is optional. E.g. FunctionName(Arg1 [,Arg2) 19 | indicates that Arg2 is optional when calling the function Arg1.

20 |

Having said that, the next issue is how one constructs objects to do meaningful 21 | things in LambdaMOO. The major issues at this point are 22 |

30 |
31 | Data Types in LambdaMOO 32 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 |
Data Typetypeof(...)==Conversion Function>Comments
IntegersINT or 0tonum(...) or toint(...)Signed whole numbers
ObjectOBJ or 1toobj(...)Reference number for an object
StringsSTR or 2tostr(...)String or character
ErrorERR or 3noneError condition. Used for various runtime errors
ListsLIST or 4noneImmutable list. Used for arrays and sets
Floating point numberFLOAT or 9tofloat(...)Signed floating point number
80 | Creating Objects 81 | 122 | Creating properties on an object 123 | 145 | Needless to say, it is generally horribly complicated to add properties 146 | to objects from within a verb, and for most purposes there isn't really 147 | any need to. I'd personally recommend staying true to the rule of thumb that 148 | say "If you think you need to add a property to an object from within a verb 149 | you're probably wrong." To change the value (or type for that matter) 150 | of a property, the easiest way is simply to assign it a new value. 151 | Programmatically, this is simply 152 |
153 |     ObjectReferece.Property = NewValue;
154 | 
155 | and to change a value at the command line, the only difference is to add 156 | a semicolon (to tell the command line interpreter that you are typing in 157 | an expression and not a command), like so: 158 |
159 | ;ObjectReference.Property = NewValue;
160 | 
161 | Creating verbs on an object 162 | 164 | Fun with lists and strings 165 |
Accessing and modifying elements of lists and strings
166 |

To access a particular element of a list or string, the square braces 167 | are used. Elements are numbered sequentially from 1 to n, where n is the 168 | length of the string or list.

169 |

Assigning to an element of a list or string modifies the corresponding 170 | element of the list or string.

171 |

For example:

172 |
173 |     Test="George";	// Our example string
174 |     Test[3]="4";	// Test is now "Ge4rge"
175 |     Test[5..6]="56";    // Test is now "Ge456e"
176 |     Test={"A","B","C"}; // Our example list
177 |     Test[2]="BC";	// Test is now {"A","BC","C"};
178 |     Test[2]="BC","DE";  // Error.  Invalid statement.
179 |     Test[2]={"BC","DE"}; // Test is now {"A",{"BC","DE"},"C"};
180 | 
181 | As shown in the example, the elements selected from a list or string can 182 | be more than one element long. However, those elements have to be 183 | continuous. For example 184 |
185 |     "George"[3..3]	// "o"
186 |     "George"[3..5]	// "org"
187 |     "George"[3..5,7..9] // syntax error
188 | 
189 | Strings can be costructed using the + operator to append two strings together. 190 | Unfortunately, this modifies neither of the two original strings. 191 |
192 |     OriginalName="George";
193 |     LastName=" The Great";
194 |     NewName=OriginalName+LastName;
195 | 
196 | Changes neither OriginalName or LastName. There is absolutely nothing 197 | wrong with assigning like 198 |
199 |     OriginalName="George";
200 |     LastName=" The Great";
201 |     OriginalName=OriginalName+LastName; // OriginalName = "George The Great"
202 |     OriginalName="George"+LastName;     // Same result
203 |     OriginalName="George"+" The Great"; // Same result
204 | 
205 | In fact, this way of appending to strings in absolutely necessary. 206 | In LambdaMOO (as in Java), a string is only as long as it absolutely 207 | has to be, and to append to a string (such as changing a string stored 208 | in a property) requires such a construct. 209 |

Lists are constructed in a similar manner. The ampersand operator, 210 | when applied to the front of a list name, expands out to the contents 211 | of that list. For example: 212 |

213 |     List={1,2,3,4,5};
214 |     List={@List};			// Identity operation
215 |     List={@List,6};			// List = {1,2,3,4,5,6};
216 |     Counter=1;
217 |     while(Counter<6)
218 |         List={@List,Counter};
219 |         Counter=Counter+1;
220 |     endwhile
221 |     // List is now {1,2,3,4,5,6,1,2,3,4,5}
222 |     NewList=List[7..10];		// NewList = {1,2,3}
223 | 
224 |

The elements of a list can be lists themselves. There is no restriction 225 | on the layout of each sublist, and lists can be nested arbitrarily deep 226 | to construct multidimensional arrays or fancy data structures.

227 |
228 |     List={};
229 |     List={@List,{},{},{}};		// List now contains three empty lists.
230 |     List[1]={1,2,{3,4,5,6}};		// The first list in List now contains
231 | 					// the list {1,2,{3,4,5,6}}
232 |     List[3]=List[1];			// The third list in List now contains
233 | 					// a copy of List[1]
234 |     List[3][3][2]=7;			// List is now
235 | 					// { {1,2,{3,4,5,6}}, {}, {1,2,{3,7,5,6}} }
236 | 
237 | For both lists, the in operator is very useful. in 238 | is a binary operator, the left argument being the element to look for, 239 | and the right element being the list to search. Its value is the 240 | position in its target to search, or 0 if the element is not found. 241 |
242 |     (5 in {1,2,9,3,5,2}) 	// result is 5
243 |     (4 in {0,4,4}		// result is 2
244 | 
245 | Like assignment, using in to look for substrings won't work. Likewise, if 246 | you construct a complicated list structure, in only checks the list 247 | and not any elements in the list. 248 |
249 |     ({1,2} in {0,{1,2},{3,4}}) // result is 2
250 |     (2 in {0,{1,2},{3,4}})     // result is 0
251 | 
252 |

Various utility objects, such as $string_utils, $set_utils, 253 | $assoc_utils, $match_utils, and others are available for use. 254 | The specific documentation is available by 255 | @examineing the appropriate objects, or reading the comments 256 | in the verbs of the object.

257 |

The really important thing to remember is that if your were all set 258 | to use a binary tree, hash table, or other data structure normally 259 | implemented using pointers, you have two choices: 1--rethink the 260 | algorithm using lists, or 2--write, find or convince someone to write 261 | a set of functions to implement that data structure using LambdaMOO's 262 | lists. 263 | 264 | 265 | 266 | -------------------------------------------------------------------------------- /tutorials/mud_moo_el_tutorial.md: -------------------------------------------------------------------------------- 1 | # Getting started with moo.el 2 | 3 | __by Puff the Fractal Dragon__ 4 | 5 | This should get you started with the moo.el client, an ELISP module for GNU EMACS. Moo.el is extremely useful, particularly if you're adept with GNU EMACS, but this help file is aimed at somebody who's never even used GNU EMACS before, so DON'T PANIC (kudos to Douglas Adams). 6 | 7 | __NOTE__: Many players have contacted me asking where they can get a copy of mud.el or moo.el. I never discussed this because the help object is intended to be used by those who already HAVE moo.el but don't know how to use it. JoeFeedback is the creator and sole distributor of moo.el. He requests that users do NOT distribute it, as he wishes to track its use and testing for purposes of further enhancements. 8 | 9 | ## A Note About Terminology 10 | 11 | ``` 12 | Ctrl-Anything 13 | ...means hold down the Control key and press another key. 14 | 15 | Esc Anything 16 | ...means press the Escape key (or meta key if you're not on an MS-DOS compatible keyboard), let it up, THEN press another key. 17 | 18 | Esc x blah-blah or Ctrl-X blah-blah 19 | Often I'll use this where blah-blah is a set of commands. You do either Esc x or Ctrl-x and wait a moment, and either "M-x" or "C-x" will appear on the bottom line of your screen. Start to type blah-blah. 20 | 21 | Tab and ? 22 | While typing blah-blah, if you press Tab, EMACS will try to complete the command for you. If you press Tab twice, or press ?, EMACS will list the possible completions of the command. You can continue typing and using Tab until you get the entire command, then press [enter]. 23 | ``` 24 | 25 | ## Starting up 26 | 27 | Assuming you have GNU EMACS on your system, you start it up by entering "EMACS [enter]" from the prompt. If you have more experience with EMACS you can set it up so it automatically loads moo.el, but for now, use: 28 | 29 | Esc x load-file [enter] moo.el [enter] 30 | 31 | ## Connecting 32 | 33 | Connect to a mud with: 34 | 35 | ``` 36 | Esc x mud [enter] mudname [enter]. 37 | ``` 38 | If you don't know the mud's name, you can use completion, or type ? to get a list of the muds in your .moo_worlds file. 39 | 40 | ## The .moo_worlds file 41 | 42 | If you want to add new muds that aren't in the list, you have to edit the .moo_worlds file (you should have gotten a basic .moo_worlds file when you got moo.el). To add new muds to your .moo_worlds file, you can either edit it directly, or use: 43 | 44 | ``` 45 | Esc x mud-add-world [enter] 46 | World name: LambdaMOO [enter] 47 | Login name: player's name <I suggest you leave this blank> [enter] 48 | Password: player's password <I suggest you leave this blank> [enter] 49 | Address: lambda.parc.xerox.com [enter] 50 | Port number: 8888 [enter] 51 | Type: MOO [enter] 52 | ``` 53 | 54 | You may find it handy to make more than one entry for your favorite muds. You can connect to two different characters at once, but only if you have two different entries for that mud in .moo_worlds. I use LambdaMOO and lambda, for example. If I tried to use LambdaMOO twice, moo.el would just switch me back to the LambdaMOO buffer (more about buffers later). 55 | 56 | ## Fun with moo.el for MOO 57 | 58 | Once you've connected, there are several ways moo.el can make your life easier, particularly if you're connecting to a MOO. The ones I use most often are multiple buffers, multiple windows, verb and property editing, and general nifty things you can do with your buffer when you can edit the whole thing with EMACS commands. 59 | 60 | ## Multiple Buffers 61 | 62 | Like I said up above about .moo_worlds, you can connect to a mud more than once, using multiple buffers. With multiple buffers you can hop from one mud connection to another and work on two or more muds, or edit files, and so on. Buffers are part of EMACS, and are generally nifty things that you can learn more about with the EMACS manual. One thing to keep in mind is that the buffer sticks around until you kill it - out of sight does NOT mean out of mind. 63 | 64 | ## Switching Between Buffers 65 | 66 | You switch between buffers using: 67 | 68 | ``` 69 | Ctrl-x b 70 | Switch to buffer: (default: blahblah) [enter] 71 | ``` 72 | 73 | If you just hit enter you'll go to the default buffer. The default buffer is the last buffer you were in, in this window (more about windows next). For just two buffers, this is fine, you can do Ctrl-x b forever and keep going back and forth. If you have more than one buffer, you can type part of the buffer name and use command completion or ? to see the other buffer names. 74 | 75 | ## Opening New Buffers 76 | 77 | You can also open new buffers to edit text or whatever, simply by switching to a buffer that isn't there. 78 | 79 | ``` 80 | Ctrl-x b 81 | Switch to buffer: newbuffername [enter] 82 | ``` 83 | 84 | ## Killing Buffers 85 | 86 | Generally I don't close buffers, I just @quit, or save the file I was editing, and ignore it until I'm ready to kill the whole EMACS. But if you want to or need to make it go away, you can use: 87 | 88 | ``` 89 | Esc x kill-buffer [enter] 90 | Kill buffer: (default blahblah) 91 | ``` 92 | 93 | You can specify which buffer to kill the same way you specify which buffer to switch to. 94 | 95 | ## Uploading Buffers 96 | 97 | You can send the contents of a buffer to the mud with the command: 98 | 99 | ``` 100 | Esc x mud-upload-buffer [enter] 101 | ``` 102 | 103 | EMACS will ask you for the buffer to upload, and the mud to upload it to. You can also use 104 | 105 | ## Quitting from a MUD 106 | 107 | If you want to @quit from a mud, then reconnect (to connect to another character, for example), I've found you have to @quit, wait a few seconds until it says: 108 | ``` 109 | |*** Disconnected *** 110 | ``` 111 | 112 | then press [enter] a couple of times. It'll say: 113 | 114 | ``` 115 | Process MUD<1> exited abnormally with code 256 116 | ``` 117 | 118 | And now you can do: 119 | 120 | ``` 121 | Esc x mud [enter] mudname [enter] 122 | ``` 123 | 124 | and reconnect. 125 | 126 | ## Multiple Windows 127 | 128 | Multiple windows are like multiple buffers (in fact, they usually ARE multiple buffers), only they're on the same 24 line screen. Right now,as I type this, I have about 10 lines for this document on the top half of my screen, and the bottom half contains two windows, one for a LambdaMOO session and one for a GodNet session. 129 | 130 | ## How Many Windows Is Too Many? 131 | 132 | You can keep opening more windows until all you have left on your screen is window bars (solid light bars that separate the windows). For the most part, two windows is optimal to work with, three if two of them aren't too busy, and four is pushing it. If I have to keep track of more buffers than this, I generally switch buffers in and out. 133 | 134 | ## The Windows Commands 135 | 136 | The windows commands are generally: 137 | 138 | ``` 139 | Ctrl-x 2 separate the present window into two windows. 140 | Ctrl-x 1 shut down all of the windows except the one the cursor is in. 141 | Ctrl-x o hop to the next window. 142 | Ctrl-x ^ (^ is shift-6 on most keyboards) expand the window you're in by 143 | 1 line. This one is tricky, it takes a line away the "closest" 144 | window, a not-always-predictable choice. 145 | ``` 146 | 147 | ## Verb and Property Editing 148 | 149 | The MOO verb and property editor functions in moo.el automagically open up windows to edit verbs and properties (aka fields): 150 | 151 | ``` 152 | Esc x moo-get-verb #object:verbname [enter] 153 | and 154 | Esc x moo-get-field #object.propertyname [enter] 155 | ``` 156 | 157 | These commands also pull a copy of the verb or property contents into the buffer. Once you've finished editing the verb or property, you can send it back to the MOO with: 158 | 159 | ``` 160 | Esc x mud-macro-send [enter] 161 | ``` 162 | 163 | ## Editing Long List Properties 164 | 165 | A property is basically one line, often an extremely long line. If you're editing a property that's a list of elements or strings, separated by commas, there are useful commands for breaking the list up into separate lines for editing and recombining them into a list for sending: 166 | 167 | ``` 168 | Esc x moo-explode-list [enter] 169 | Esc x moo-implode-list [enter] 170 | ``` 171 | 172 | If the list is a list of strings, with quotes (") at either end, use: 173 | 174 | ``` 175 | Esc x moo-explode-message [enter] 176 | Esc x moo-implode-message [enter] 177 | ``` 178 | 179 | This will strip off the quotes until you're ready to implode the message. 180 | 181 | ## Quoting text 182 | 183 | It's often very useful to clip out text and send it to a mud, to "quote" it to other players who are listening, or to send a long series of commands, or even to send a mail message inside MOO. Once you've put together the text you're going to send, clip or copy it into the kill ring (see below for details on doing this) then use: 184 | 185 | ``` 186 | Esc x mud-send-kill [enter] 187 | ``` 188 | 189 | This sends the text verbatim. If you wanted to put a command in front of every line, for example you wanted to "say" or "emote" every line, you could use: 190 | 191 | ``` 192 | Esc x mud-send-kill-prefix [enter] 193 | Prefix: say [enter] 194 | Suffix: [enter] 195 | ``` 196 | 197 | ## @quicksending mail 198 | 199 | If you're like me, you don't like using Lambda's mail editor (nice though it is, and kudos to Rog for coding it) to edit your mail. You can edit it in another buffer, place "@quicksend recipient-name" at the beginning and "." at the ending, and use x mud-upload-buffer or mud-send-kill to send it. 200 | 201 | ## Nifty Things EMACS can do 202 | 203 | EMACS is a very powerful and useful editor. I even use an EMACS clone (JOVE - Jonathan's Own Version of EMACS) on my PC. To really learn about it you should dig up a GNU EMACS manual or file or try 'learn EMACS' or te EMACS help function (Esc x help-for-help). GNU EMACS (in my opinion the best version, although it devours UP CPU and is a real memory hog) is available free on the Internet. You can FTP the documentation files from numerous archive sites (wuarchive.wustl.edu is my favorite site). 204 | 205 | ## Quitting EMACS 206 | 207 | Methods for quitting from EMACS vary depending on how the version in use is set up. Ctrl-c c, ctrl-x c, ctrl-c ctrl-c, etc, often do the job. When I can't do it any other way, this almost always works: 208 | 209 | ``` 210 | Esc x kill-emacs [enter] 211 | ``` 212 | 213 | ## Logging 214 | 215 | If you decide you want to save a log of a mud session, do: 216 | 217 | ``` 218 | Ctrl-x Ctrl-w 219 | Write file: ~/filename 220 | ``` 221 | 222 | ## Moving Around 223 | 224 | Sometimes you can use the keyboard arrow keys to move around, sometimes not. When you can't, remember these commands: 225 | 226 | ``` 227 | Ctrl-f forward one space. 228 | Ctrl-b backwards one space. 229 | Ctrl-p previous line. 230 | Ctrl-n next line. 231 | Ctrl-a beginning of line. 232 | Ctrl-e end of line. 233 | Ctrl-d deletes a character the cursor is on. 234 | ``` 235 | 236 | The mud session takes any line you press [enter] on as input. So if you mistype something, you can easily use Ctrl-p to go back up, Ctrl-b or Ctrl-f to get to your mistake, and Ctrl-d or backspace to fix it, then press [enter] and send it again. When I want to repeat an action many times however, I kill it and yank it back multiple times (see cutting and pasting, below). 237 | 238 | ## Macro-movement Commands 239 | 240 | There are also some useful commands for moving around the buffer by large steps: 241 | 242 | ``` 243 | Ctrl-v move down one screenfull. 244 | Esc v move up one screenfull. 245 | Esc < move to beginning of file. 246 | Esc > move to end of file. 247 | ``` 248 | 249 | ## Cutting, Pasting, Etc 250 | 251 | A couple of basic concepts; mark, region, kill, kill ring. 252 | 253 | The mark is just that, a mark. You usually can't see it. Sometimes it's represented by a highlighted space, sometimes not. The Ctrl key to set it varies from terminal to terminal, so I just remeber Esc x set-mark and I have no problems. 254 | 255 | The region is everything between your cursor and wherever the mark was last set. Everything. This is very useful for deleting or changing large areas of text, but can be dangerous if you forget where the mark last was. 256 | 257 | Killing a line means deleting from the cursor to the end of the line. To really delete the whole line you have to do it twice - once to kill the text, another time to kill the empty space. Ctrl-k usually does kill this. You can also kill an entire region with Ctrl-w. This is something you have to be careful about, or you could end up killing large regions you wanted to keep. Fortunately you have the kill ring. 258 | 259 | Killed text all goes into the kill-ring. You can "yank" back all of the text that you last killed in one "contiguous" batch with Ctrl-y. This means that if you kill five lines in a row by pressing Ctrl-k ten times, Ctrl-y will yank them all back. But if you press Ctrl-k ten times, then use Ctrl-n to move somewhere else and do Ctrl-k four more limes, you're only going to be able to yank back the last batch (the four lines) you killed. 260 | 261 | If you don't want to kill anything, you can just set the mark, move to the end of the area you want, and do Esc x copy-region to copy those lines into the kill ring just as if you'd killed them. 262 | 263 | There are commands for moving around the kill ring; check EMACS help or your EMACS manual. 264 | 265 | ``` 266 | Esc x set-mark set the mark. 267 | Ctrl-k kill to end of line. 268 | Ctrl-w kill region. 269 | Ctrl-y yank back last contiguous kill. 270 | Esc x copy-region copy region into kill-ring. 271 | ``` 272 | 273 | ## Mud- commands 274 | 275 | ``` 276 | mud-add-world mud-beginning-of-line 277 | mud-cancel-input mud-cleanup-extra-processes 278 | mud-get-text mud-interactive-mode 279 | mud-load-macro-commands mud-load-worlds 280 | mud-macro-abort mud-macro-command 281 | mud-macro-expansion-mode mud-macro-label 282 | mud-macro-send mud-macro-send-and-destroy 283 | mud-macro-send-slowly mud-mode 284 | mud-next-command mud-output-here 285 | mud-parse-entry-file mud-previous-command 286 | mud-previous-matching-command mud-quit 287 | mud-realign-and-send mud-recover-last-macro 288 | mud-remove-macro mud-retarget 289 | mud-send mud-send-kill 290 | mud-send-kill-prefix mud-shell-buffer 291 | mud-store-macro-commands mud-upload-buffer 292 | mud-upload-file mud-upload-file-as-lambda-mail 293 | mud-upload-file-straight 294 | ``` 295 | 296 | ## This file was modified from it's original to include updated markdown formatting. The original can be found in the /src directory 297 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Lambda MOO Programming 2 | 3 | Table of Contents 4 | ================= 5 | 6 | * [Introduction](#introduction) 7 | * [Local Setup](#local-setup) 8 | * [Contents](#contents) 9 | * [ToastStunt Programmer's Manual](#toaststunt-programmers-manual) 10 | * [Lambda MOO Programmer's Manual \[Updated\]](#lambda-moo-programmers-manual-updated) 11 | * [Learn MOO Programming Videos](#learn-moo-programming-videos) 12 | * [Yib's Pet Rock](#yibs-pet-rock) 13 | * [LambdaMOO Programming Tutorial (Steven Owens)](#lambdamoo-programming-tutorial-steven-owens) 14 | * [Colin's Way Easy intro Guide to MOO Programming](#colins-way-easy-intro-guide-to-moo-programming) 15 | * [Winding Duck](#winding-duck) 16 | * [MOO Progrmaming Tips](#moo-programming-tips) 17 | * [Hacking LambdaMOO Server Nodak](#hacking-lambdamoo-server-nodak) 18 | * [Lambda MOO Background Information Nodak](#lambda-moo-background-information-nodak) 19 | * [Lambda MOO Programming Nodak](#lambda-moo-programming-nodak) 20 | * [How to set up a LambdaMOO server on GenesisMud](#how-to-set-up-a-lambdamoo-server-on-genesismud) 21 | * [Getting Started With moo.el](#getting-started-with-moo-el) 22 | 23 | * [Server Patches](#server-patches) 24 | * [Server Setup](#server-setup) 25 | * [Automated Local Environment Setup (Stunt & LambdaCore)](#automated-local-environment-setup-stunt--lambdacore) 26 | * [Useful MOO Code](#useful-moo-code) 27 | * [Web Client](#web-client) 28 | * [Public Web Client](#public-web-client) 29 | * [Notes](#notes) 30 | * [ToDo](#todo) 31 | * [Contributing](#contributing) 32 | * [Author](#author) 33 | 34 | ## Introduction 35 | The primary purpose of this repository is to aggregate and update the existing LambdaMOO Programming Resources that exist. The core of the work done here has been to convert the original MOO Programmer's Manual written by Pavel Curtis to Markdown as well as HTML5 w/ Bootstrap and to update each section of the manual with additional information. 36 | 37 | There are a number of other files included in this repository. Many are scraped from various websites that continue to exist, somehow, to this day. Most are at least 15 years old. We have included them here for posterity, since losing them would be tragic. 38 | 39 | ## Local Setup 40 | ```bash 41 | git clone git@github.com:SevenEcks/the-new-moo-programming-guide.git 42 | ``` 43 | 44 | ### Markdown Conversion 45 | 46 | We use the [html2md](https://github.com/fabianmoronzirfas/to-markdown-cli) library to convert HTML files to markdown on the command line. It can be installed using NPM. 47 | 48 | ```bash 49 | npm install -g to-markdown-cli 50 | html2md -i ./foo.html -o out.md 51 | ``` 52 | 53 | ## Contents 54 | There are two versions of each document in this repository. One is a markdown copy located in [/tutorials](/tutorials) which is accessible on GitHub and one is an HTML version located in [/tutorials/src](/tutorials/src). 55 | 56 | ### ToastStunt Programmer's Manual 57 | This is an updated Programmer's Manual for [ToastStunt](https://github.com/lisdude/toaststunt) (the most cutting edge fork of LambdaMOO). It is a fork of the updated LambdaMOO Manual (see below) which incorpoates the original manual and Todd Sundsted's Stunt updates with regular updates as new features are released for ToastStunt. 58 | 59 | This manual now lives in its own repository. 60 | 61 | [ToastStunt Programmer's Manual](https://github.com/lisdude/toaststunt-documentation/blob/master/manual/toaststunt-programmers-manual.md) 62 | 63 | ### Lambda MOO Programmer's Manual [Updated] 64 | This LambdaMOO Programmer's Manual is an **updated an expanded version** of the *LambdaMOO Programmer's Manual* originally written by Pavel Curtis. Some pretty extensive work has gone into formatting it, and updating the content. 65 | 66 | #### On The Web 67 | This file is important, and thus, it is available online without having to interact with this GitHub at all: [MOO Programmers Manual](https://www.sindome.org/moo-manual.html) 68 | 69 | #### On GitHub 70 | To use this guide you simply need to open [MOO Programmer's Manual](/tutorials/moo-programmers-manual-updated.md). This version of the MOO Programmer's Manual lacks much of the formatting that the HTML5 version below has. It is great for quick perusal, but the HTML version is superior. 71 | 72 | #### Locally 73 | To access this file locally you simply need clone the repository and open [/tutorials/src/moo-programmers-manual-updated.html](/tutorials/src/moo-programmers-manual-updated.html) in your web browser. 74 | 75 | ### Learn MOO Programming Videos 76 | This is a series of videos on installing and learning MOO programming. 77 | 78 | 1. [Installing and Compiling LambdaMOO from Source Code](https://www.youtube.com/watch?v=CmwWF6Hm4iE&t=&index=1&list=PLDRWME7vpHrrHmGJ8Va7GAIbkxg3BkT94) 79 | 80 | 2. [Using LambdaMOO DB and Applying Server Patches](https://www.youtube.com/watch?v=IyxPQFn0kG4&t=&index=2&list=PLDRWME7vpHrrHmGJ8Va7GAIbkxg3BkT94) 81 | 82 | 3. [The Basics of LambdaMOO Programming](https://www.youtube.com/watch?v=kj6ABbvnybI&t=1s&index=3&list=PLDRWME7vpHrrHmGJ8Va7GAIbkxg3BkT94) 83 | 84 | 4. [Create your own Debugger in MOO](https://www.youtube.com/watch?v=s0-cBEpW7JM&t=2s&index=4&list=PLDRWME7vpHrrHmGJ8Va7GAIbkxg3BkT94) 85 | 86 | 5. [Intro to Properties on Object (1/3)](https://www.youtube.com/watch?v=QBnXvtR1qBw&t=0s&index=5&list=PLDRWME7vpHrrHmGJ8Va7GAIbkxg3BkT94) 87 | 88 | 6. [Properties on Objects Continued (2/3)](https://www.youtube.com/watch?v=Lv5ZCjTqrjQ&t=0s&index=6&list=PLDRWME7vpHrrHmGJ8Va7GAIbkxg3BkT94) 89 | 90 | 7. [Properties on Object Concluded (3/3)](https://www.youtube.com/watch?v=HYOsog_og-o&t=0s&index=7&list=PLDRWME7vpHrrHmGJ8Va7GAIbkxg3BkT94) 91 | 92 | 8. [Extending the MOO with Custom Verbs](https://www.youtube.com/watch?v=yYoGHvmo7RU&t=0s&index=8&list=PLDRWME7vpHrrHmGJ8Va7GAIbkxg3BkT94) 93 | 94 | ### Yib's Pet Rock 95 | A great first introduction to Lambda MOO programming. 96 | 97 | #### On GitHub 98 | To use this guide you simply need to open [Yib's Pet Rock](/tutorials/yibs-pet-rock.md). 99 | 100 | #### Locally 101 | To access this file locally you simply need clone the repository and open [/tutorials/src/yibs-pet-rock-non-html5.html](/tutorials/src/yibs-pet-rock-non-html5.html) in your web browser. 102 | 103 | ### LambdaMOO Programming Tutorial (Steven Owens) 104 | This is an excellent guide, the most complete one I've seen aside from the original Lambda MOO Programmer's Manual. It was written, as the title states, by Steven J. Owens. __This guide was updated 03/07/18__ after Steven sent over a fresh link to an updated version of the guide. 105 | 106 | #### On GitHub 107 | To use this guide you simply need to open [LambdaMOO Programming Tutorial by Steven Owens](/tutorials/lambda-moo-steven-owens-guide.md). 108 | 109 | #### Locally 110 | To access this file locally you simply need clone the repository and open [/tutorials/src/dark-sleep-lambdamoo-programming-tutorial-non-html5.html](/tutorials/src/dark-sleep-lambdamoo-programming-tutorial-non-html5.html) in your web browser. 111 | 112 | ### Colin's Way Easy Intro Guide to MOO Programming 113 | A good introduction to MOO programming. This guide gets you through all the basics. 114 | 115 | #### On GitHub 116 | To use this guide you simply need to open [Colin's Way Easy Intro Guide to MOO Programming](/tutorials/lambda-moo-way-easy.md). 117 | 118 | #### Locally 119 | To access this file locally you simply need clone the repository and open [/tutorials/src/way-easy-moo-programming-guide-non-html5.html](/tutorials/src/way-easy-moo-programming-guide-non-html5.html) in your web browser. 120 | 121 | ### Winding Duck 122 | Winding Duck MOO Programmer's Tutorial walks you through, step by step, how to create some simple code, then make it progressively more complicated. 123 | 124 | #### On GitHub 125 | To use this guide you simply need to open [Winding Duck](/tutorials/winding-duck.md). 126 | 127 | #### Locally 128 | To access this file locally you simply need clone the repository and open [/tutorials/src/winding-duck-non-html5.html](/tutorials/src/winding-duck-non-html5.html) in your web browser. 129 | 130 | ### MOO Programming Tips 131 | This looks like it was a guide created for a specific MOO but it has some tips that those new to MOO might find useful. 132 | 133 | #### On GitHub 134 | To use this guide you simply need to open [MOO Programming Tips](/tutorials/zompost-moo-help.md). 135 | 136 | #### Locally 137 | To access this file locally you simply need clone the repository and open [/tutorials/src/zompost-moo-help-non-html5.html](/tutorials/src/zompost-moo-help-non-html5.html) in your web browser. 138 | 139 | ### Hacking LambdaMOO Server Nodak 140 | This document walks you through what happens when a connection is made to the Lambda MOO server. 141 | 142 | #### On GitHub 143 | To use this guide you simply need to open [Hacking LambdaMOO Server](/tutorials/hacking-lambda-moo-server.md). 144 | 145 | #### Locally 146 | To access this file locally you simply need clone the repository and open [/tutorials/src/hacking-lambda-moo-server-non-html5.html](/tutorials/src/hacking-lambda-moo-server-non-html5.html) in your web browser. 147 | 148 | ### Lambda MOO Background Information Nodak 149 | This document contains background information on what LambdaMOO is in relation to other servers and languages. 150 | 151 | #### On GitHub 152 | To use this guide you simply need to open [Lambda MOO Background](/tutorials/lambda-moo-background.md). 153 | 154 | #### Locally 155 | To access this file locally you simply need clone the repository and open [/tutorials/src/lambda-moo-background-non-html5.html](/tutorials/src/lambda-moo-background-non-html5.html) in your web browser. 156 | 157 | ### Lambda MOO Programming Nodak 158 | This document contains information on Lambda MOO programming. It is a short and somewaht technical document without much background information. 159 | 160 | #### On GitHub 161 | To use this guide you simply need to open [Lambda MOO Nodak Edu](/tutorials/lambda-moo-nodak-edu.md). 162 | 163 | #### Locally 164 | To access this file locally you simply need clone the repository and open [/tutorials/src/lambda-moo-programming-tutorial-nodak-edu-non-html5.html](/tutorials/src/lambda-moo-programming-tutorial-nodak-edu-non-html5.html) in your web browser. 165 | 166 | ### How to set up a LambdaMOO server on GenesisMud 167 | This is a step by step guide by Puff the Fractal Dragon on how get LambdaMOO running on GenesisMud. However, for anyone looking to get a general idea of how to get LambdaMOO up and running, this tutorial provides a good reference for the process. 168 | 169 | #### On GitHub 170 | To use this guide you simply need to open [LambdaMOO on GenesisMud](/tutorials/genesismud.md). 171 | 172 | #### Locally 173 | To access this file locally you simply need clone the repository and open [/tutorials/src/genesismud.txt](/tutorials/src/genesismud.txt) in your web browser. 174 | 175 | ### Gettings Started With moo.el 176 | This tutorial by Puff the Fractal Dragon walks you through using emacs to edit MOO code. It also includes some generalized emacs information. 177 | 178 | #### On GitHub 179 | To use this guide you simply need to open [moo.el](/tutorials/mud_moo_el_tutorial.md). 180 | 181 | #### Locally 182 | To access this file locally you simply need clone the repository and open [/tutorials/src/mud_el_tutorial.txt](/tutorials/src/mud_el_tutorial.txt) in your web browser. 183 | 184 | ### Server Patches 185 | I've been worried that we might lose some server patches to time. Also they aren't really gathered anywhere on GitHub, just on [LisDude MOO Resources](http://www.lisdude.com/moo/) and random source forge unmainted LambdaMOO repos. So I'm starting to collect them here. If you have any, throw them up in a pull request please! 186 | 187 | * [FileIO 1.5p3](patches/fileio-1.5p3) (Todd Sundsted version) 188 | 189 | ### Server Setup 190 | 191 | ### Automated Local Environment Setup (Stunt & LambdaCore) 192 | Since I abuse virtual machines for this sort of thing and don't retain the data from them, I have created a repository, [moolite](https://github.com/amnsia/moolite), to reproduce my environment without much typing. It consists of the [Stunt](https://github.com/toddsundsted/stunt) server and the latest LambdaCore database from Brendan's [LambdaMOO](https://github.com/SevenEcks/LambdaMOO) repo. Right now it's a couple of shell scripts, but I will be adding more to it and the quickstart in the README. -- amnsia. 193 | 194 | ### Useful MOO Code 195 | There are some code samples for enterprising developers to check out. It's limited for now, please feel free to put a pull request in for any code you'd like included. 196 | 197 | * [Local Editing](code/LocalEditing.md) (Edit in a pop up window with @edit using Dome-Client web client) 198 | * [Code Scanner](code/CodeScanner.md) (Scan your MOO code for common issues & optimizations) 199 | * [$scheduler](code/SindomeScheduler.txt) (Schedule tasks to run in the future) 200 | 201 | ### Web Client 202 | I've mentioned [Dome Client](https://github.com/JavaChilly/dome-client.js) in the updated MOO Programming Guide. It's a web based MOO client that uses NodeJS and web sockets. It works great, connecting to most LambdaMOO servers. You can run your own. 203 | 204 | #### Public Web Client 205 | Sindome offers a [public web client](http://pubclient.sindome.org/) that can connect to whatever LambdaMOO server you want: http://pubclient.sindome.org/ 206 | 207 | It works great, and if you use the [Local Editing](code/LocalEditing.md) code in this repo, you can write an @edit verb which will let you edit code in a nice pop up window. It's excellent. 208 | 209 | ## Changes 210 | See [CHANGELOG.md](CHANGELOG.md) 211 | 212 | ## Notes 213 | See [NOTES.md](NOTES.md) 214 | 215 | ## TODO 216 | See [TODO.md](TODO.md) 217 | 218 | ## Contributing 219 | Please feel free to fork the repo and contribute via pull request. This is a labor of love. Give back to the community. If you want to edit one of the HTML src files, you can do so, but you should run it through html2md (npm install -g to-markdown-cli) and then move the markdown file into the tutorials directory. 220 | 221 | Example of creating a new version of the markdown file from the HTML 5 source and moving to the appropriate folder: 222 | 223 | ```bash 224 | cd tutorials/src 225 | html2md -i moo-programmers-manual-updated.html -o moo-programmers-manual-updated.md 226 | mv moo-programmers-manual.md ../ 227 | ``` 228 | 229 | ## Author 230 | [Sindome](https://www.sindome.org/) has been online since 1997. I've been playing it since 2003. I've been developing in MOO since 2005, and a developer on Sindome since 2006. Ours is heavily modified, we have roughly 70-80 players online a lot of time time and dozens of people have developed the game over 20+ year run. We've got a ton of best practices and design patterns that we implement in order to keep the MOO happy and healthy. I've attempted to distill as much of that into this document while at the same time present the document in a more up to date format. 231 | 232 | Text based gaming is an important part of our history and our future. No game is more accessible to those with handicaps, for example. 233 | 234 | Come visit me on Sindome if you dare :) 235 | 236 | -- Brendan / Slither / Fengshui 237 | -------------------------------------------------------------------------------- /tutorials/src/mud_el_tutorial.txt: -------------------------------------------------------------------------------- 1 | * Getting started with moo.el * 2 | 3 | by Puff the Fractal Dragon 4 | 5 | This should get you started with the moo.el client, an ELISP module 6 | for GNU EMACS. Moo.el is extremely useful, particularly if you're adept 7 | with GNU EMACS, but this help file is aimed at somebody who's never even 8 | used GNU EMACS before, so DON'T PANIC (kudos to Douglas Adams). 9 | 10 | 11 | NOTE: Many players have contacted me asking where they can get a copy 12 | of mud.el or moo.el. I never discussed this because the help object 13 | is intended to be used by those who already HAVE moo.el but don't know 14 | how to use it. JoeFeedback is the creator and sole distributor of 15 | moo.el. He requests that users do NOT distribute it, as he wishes to 16 | track its use and testing for purposes of further enhancements. 17 | * A Note About Terminology * 18 | 19 | Ctrl-Anything 20 | ...means hold down the Control key and press another key. 21 | 22 | Esc Anything 23 | ...means press the Escape key (or meta key if you're not on an MS-DOS 24 | compatible keyboard), let it up, THEN press another key. 25 | 26 | Esc x blah-blah or Ctrl-X blah-blah 27 | Often I'll use this where blah-blah is a set of commands. You do 28 | either Esc x or Ctrl-x and wait a moment, and either "M-x" or "C-x" will 29 | appear on the bottom line of your screen. Start to type blah-blah. 30 | 31 | Tab and ? 32 | While typing blah-blah, if you press Tab, EMACS will try to complete 33 | the command for you. If you press Tab twice, or press ?, EMACS will list 34 | the possible completions of the command. You can continue typing and using 35 | Tab until you get the entire command, then press [enter]. 36 | 37 | * Starting up * 38 | 39 | Assuming you have GNU EMACS on your system, you start it up by 40 | entering "EMACS [enter]" from the prompt. If you have more experience 41 | with EMACS you can set it up so it automatically loads moo.el, but for 42 | now, use: 43 | 44 | Esc x load-file [enter] moo.el [enter] 45 | 46 | * Connecting * 47 | 48 | Connect to a mud with: 49 | 50 | Esc x mud [enter] mudname [enter]. 51 | 52 | If you don't know the mud's name, you can use completion, or type ? 53 | to get a list of the muds in your .moo_worlds file. 54 | 55 | * The .moo_worlds file * 56 | 57 | If you want to add new muds that aren't in the list, you have to edit the 58 | .moo_worlds file (you should have gotten a basic .moo_worlds file when you 59 | got moo.el). To add new muds to your .moo_worlds file, you can either edit 60 | it directly, or use: 61 | 62 | Esc x mud-add-world [enter] 63 | World name: LambdaMOO [enter] 64 | Login name: player's name [enter] 65 | Password: player's password [enter] 66 | Address: lambda.parc.xerox.com [enter] 67 | Port number: 8888 [enter] 68 | Type: MOO [enter] 69 | 70 | You may find it handy to make more than one entry for your favorite 71 | muds. You can connect to two different characters at once, but only if you 72 | have two different entries for that mud in .moo_worlds. I use LambdaMOO 73 | and lambda, for example. If I tried to use LambdaMOO twice, moo.el would 74 | just switch me back to the LambdaMOO buffer (more about buffers later). 75 | 76 | * Fun with moo.el for MOO * 77 | 78 | Once you've connected, there are several ways moo.el can make your 79 | life easier, particularly if you're connecting to a MOO. The ones I use 80 | most often are multiple buffers, multiple windows, verb and property editing, 81 | and general nifty things you can do with your buffer when you can edit the 82 | whole thing with EMACS commands. 83 | 84 | * Multiple Buffers * 85 | 86 | Like I said up above about .moo_worlds, you can connect to a mud more 87 | than once, using multiple buffers. With multiple buffers you can hop from 88 | one mud connection to another and work on two or more muds, or edit files, 89 | and so on. Buffers are part of EMACS, and are generally nifty things that 90 | you can learn more about with the EMACS manual. One thing to keep in 91 | mind is that the buffer sticks around until you kill it - out of sight does 92 | NOT mean out of mind. 93 | 94 | * Switching Between Buffers * 95 | 96 | You switch between buffers using: 97 | 98 | Ctrl-x b 99 | Switch to buffer: (default: blahblah) [enter] 100 | 101 | If you just hit enter you'll go to the default buffer. The default 102 | buffer is the last buffer you were in, in this window (more about windows 103 | next). For just two buffers, this is fine, you can do Ctrl-x b forever and 104 | keep going back and forth. If you have more than one buffer, you can type 105 | part of the buffer name and use command completion or ? to see the other 106 | buffer names. 107 | 108 | * Opening New Buffers * 109 | 110 | You can also open new buffers to edit text or whatever, simply by 111 | switching to a buffer that isn't there. 112 | 113 | Ctrl-x b 114 | Switch to buffer: newbuffername [enter] 115 | 116 | * Killing Buffers * 117 | 118 | Generally I don't close buffers, I just @quit, or save the file I was 119 | editing, and ignore it until I'm ready to kill the whole EMACS. But if 120 | you want to or need to make it go away, you can use: 121 | 122 | Esc x kill-buffer [enter] 123 | Kill buffer: (default blahblah) 124 | 125 | You can specify which buffer to kill the same way you specify which 126 | buffer to switch to. 127 | 128 | * Uploading Buffers * 129 | 130 | You can send the contents of a buffer to the mud with the command: 131 | 132 | Esc x mud-upload-buffer [enter] 133 | 134 | EMACS will ask you for the buffer to upload, and the mud to upload it 135 | to. You can also use 136 | 137 | * Quitting from a MUD * 138 | 139 | If you want to @quit from a mud, then reconnect (to connect to 140 | another character, for example), I've found you have to @quit, wait a 141 | few seconds until it says: 142 | 143 | |*** Disconnected *** 144 | 145 | then press [enter] a couple of times. It'll say: 146 | 147 | Process MUD<1> exited abnormally with code 256 148 | 149 | And now you can do: 150 | 151 | Esc x mud [enter] mudname [enter] 152 | 153 | and reconnect. 154 | 155 | * Multiple Windows * 156 | 157 | Multiple windows are like multiple buffers (in fact, they usually ARE 158 | multiple buffers), only they're on the same 24 line screen. Right now,as I 159 | type this, I have about 10 lines for this document on the top half 160 | of my screen, and the bottom half contains two windows, one for a LambdaMOO 161 | session and one for a GodNet session. 162 | 163 | * How Many Windows Is Too Many? * 164 | 165 | You can keep opening more windows until all you have left on your screen 166 | is window bars (solid light bars that separate the windows). For the most 167 | part, two windows is optimal to work with, three if two of them aren't too 168 | busy, and four is pushing it. If I have to keep track of more buffers than 169 | this, I generally switch buffers in and out. 170 | 171 | * The Windows Commands * 172 | 173 | The windows commands are generally: 174 | 175 | Ctrl-x 2 separate the present window into two windows. 176 | Ctrl-x 1 shut down all of the windows except the one the cursor is in. 177 | Ctrl-x o hop to the next window. 178 | Ctrl-x ^ (^ is shift-6 on most keyboards) expand the window you're in by 179 | 1 line. This one is tricky, it takes a line away the "closest" 180 | window, a not-always-predictable choice. 181 | 182 | * Verb and Property Editing * 183 | 184 | The MOO verb and property editor functions in moo.el automagically 185 | open up windows to edit verbs and properties (aka fields): 186 | 187 | Esc x moo-get-verb #object:verbname [enter] 188 | and 189 | Esc x moo-get-field #object.propertyname [enter] 190 | 191 | These commands also pull a copy of the verb or property contents into 192 | the buffer. Once you've finished editing the verb or property, you can 193 | send it back to the MOO with: 194 | 195 | Esc x mud-macro-send [enter] 196 | 197 | * Editing Long List Properties * 198 | 199 | A property is basically one line, often an extremely long line. If 200 | you're editing a property that's a list of elements or strings, separated 201 | by commas, there are useful commands for breaking the list up into separate 202 | lines for editing and recombining them into a list for sending: 203 | 204 | Esc x moo-explode-list [enter] 205 | Esc x moo-implode-list [enter] 206 | 207 | If the list is a list of strings, with quotes (") at either end, use: 208 | 209 | Esc x moo-explode-message [enter] 210 | Esc x moo-implode-message [enter] 211 | 212 | This will strip off the quotes until you're ready to implode the 213 | message. 214 | 215 | * Quoting text * 216 | 217 | It's often very useful to clip out text and send it to a mud, to 218 | "quote" it to other players who are listening, or to send a long series 219 | of commands, or even to send a mail message inside MOO. Once you've put 220 | together the text you're going to send, clip or copy it into the kill ring 221 | (see below for details on doing this) then use: 222 | 223 | Esc x mud-send-kill [enter] 224 | 225 | This sends the text verbatim. If you wanted to put a command in front 226 | of every line, for example you wanted to "say" or "emote" every line, you 227 | could use: 228 | 229 | Esc x mud-send-kill-prefix [enter] 230 | Prefix: say [enter] 231 | Suffix: [enter] 232 | 233 | * @quicksending mail * 234 | 235 | If you're like me, you don't like using Lambda's mail editor (nice 236 | though it is, and kudos to Rog for coding it) to edit your mail. You can 237 | edit it in another buffer, place "@quicksend recipient-name" at the 238 | beginning and "." at the ending, and use x mud-upload-buffer or 239 | mud-send-kill to send it. 240 | 241 | * Nifty Things EMACS can do * 242 | 243 | EMACS is a very powerful and useful editor. I even use an EMACS 244 | clone (JOVE - Jonathan's Own Version of EMACS) on my PC. To really 245 | learn about it you should dig up a GNU EMACS manual or file or try 'learn 246 | EMACS' or te EMACS help function (Esc x help-for-help). GNU EMACS (in my 247 | opinion the best version, although it devours UP CPU and is a real memory 248 | hog) is available free on the Internet. You can FTP the documentation 249 | files from numerous archive sites (wuarchive.wustl.edu is my favorite site). 250 | 251 | * Quitting EMACS * 252 | 253 | Methods for quitting from EMACS vary depending on how the version in 254 | use is set up. Ctrl-c c, ctrl-x c, ctrl-c ctrl-c, etc, often do the job. 255 | When I can't do it any other way, this almost always works: 256 | 257 | Esc x kill-emacs [enter] 258 | 259 | * Logging * 260 | 261 | If you decide you want to save a log of a mud session, do: 262 | 263 | Ctrl-x Ctrl-w 264 | Write file: ~/filename 265 | 266 | * Moving Around * 267 | 268 | Sometimes you can use the keyboard arrow keys to move around, 269 | sometimes not. When you can't, remember these commands: 270 | 271 | Ctrl-f forward one space. 272 | Ctrl-b backwards one space. 273 | Ctrl-p previous line. 274 | Ctrl-n next line. 275 | Ctrl-a beginning of line. 276 | Ctrl-e end of line. 277 | Ctrl-d deletes a character the cursor is on. 278 | 279 | The mud session takes any line you press [enter] on as input. So if 280 | you mistype something, you can easily use Ctrl-p to go back up, Ctrl-b 281 | or Ctrl-f to get to your mistake, and Ctrl-d or backspace to fix it, 282 | then press [enter] and send it again. When I want to repeat an action 283 | many times however, I kill it and yank it back multiple times (see 284 | cutting and pasting, below). 285 | 286 | * Macro-movement Commands * 287 | 288 | There are also some useful commands for moving around the buffer by 289 | large steps: 290 | 291 | Ctrl-v move down one screenfull. 292 | Esc v move up one screenfull. 293 | Esc < move to beginning of file. 294 | Esc > move to end of file. 295 | 296 | * Cutting, Pasting, Etc * 297 | 298 | A couple of basic concepts; mark, region, kill, kill ring. 299 | 300 | The mark is just that, a mark. You usually can't see it. Sometimes 301 | it's represented by a highlighted space, sometimes not. The Ctrl key to 302 | set it varies from terminal to terminal, so I just remeber Esc x set-mark 303 | and I have no problems. 304 | 305 | The region is everything between your cursor and wherever the mark was 306 | last set. Everything. This is very useful for deleting or changing large 307 | areas of text, but can be dangerous if you forget where the mark last was. 308 | 309 | Killing a line means deleting from the cursor to the end of the line. 310 | To really delete the whole line you have to do it twice - once to kill the 311 | text, another time to kill the empty space. Ctrl-k usually does kill this. 312 | You can also kill an entire region with Ctrl-w. This is something you have 313 | to be careful about, or you could end up killing large regions you wanted to 314 | keep. Fortunately you have the kill ring. 315 | 316 | Killed text all goes into the kill-ring. You can "yank" back all of 317 | the text that you last killed in one "contiguous" batch with Ctrl-y. This 318 | means that if you kill five lines in a row by pressing Ctrl-k ten times, 319 | Ctrl-y will yank them all back. But if you press Ctrl-k ten times, then 320 | use Ctrl-n to move somewhere else and do Ctrl-k four more limes, you're 321 | only going to be able to yank back the last batch (the four lines) you 322 | killed. 323 | 324 | If you don't want to kill anything, you can just set the mark, move to 325 | the end of the area you want, and do Esc x copy-region to copy those lines 326 | into the kill ring just as if you'd killed them. 327 | 328 | There are commands for moving around the kill ring; check EMACS help 329 | or your EMACS manual. 330 | 331 | Esc x set-mark set the mark. 332 | Ctrl-k kill to end of line. 333 | Ctrl-w kill region. 334 | Ctrl-y yank back last contiguous kill. 335 | Esc x copy-region copy region into kill-ring. 336 | 337 | * Mud- commands * 338 | 339 | mud-add-world mud-beginning-of-line 340 | mud-cancel-input mud-cleanup-extra-processes 341 | mud-get-text mud-interactive-mode 342 | mud-load-macro-commands mud-load-worlds 343 | mud-macro-abort mud-macro-command 344 | mud-macro-expansion-mode mud-macro-label 345 | mud-macro-send mud-macro-send-and-destroy 346 | mud-macro-send-slowly mud-mode 347 | mud-next-command mud-output-here 348 | mud-parse-entry-file mud-previous-command 349 | mud-previous-matching-command mud-quit 350 | mud-realign-and-send mud-recover-last-macro 351 | mud-remove-macro mud-retarget 352 | mud-send mud-send-kill 353 | mud-send-kill-prefix mud-shell-buffer 354 | mud-store-macro-commands mud-upload-buffer 355 | mud-upload-file mud-upload-file-as-lambda-mail 356 | mud-upload-file-straight 357 | -------------------------------------------------------------------------------- /tutorials/zompost-moo-help.md: -------------------------------------------------------------------------------- 1 | 2 | ## Some tips on SpinnMoo programming 3 | 4 | I'm still trying to get the hang of the LambdaMoo programming language. I thought I'd share some of what I've figured out, for both people who can program and for those who can't (yet). 5 | 6 | This page isn't by any means complete. For that, see the documents on [Shawn's page][1]: the _LambdaCore User's Manual_, the _LambdaMOO Programmer's Manual, _and the _LambdaCore Database Programing Manual. 7 | 8 | --Zompist_ 9 | 10 | --- 11 | 12 | ### Basic objects 13 | 14 | Most of you have figured out how to make objects; in case you haven't, the basic command looks like this: 15 | 16 | > @create $thing named Maxwell's Silver Hammer,hammer 17 | > 18 | 19 | The new item will have a full name of Maxwell's Silver Hammer, but can be referred to as hammer for short. An item can have multiple **_aliases_**, separated by commas; you can also add aliases later with the @addalias command. 20 | 21 | If you want a container instead, say $container instead of $thing. 22 | 23 | You probably want to add a description that the user will see if she uses the look command: 24 | 25 | > @describe hammer as "It's silver, and small, and really really dangerous." 26 | > 27 | 28 | Now, you want it to do something! How? 29 | 30 | ### Basic verbs 31 | The simplest thing you might want to do is nudge the object and have it respond. This is easy enough, though at first the statements will seem cryptic. First, we **define the verb**: 32 | 33 | > @verb rabbit:nudge this none none 34 | > 35 | 36 | That tells the moo to create a new verb nudge for the rabbit, and that the usage will be nudge rabbit. (Moo stupidly doesn't understand articles, tho' I suppose you could define an alias the rabbit for your rabbit.) If you're an accurate typist, you can go on to **program the verb** like this: 37 | 38 | > @program rabbit:nudge 39 | > 40 | > player:tell( "You nudge the rabbit. It squeaks in dismay." ); 41 | > 42 | > player.location:announce( player.name, " nudges the rabbit. It squeaks in dismay." ); 43 | > 44 | > . 45 | > 46 | 47 | ### Editing 48 | With any luck the moo will tell you that it successfully compiled the verb, and you're in business---you can test your program by typing nudge rabbit. More likely, though, you typed something wrong and the damn moo will bleat unhelpfully. If you've never programmed at all, you'll be amazed at the anality of the computer; it cannot make the simplest corrections (e.g. you typed player:location.announce instead of player.location:announce; or you spelled name as nmae). 49 | 50 | So, I edit my verbs with the **editor**, which you enter like this: 51 | 52 | > @edit rabbit:nudge 53 | > 54 | 55 | This will take you into a vintage-1970s line editor. You can type the program in the same way, but preceding each line with a quote mark, and omitting the final period: 56 | 57 | > "player:tell( "You nudge the rabbit. It squeaks in dismay." ); 58 | > 59 | > "player.location:announce( player.name, " nudges the rabbit. It squeaks in dismay." ); 60 | > 61 | 62 | Type list to list the program so far. Type list 3 to list just line 3; del 3 to delete it; s/fred/joe/3 to replace fred with joe on line 3\. For more commands, see the _Programming Manual_. 63 | 64 | You type save to attempt to compile it. The moo may well bleat again; but the big advantage of @edit over @program is that you don't have to retype your program (an amusement which quickly gets old), just correct it. 65 | 66 | ### What was that masked command? 67 | It's worth understanding those player commands, because you'll be using them over and over. player:tell prints a message to the _player_, which is the character that entered the command. Player.location:announce prints a message to everyone in the player's room _except _the player. 68 | 69 | As in the example, you often want these two messages to differ. The message to others should include the player's name, not 'you'; you get at it with player.name. 70 | 71 | What is printed appears in parentheses. You can print several things at once, including numbers, text, variables, and more exotic things. You just include them all in the parentheses, separated by commas. 72 | 73 | Text appears in quotes: "This object rocks." If you're not a programmer, it's very easy to leave out the quotes, or just one of them. Don't do that; the moo will complain. 74 | 75 | Like most statements, the command ends in a semicolon. 76 | 77 | ### Remembering values 78 | The first step to making the verb more interesting is usually to make the object remember certain values between iterations. For instance, the Glass Dildo remembers whether it's on or off. Remembered things are called **properties**, and you define them like this: 79 | 80 | > @property dildo:on 1 81 | > 82 | > @property radio:lastuser \#233 83 | > 84 | > @property topiary:form "a giant fish" 85 | > 86 | 87 | Unlike 'real' programming languages, moo doesn't require variables to be typed or declared; it just figures out what they are from what you put in them: in the example, respectively, a number, an object (object references are always preceded by \# in moo), and a string (that's programmerese for 'text'). 88 | 89 | Now, **within a verb's code**, you refer to the object's properties with this: e.g. 90 | 91 | > this.on = 1; 92 | > this.lastuser = player; 93 | > this.form = iobjstr; 94 | > 95 | 96 | From this you can figure out that player.name is a property---all players have a name---and player.location is another property, one that points to the room that currently contains the player. The _Database Manual_ gives a full listing of all the properties that various canned objects (like players, rooms, containers, and exits) have. 97 | 98 | ### Varying the description 99 | Very possibly your property is so important you want it to be part of the object's description---you want it to be included when the player looks at your object. (Note that this is the only thing you _can_ do with my Slow Glass object!) This is not hard to do, though it will seem cryptic. First, you @describe the object as usual---this should provide the invariant part of the description. Then you tell the moo that the object has a variable description: 100 | 101 | > @verb glass:description this none this 102 | > 103 | 104 | Now you edit the :description verb. An example might be a radio:description verb: 105 | 106 | > basic = pass(@args); 107 | > 108 | > if (this.on == 1) 109 | > 110 | > basic = basic + " The radio is on." ; 111 | > 112 | > else 113 | > 114 | > basic = basic + " The radio is off." ; 115 | > 116 | > endif 117 | > 118 | > return basic; 119 | > 120 | 121 | We are overriding the default action of the description command, normally done by the parent object (the generic thing, generic container, or whatever). The first line executes the default routine---pass passes the parameters of our call to the parent---in this case returning the default description to us, the one we set using @describe. 122 | 123 | Now we add to this description. The + operator of course adds two strings together into a longer string. Note that we're testing our property, this.on. Don't forget the semicolons. 124 | 125 | Finally, we return the completed string, basic, because description expects a string value to be returned (passed back for printing). 126 | 127 | Note that description doesn't use player:tell; this will be done for us somewhere else. 128 | ** 129 | 130 | Verb parameters 131 | ** 132 | 133 | If you're used to programming, moo verbs are frustratingly **limited in parameter handling**. (Actually the limitation is on the parser rather than the verbs themselves.) Basically you can only write verbs on the following models: 134 | 135 | > Eat pill 136 | > Turn radio on 137 | > Give hernia to zompist 138 | > Poke k-man with sharp stick 139 | > 140 | 141 | In effect all verbs have **three parameters** (called _direct object, preposition, _and _indirect object_). One of the objects must refer to the object, and the preposition is limited to a short list of predefined words. 142 | 143 | When you define a verb, you do it **backwards**. You're going to _say_ "eat pill", but you _define_ it as "pill:eat". 144 | 145 | When you create the verb, you always have to specify all three parameters. An unused parameter is **none**; one that refers to the object itself is **this**; one that refers to something else is **any**. The above verbs would be defined this way: 146 | 147 | > @verb pill:eat this none none 148 | > @verb radio:turn this on none 149 | > @verb hernia:give this to any 150 | > @verb stick:poke any with this 151 | > 152 | 153 | Each definition has **one this parameter**. You may be tempted to leave it out (@verb pill:eat none none none), but then the parser will never be able to call your verb! Think about the commands again, e.g. Eat pill. If it's a command for the pill object, the word pill has to occur somewhere in the command-- otherwise, the parser simply does not have any reason to pass the command to the pill at all. 154 | 155 | For the **simplest commands**, then, the parameter list should read this none none. 156 | ** 157 | 158 | What if I defined it wrong? **You can use @rmverb. I tend to forget the parameters at first, then have to user @rmverb to get rid of the uncallable verb, as in this session: 159 | 160 | > @verb pill:eat 161 | > 162 | > "D'oh! 163 | > 164 | > @rmverb pill:eat 165 | > 166 | > @verb pill:eat this none none 167 | > 168 | 169 | Some verbs, like _turn on _or _act up_, sound better with an **objectless preposition**. (Linguistically it's a particle, but moo calls it a preposition, so there.) You want to say Turn radio on, not Turn radio. You do this with the this (preposition) none parameter list. Note that the parser is not smart enough to let you say Turn on radio. 170 | 171 | Often you want **two objects**---normally because you want your object to act on some other object. In this case you **must** define a preposition in between (from the list in the _Programming Manual_). You can't have a command that reads Give zompist money; it has to be Give money to zompist. That's why I had to define odd syntax like Play piano with "Greensleeves" or Drive bus to east. 172 | 173 | In this case you can either specify the exact preposition in the verb (this on any), or allow any preposition (this any any). 174 | 175 | You can put the other object (the any parameter) either as **direct or indirect object**. It's just a matter of what command sounds better. Compare the following verbs for the Sharp Stick: 176 | 177 | > @verb stick:poke any with this -\> "Poke zompist with stick." 178 | > @verb stick:poke this at any -\> "Poke stick at zompist." 179 | > 180 | 181 | When you **edit** a verb, you don't have to repeat the parameters, which is a blessing. You can @define a verb as shed:get any off this (quick quiz! What command will you use with that?), but to edit the code all you need to say is @edit shed:get. 182 | ** 183 | 184 | Can I have multiple variations of a verb**? Sure---just in case you really really want to be able to say **both** "poke zompist with stick" and "poke stick at zompist". Just @define each variation, with its own parameter list. Now you'll have to specify the parameter list when you edit. A warning, though: I don't think it's worth it. It's no use trying to pretend to the user that she's typing ordinary English; and you're just likely to confuse or frustrate yourself with the extra parameters and verb versions. 185 | ** 186 | 187 | Can I have more parameters? **Sort of. What you do is parse the third argument (the indirect object, available inside the program as the string **iobjstr**) yourself. For instance, maybe you want to say listplayers game from 1 to 10. You have to define this as @verb game:listplayers this from any. You'll get "1 to 10" as the iobjstr, which you can then parse yourself. There are functions to help you do this. It's a bit of a hassle, though, and your life will be easier if you define separate verbs or something. 188 | ** 189 | 190 | I want a string as the third parameter. **No problem; the parser takes care of this for you. The piano uses this: I defined 'play' as @verb piano this with any. If the player types Play piano with the music of Burt Bacharach, the parser takes the entire rest of the sentence, "the music of Burt Bacharach", as the indirect object, and places it in iobjstr. Since I simply want to treat it as a string, I don't need to do anything special. 191 | 192 | ### Error handling 193 | It's not terribly difficult to write a program that handles correct input. The mark of a professional program, however, is how it handles incorrect input. If you don't think about this, your verb will sometimes crash embarrassingly, or produce silly results, or even corrupt your objects. 194 | 195 | The parser will handle some errors for you: misspelling required words (such as the names of your objects or verbs), leaving parameters out; referring to objects that aren't there. 196 | 197 | Some error conditions to think about: 198 | 199 | * The state you're setting is already set. E.g. you turn the dildo on, and it's already on. 200 | * The property you're checking is empty. E.g. the topiary doesn't have a form right now. 201 | * You wanted an object as a parameter but got garbage. You can test this with (say) iobj == \#0 for this. 202 | * You wanted a player and got an object. You can test this with is\_player(iobj). 203 | * The property you're remembering no longer applies. For instance, the piano remembers who's hiding under it. But if that person has left the room, we have to consider the property to be null. 204 | * You want the object (e.g. the sharp stick) to act on another object; what if the object supplied is yourself, or the object itself? 205 | 206 | In general these conditions are handled by if statements. For instance, a radio:on verb might look like this: 207 | 208 | > if (this.on == 1) 209 | > 210 | > player:tell( "The radio is already on!" ); 211 | > 212 | > else 213 | > 214 | > player:tell( "You turn on the radio." ); 215 | > 216 | > this.on = 1; 217 | > 218 | > endif 219 | > 220 | 221 | As usual, you can't deviate from the exact syntax the moo wants. The condition has to be in parentheses; there's no semicolon after the if, else, or endif; the endif must exist, even for a one-line statement. 222 | 223 | Very confusing if you're not a C/C++ programmer: **testing** a value requires a **double** equals sign (this.on == 1); **setting** one requires a **single** equals (this.on = 1). You can look at a misbehaving verb for minutes on end without noticing an error like this. (Not-equals is !=, by the way.) 224 | 225 | The else part is optional. You can also have any number of elseif (another condition) blocks before the else. 226 | 227 | ### Some notes for C++ programmers 228 | You'll see a strong C influence on the moo language. Compound statements, such as if/endif, however, look more like Basic. 229 | 230 | The language is object-oriented, with inheritance. Note that the @create command specifies the base class of the newly created object. Verbs correspond to methods (but you use object:method, not object::method), and properties to data members (using the same . as in C/C++). 231 | 232 | Can you define a verb that's not called by a player, but only by other verbs? Sure. It uses a special parameter list: this none this, which the player cannot enter (if there's no preposition the parser just quits). The dartboard's hit command works like this. It's defined by @verb dartboard:hit this none this and called by the dart:toss routine with the line 233 | 234 | > \#278:hit(); 235 | > 236 | 237 | \#278 is the object number of the dartboard (object numbers are a special data type; think of them as pointers to objects). You can use this anywhere, to make sure the moo understands you whether or not the dartboard is around. It's better programming practice, of course, to avoid using the same number all over---it means you couldn't use the object in another moo, for instance. If we needed to refer to it often, it'd be better to put the dartboard number in a property of the darts, for instance. 238 | 239 | There's fairly nice list (array) support, and lists can include any other type, including other lists, as members. 240 | 241 | One little gotcha: the C construction cond ? texpr : fexpr morphs in moo into cond ? texpr | fexpr. 242 | 243 | Most things the player can do on the command line can also be done by the programmer within a verb-- sometimes with a slightly different verb or syntax. For a full exploration of this, see the _Database Manual_. 244 | 245 | Don't worry about prettyprinting your program. Moo stores it in an interpreted form, and supplies its own indents and spacing when you next edit it. 246 | 247 | ### A few examples 248 | You may be interested in how I got some of the effects in the existing SpinnMoo toys. You can inspect the program code with @edit to see exactly how something was done. 249 | ** 250 | 251 | Dildo**. The dildo remembers its state in an on property. It also remembers the last user; I defined a lastuser property pointing to an object, and set it to player when appropriate. The delayed action is accomplished using a fork command; see the _Programming Manual_. 252 | ** 253 | 254 | Piano**. The piano isn't really a container; it just remembers who is hiding under it (using a hider property). The description is modified to make this seem more real. Note the error handling in the hide command: there can only be one hider at a time, so the previous hider (if it's not the player!) must be ejected. We cannot prevent the hider from leaving the room, so the description and other commands must check to see that the hider is really still there. We do this by checking if hider.location == this.location-- if the hider is in the same room as the piano. The play command uses random to determine the player's style and the onlookers' reaction. 255 | ** 256 | 257 | Slow glass**. This is mostly implemented in a huge description command. There are three properties that increment (at different speeds, so they don't overlap) on each call: location, weather, and time of day. There's also a direction, 1 or --1, which determines whether location is incremented or decremented. 258 | ** 259 | 260 | Readme**. The readme object simply contains couple of list properties, items and signed. The first contains the items added to the bulletin board; the second contains the players' names. 261 | ** 262 | 263 | Dartboard**. This was the most complicated programming task. The dartboard remembers the current scores (as pair of lists, the players and their scores) and contains verbs to reset the scores, set the target, print out the scores, or retrieve the darts, which may have wandered to another room. There is also a hidden verb, hit, which is called by the darts when you toss them. This verb makes heavy use of the random function to vary the results. It throws three darts, totals them up, adjusts the scores, determines winners, and on the second and higher rounds passes the darts to the next player. 264 | ** 265 | 266 | VW Microbus**. This required wizard status to fully implement; but the actual coding is straightforward. The bus is actually a separate room which stays in one place; the "bus" object you can pick up and carry around (and which moves around the moo as the bus is driven) is not even a container, and its enter verb just teleports the player to the separate room. The room has a single exit, whose destination point is reset as the player drives. The bus room itself is, of course, not inside another room at all; we just remember where it is as a property. The ways() function tells us what exits a room has; the _Database Manual_ can be ransacked for tidbits like this. The difficulty here was not so much writing the code, as in being careful about what needed to be done. For instance, as the bus moves, the bus object not only has to be moved, but messages have to be printed in the previous and new locations so users see the bus coming through. 267 | 268 | --- 269 | 270 | # The sourcecode for this file was taken from [http://www.zompist.com/moohelp.htm][0] and is included in this repository for posterity. It is non-HTML5\. 271 | 272 | [0]: http://www.zompist.com/moohelp.htm 273 | [1]: http://moo.collapsar.cx/ 274 | -------------------------------------------------------------------------------- /tutorials/winding-duck.md: -------------------------------------------------------------------------------- 1 | # Winding Duck MOO Programmer's Tutorial 2 | 3 | * [**Chapter 1: A Simple 4 | Object**](#A1) 5 | * [Introduction](#introduction) 6 | * [Simple Wind](#simple-wind) 7 | * [Simple Messages](#simple-messages) 8 | * [Simple Drop](#simple-drop) 9 | 10 | * [**Chapter 2: A More Complex 11 | Object**](#chapter-2-a-more-complex-object) 12 | * [Complex Wind](#complex-wind) 13 | * [Complex Messages](#complex-messages) 14 | * [Complex Drop](#complex-drop) 15 | 16 | * [**Chapter 3: Other Programming 17 | Issues**](#chapter-3-other-programming-issues) 18 | * [Description](#description) 19 | 20 | ### Introduction 21 | This is a programming example of reasonable 22 | complexity. We are going to demonstrate property use, 23 | forking, and generic object use. 24 | 25 | Our example is a wind-up toy. The basic things that a 26 | wind-up toy does is get wound, and then slowly wind 27 | its way down, hopping or rolling or twirling along. 28 | 29 | First, let's create a couple of objects to work from. 30 | 31 | 32 | @create $thing named Generic Wind-Up Toy,Toy 33 | You now have Generic Wind-Up Toy (aka Toy) with object number #12221 34 | and parent generic thing (#5). 35 | 36 | @create #12221 named Wind-Up Duck,Duck 37 | You now have Wind-Up Duck (aka Duck) with object number #12222 and 38 | parent Generic Wind-Up Toy (#12221). 39 | 40 | 41 | We'll refer to these as Toy and Duck, the aliases we gave in 42 | the @create command. 43 | 44 | ### Simple Wind 45 | First, we need some way to tell if the toy has 46 | been wound up. We'll create a property called "wound" 47 | on the generic toy. 48 | 49 | 50 | @property toy.wound 0 51 | Property added with value 0. 52 | 53 | 54 | Before we can write our program, we need a verb... We want to 55 | give commands like "wind duck" so we give it an argument list of 56 | "this". The variable "this", when used inside a verb, refers to the 57 | actual object (e.g. the duck) on which the verb called. Its place 58 | in the argument list designates how the verb will be found by the 59 | built-in parser. 60 | 61 | 62 | @verb toy:wind this 63 | Verb added. 64 | 65 | 66 | Now we can make a simple program to wind the toy. 67 | 68 | 69 | @program toy:wind 70 | this.wound = this.wound + 2; 71 | player:tell("You wind up the ", this.name,"."); 72 | player.location:announce(player.name, " winds up the ", this.name,"."); 73 | . 74 | 75 | 76 | Remember that "this" takes the place of whatever we will type 77 | in place of "this" in our command line, so when the program runs, 78 | it will change the .wound property of the duck. "Player" is the 79 | person who types the command. These are built in variables 80 | available in every program, they just take on different meanings 81 | depending on who types the command and what object is used in the 82 | command. The idea behind adding 2 to this.wound is that you can 83 | wind it a bunch of times to make it go longer. 84 | 85 | Let's try it on our duck: 86 | 87 | 88 | wind duck 89 | You wind up the Wind-Up Duck. 90 | 91 | 92 | Everyone else sees: 93 | 94 | 95 | yduJ winds up the Wind-Up Duck. 96 | 97 | 98 | ### Simple Messages 99 | Next, we want to make the duck actually move 100 | around. Remember that we're doing all the programming 101 | on the generic toy, and using the specific instance of 102 | the duck to test things out. To be properly generic, we 103 | need to define different properties that can hold 104 | message strings for the motions of the toy. So let's 105 | define two messages. One for the toy to start moving, 106 | and another for it to print as it continues to wind 107 | down. 108 | 109 | 110 | @property toy.startup_msg "" 111 | Property added with value "". 112 | 113 | @property toy.continue_msg "" 114 | Property added with value "". 115 | 116 | 117 | We named these with "\_msg" in order for the messages to show 118 | up in @messages, and be settable with the @message\_name object is 119 | "text" command. (See help @messages for more details.) Let's set 120 | values of these messages on the duck. While we're at it, we should 121 | give our duck a description! 122 | 123 | 124 | @startup duck is "waddles about and starts rolling forward." 125 | You set the "startup" message of Wind-Up Duck (#12222). 126 | @continue duck is "swivels its neck and emits a >> Quack <<" 127 | You set the "continue" message of Wind-Up Duck (#12222). 128 | @describe duck as "A yellow plastic duck with wheels at the bottom and a knob 129 | for winding." 130 | Description set. 131 | 132 | 133 | ### Simple Drop 134 | We should be ready to roll now... We're going to 135 | have the wind up toy start up when the player drops it. 136 | This introduces another concept, that of specializing 137 | an existing verb. The generic thing ($thing) defines 138 | the verb :drop. Normally, if we type "drop duck" the 139 | verb on $thing gets invoked on the duck (even though 140 | it's a few levels removed from $thing). If we define a 141 | :drop verb on the generic toy, we don't want to have to 142 | type in all the code from $thing:drop; we just want to 143 | have our new code executed after the normal drop gets 144 | done. In order to get both things, we use the primitive 145 | pass(@args). 146 | 147 | This is the slipperiest concept in MOO programming, 148 | yet the basis for the entire object oriented nature 149 | of the MOO. An easy way to think of pass is like 150 | another verb call: it just does the "basic thing" 151 | that this verb normally does. We can add our 152 | specialization code both before and after this basic 153 | thing. The @args part means just to use the same verb 154 | arguments in the base case as we do in our 155 | specialization. More sophisticated users of pass may 156 | wish to change these arguments; for now, just take it 157 | as gospel. Sometimes you won't want to use pass at 158 | all, if you don't want the basic thing to happen, but 159 | want to completely override the default behavior. 160 | This is OK, you just have to think about what you 161 | want when deciding whether and where to put the call 162 | to pass(@args). 163 | 164 | We want the dropping to happen before any of our 165 | startup, so we call pass(@args) right away. We better 166 | check if the toy has been wound up, and not do 167 | anything special if it hasn't. Next, we print out our 168 | startup message, and schedule some tasks to be run a 169 | little later to print out the progress of our toy. 170 | Let's pick every 15 seconds to print out a message, 171 | and print out as many messages as we have in 172 | this.wound. Each time it actually prints a message, 173 | it will "unwind" a little, so we decrement 174 | this.wound. Note that the stuff between the fork and 175 | the endfork doesn't get done until the fork actually 176 | starts, and the forks start 15, 30, 45, etc. seconds 177 | after all the code in our drop verb has finished. 178 | 179 | Our @verb command looks a little funny, because we're 180 | forced into using some more advanced features of MOO 181 | verbs. If you have a verb named foo\*bar, then in 182 | order to invoke that verb, the user must type at 183 | least "foo", and may type any part of "bar" that they 184 | like; the \* is putting in an abbreviation. 185 | $thing:drop has these abbreviations built in, so you 186 | can just type "d object" to drop something. Also, 187 | drop has a synonym, "throw" (more properly, th\*row). 188 | Putting both verbs in a double-quoted string is the 189 | way to say "two names for this verb". Really, any 190 | number of synonyms are possible. We'll continue to 191 | refer to this verb as "drop", even though we could 192 | just as truthfully refer to it as "throw". We use 193 | these names because we want to name our verb exactly 194 | the same as the one on $thing, so people who prefer 195 | to use "throw" will still get our specializations. 196 | 197 | 198 | @verb toy:"d*rop th*row" this 199 | Verb added. 200 | 201 | @program toy:drop 202 | pass(@args); 203 | if (this.wound) 204 | this.location:announce_all(this.name, " ", this.startup_msg); 205 | for x in [1..this.wound] 206 | fork (x * 15) 207 | this.location:announce_all(this.name," ", this.continue_msg); 208 | this.wound = this.wound - 1; 209 | endfork 210 | endfor 211 | endif 212 | . 213 | 214 | 215 | It's worth noting that 0 in MOO is also "false" as well as 216 | being "zero", which is convenient. Thus we didn't have to use 217 | (this.wound!=0) in our if statement, but could use just 218 | (this.wound). 219 | 220 | Now, let's drop our duck and see how it works! 221 | 222 | 223 | drop duck 224 | You drop Wind-Up Duck. 225 | Wind-Up Duck waddles about and starts rolling forward. 226 | Wind-Up Duck swivels its neck and emits a >> Quack << 227 | Wind-Up Duck swivels its neck and emits a >> Quack << 228 | 229 | 230 | It actually waited 15 seconds in between each of those 231 | messages. I tried it again, but this time I typed @forked right 232 | away to see that the tasks had been scheduled: 233 | 234 | 235 | @forked 236 | Queue ID Start Time Owner Verb (Line) [This] 237 | -------- ---------- ----- ------------------ 238 | 1231283976 Jul 23 15:03:28 1991 yduJ #12221:drop (6) [#12222] 239 | 577459244 Jul 23 15:03:43 1991 yduJ #12221:drop (6) [#12222] 240 | 241 | 242 | --- 243 | 244 | ## Chapter 2: A More Complex Object 245 | How could we improve on our wind-up toy? Or, 246 | rather, what are some of its problems? 247 | 248 | What happens when someone picks up the toy while it's 249 | going? What if you try to drop it somewhere you're 250 | not allowed to? Perhaps we should only allow someone 251 | to wind it up if they are holding it. What if someone 252 | winds it five thousand times? Perhaps we should allow 253 | programmers to make more complicated messages on 254 | their child objects. 255 | 256 | We'll address each of these issues in chapter 2\. 257 | 258 | ### Complex Wind 259 | Let's start with requiring someone to be holding 260 | the duck in order to wind it. We take the code from 261 | before, and stick an if/else/endif around it. 262 | 263 | 264 | @program toy:wind 265 | if (this.location == player) 266 | this.wound = this.wound + 2; 267 | player:tell("You wind up the ", this.name,"."); 268 | player.location:announce(player.name, " winds up the ", this.name,"."); 269 | else 270 | player:tell("You have to be holding the ", this.name,"."); 271 | endif 272 | . 273 | 274 | 275 | That was simple enough... again 'this' is whatever wind-up 276 | toy was wound, and 'this.location' is where the toy is now. If it 277 | is in the player's inventory then this.location will be equal to 278 | the variable 'player'. 279 | 280 | Let's complicate the code once more, and then we'll 281 | be done with the :wind verb. We should have a maximum 282 | number of turns the toy will allow, otherwise a 283 | malicious player could spend a few hours winding the 284 | toy, and then unleash it on an unsuspecting public. 285 | If we wanted to be really fancy, we could make the 286 | toy break if they wound it too far, and require then 287 | to repair it with a screwdriver (which we would 288 | create for the purpose), but let's leave that as an 289 | exercise for the reader. 290 | 291 | We create a property for the maximum number of turns, 292 | so different toys can have different maximums. We'll 293 | give it a default value of 20, though, so it doesn't 294 | have to be set for each new toy. 295 | 296 | 297 | @property toy.maximum 20 298 | Property added with value 20. 299 | 300 | 301 | Now we insert another set of if/else/endifs inside our 302 | current set. 303 | 304 | 305 | @program toy:wind 306 | if (this.location == player) 307 | if (this.wound < this.maximum) 308 | this.wound = this.wound + 2; 309 | player:tell("You wind up the ", this.name,"."); 310 | player.location:announce(player.name, " winds up the ", this.name,"."); 311 | if (this.wound >= this.maximum) 312 | player:tell("The knob comes to a stop while winding."); 313 | endif 314 | else 315 | player:tell("The ",this.name," is already fully wound."); 316 | endif 317 | else 318 | player:tell("You have to be holding the ", this.name,"."); 319 | endif 320 | . 321 | 322 | 323 | In order to add to the feel of the toy, we put in another 324 | test: if, after adding 2 to the wound property, it has reached the 325 | maximum, we tell the player they've come to the end. The more 326 | completely you can describe an object's actions and responses to 327 | actions, the richer the feel of the Virtual Reality. 328 | 329 | ### Complex Messages 330 | Earlier we showed how to make user settable 331 | messages on an object. Now we will show how to enable 332 | another programmer make more complicated messages on 333 | eir windup toy. 334 | 335 | A simple yet very useful method is to define a verb 336 | for each message property, which just returns that 337 | property, and then have all the uses of that property 338 | use the verb instead. For example: 339 | 340 | 341 | @verb toy:continue_msg this none this 342 | @program toy:continue_msg 343 | return this.continue_msg; 344 | . 345 | 346 | 347 | However, it seems almost silly to make a whole bunch of 348 | verbs, one for each property... And indeed it is. The built-in 349 | variable "verb" is always set to the name that this verb was 350 | invoked with, so we can get all the messages in one verb, with this 351 | idiom: 352 | 353 | 354 | @verb toy:"wind_down_msg continue_msg startup_msg going_msg" this none this 355 | @program toy:continue_msg 356 | return this.(verb); 357 | . 358 | 359 | 360 | There's a lot of stuff to explain in this little bit of code! 361 | First, the syntax of the @verb command, with the several messages 362 | listed in a quoted string, indicates that each of those names is an 363 | alias for the verbname, and the verb can be invoked by any of those 364 | names. Second, the builtin variable "verb" is set to whichever name 365 | was actually used. This enables the property reference to be a 366 | simple construction from the verb name, using the expression syntax 367 | for property references, 368 | .(), where in this case expression is a simple 369 | variable reference. 370 | 371 | Here's an example of how we can customize just one 372 | of those messages programmatically with the wind-up 373 | duck. In the continue\_msg we'll make the duck quack 374 | once, twice, or thrice, randomly. We leave the 375 | other messages alone, as we'll just be using static 376 | text. 377 | 378 | 379 | @verb duck:continue_msg this none this 380 | @program duck:continue_msg 381 | times = {"once","twice","thrice"}[random(3)]; 382 | return "swivels its neck and quacks " + times + "."; 383 | . 384 | 385 | 386 | ### Complex Drop 387 | Let's tackle the drop verb now. There are a lot 388 | of problems. First, what happens if someone picks up 389 | the toy while it's still going? The messages still 390 | appear! What should happen? Two ideas come to mind: 391 | 1\. It should fully discharge its windings, as real 392 | life windup toys are wont to do; 2\. It should just 393 | stop running, as though the player had grabbed the 394 | windup knob to prevent it from further discharge. 395 | We'll choose option 2 for our example. Option 1 would 396 | be easiest to do by specializing the existing "take" 397 | verb to reduce the .wound property to 0, printing 398 | appropriate messages. 399 | 400 | The major problem with our drop verb is that it 401 | schedules all its tasks at one time. Once 402 | scheduled, they \*will\* run, whether or not the 403 | situation has changed. To solve this problem, we 404 | only schedule one task at a time, making it the job 405 | of that task to figure out whether it has wound 406 | down, and schedule another task. It should also 407 | check if it has been picked up; if so it should 408 | neither print messages nor schedule the next task. 409 | 410 | Having called this section "Complex Drop", we're 411 | going to defer the complexity to an auxiliary verb, 412 | and make Drop itself simpler. 413 | 414 | 415 | @program toy:drop 416 | pass(@args); 417 | if (this.wound) 418 | this.location:announce_all(this.name, " ", this:startup_msg()); 419 | fork (15) 420 | this:do_the_work(); 421 | endfork 422 | endif 423 | . 424 | 425 | 426 | We will use the special arguments "this none this" in our 427 | @verb command. This is a special kluge that means this verb is not 428 | a command, and cannot be typed at the command line. It also won't 429 | show up in @examine commands. This helps to keep down the clutter 430 | of @examine, and because this verb is only an internal function, it 431 | wouldn't make sense to type as a command line. 432 | 433 | 434 | @verb toy:do_the_work this none this 435 | @program toy:do_the_work 436 | if (this.wound) 437 | if ($object_utils:isa(this.location,$room)) 438 | this.location:announce_all(this.name," ", this:continue_msg()); 439 | this.wound = this.wound - 1; 440 | if (this.wound) 441 | fork (15) 442 | this:do_the_work(); 443 | endfork 444 | else 445 | this.location:announce_all(this.name, " ", this:wind_down_msg()); 446 | endif 447 | endif 448 | if (this.wound < 0) 449 | this.wound = 0; 450 | endif 451 | endif 452 | . 453 | 454 | 455 | Let's go through this step by step. First we check if we're 456 | still wound up. Theoretically this should always be true, but it's 457 | best to check. Assuming .would is true (that is, nonzero), we check 458 | to see if we're in a room. The utility function 459 | $object\_utils:isa(x,y) checks to see if an object X will behave as 460 | an object Y, that is, if Y is in the object's ancestor tree. We do 461 | this because we only want to make our noises if we're wandering 462 | around in a room. A player won't be a room, and so won't pass this 463 | test. Now, we print our message, and decrement this.wound to say 464 | we've wound down once. Next we think about scheduling the next 465 | task. We check this.wound again, because if we subtracted the last 466 | bit, we don't want to bother scheduling it. Note that we call this 467 | very same verb, this:do\_the\_work. Just for extra feeling, we put in 468 | a new message to be printed when the toy has actually wound down. 469 | Oops! Better add that property... And finally, at the very end, 470 | we've gotten all anal retentive with error checking and made sure 471 | that this.wound never gets into negative numbers. (Notice that if 472 | it \*did\* get into negative numbers, it would never stop. So this 473 | check is not entirely pointless!) 474 | 475 | 476 | @property toy.wind_down_msg "" 477 | Property added with value "". 478 | 479 | @wind_down duck is "hiccups once and stops rolling." 480 | You set the "wind_down" message of Wind-Up Duck (#12222). 481 | 482 | 483 | --- 484 | 485 | ## Chapter 3: Other Programming Issues 486 | 487 | ### Description 488 | It might be nice if you could tell by looking at 489 | the toy that it was moving, and not have to wait for 490 | it to tell you so. Like "drop", this is done by 491 | specializing an already existing verb, called 492 | description, by calling pass(@args) and then doing 493 | some additional stuff. There are a number of relevant 494 | differences between this and most other verbs, 495 | however. 496 | 497 | Most verbs do something, that is, when you type a 498 | command, it produces a set of messages, and changes 499 | some properties on some objects. Some verbs, 500 | however, are not commands, and don't even print any 501 | messages. Instead, they return a value to the 502 | calling verb, for that verb to use to produce 503 | messages or change properties. These are analogous 504 | to "functions" in other languages. Description is 505 | one such verb. By default, that is, in the basic 506 | case, it returns the property 'this.description'. 507 | Verbs like 'look' call description on objects and 508 | print out the strings they return. So when we 509 | specialize description, we won't be calling 510 | pass(@args) all by itself, and then printing out 511 | more text, instead we'll call pass(@args), add text 512 | to that, and return the whole pile. 513 | 514 | Again because we're trying to write generically, we 515 | create a property to hold a message to be added to 516 | the description when the toy is running, and we set 517 | it for the duck. 518 | 519 | 520 | @property toy.going_msg "" 521 | Property added with value "". 522 | 523 | @going duck is "The duck is rolling forward with a slight waddle." 524 | You set the "going" message of Wind-Up Duck (#12222). 525 | 526 | 527 | The description verb again needs the "this none this" args, 528 | since it is just an internal verb called by programs. 529 | 530 | 531 | @verb toy:description this none this 532 | Verb added. 533 | 534 | @program toy:description; 535 | basic = pass(@args); 536 | if (this.wound) 537 | return basic + " " + this:going_msg(); 538 | else 539 | return basic; 540 | endif 541 | . 542 | 543 | 544 | Pretty simple code, huh? First we call pass. We store away 545 | that text in a variable 'basic', because we'll need it later. Then 546 | we check to see if we're wound up. If we are, we return the basic 547 | information plus our going\_msg. We put in a couple of spaces to 548 | separate the sentences. Otherwise, we're quiet, and we just return 549 | the basic string. What does it look like? 550 | 551 | 552 | look duck 553 | A yellow plastic duck with wheels at the bottom and a knob for winding. The 554 | duck is rolling forward with a slight waddle. 555 | 556 | 557 | ### Permissions 558 | To allow other people to create their own 559 | wind-up toys, we set the generic toy world-readable 560 | and \`fertile': 561 | 562 | 563 | @chmod toy rf 564 | Object permissions set to rf. 565 | 566 | 567 | This isn't really good enough, due to the screwinesses of the 568 | MOO permission system. Generally speaking, when you create an 569 | object, you become the owner of all its properties as well. This is 570 | called "having c permissions". But that means that someone else 571 | (including the owner of the \*verb\* on the object) can't change the 572 | property. To get around this problem, we set the wound property !c 573 | (meaning "not c"). Then the wound property always belongs to the 574 | owner of the generic toy, and not to the creator of any specific 575 | instance. 576 | 577 | 578 | @chmod toy.wound !c 579 | Property permissions set to r. 580 | 581 | 582 | It tells us what the permissions ended up being, after 583 | stripping off the "c", which is "r" for readable. 584 | 585 | #### This tutorial was taken from [http://netlab.gmu.edu/muve/html/Winding-Duck.html](http://netlab.gmu.edu/muve/html/Winding-Duck.html) and is included in this repository for posterity. 586 | -------------------------------------------------------------------------------- /tutorials/src/zompost-moo-help-non-html5.html: -------------------------------------------------------------------------------- 1 | 2 | Some tips on SpinnMoo programming 3 | 4 | 5 |

The sourcecode for this file was taken from http://www.zompist.com/moohelp.htm and is included in this repository for posterity. It is non-HTML5.

6 | 7 | SpinnMoo 8 |

Some tips on SpinnMoo programming

9 | 10 |

I’m still trying to get the hang of the LambdaMoo programming language. I thought I’d share some of what I’ve figured out, for both people who can program and for those who can’t (yet). 11 | 12 |

This page isn’t by any means complete. For that, see the documents on Shawn’s page: the LambdaCore User's Manual, the LambdaMOO Programmer's Manual, and the LambdaCore Database Programing Manual. 13 | 14 |

--Zompist 15 | 16 | 17 |

Home [ Page o' SpinnStuff ] 18 | 19 |


20 | 21 |

Basic objects

22 | 23 |

Most of you have figured out how to make objects; in case you haven’t, the basic command looks like this: 24 | 25 |

26 |

@create $thing named Maxwell’s Silver Hammer,hammer 27 |

28 | 29 |

The new item will have a full name of Maxwell’s Silver Hammer, but can be referred to as hammer for short. An item can have multiple aliases, separated by commas; you can also add aliases later with the @addalias command. 30 | 31 |

If you want a container instead, say $container instead of $thing. 32 | 33 |

You probably want to add a description that the user will see if she uses the look command: 34 | 35 |

36 |

@describe hammer as "It’s silver, and small, and really really dangerous." 37 |

38 | 39 |

Now, you want it to do something! How? 40 | 41 |

Basic verbs

42 | 43 | The simplest thing you might want to do is nudge the object and have it respond. This is easy enough, though at first the statements will seem cryptic. First, we define the verb: 44 | 45 |
46 |

@verb rabbit:nudge this none none 47 |

48 | 49 |

That tells the moo to create a new verb nudge for the rabbit, and that the usage will be nudge rabbit. (Moo stupidly doesn’t understand articles, tho’ I suppose you could define an alias the rabbit for your rabbit.) If you’re an accurate typist, you can go on to program the verb like this: 50 | 51 |

52 |

@program rabbit:nudge 53 |
player:tell( "You nudge the rabbit. It squeaks in dismay." ); 54 |
player.location:announce( player.name, " nudges the rabbit. It squeaks in dismay." ); 55 |
.
56 |

57 | 58 |

Editing

59 | 60 | With any luck the moo will tell you that it successfully compiled the verb, and you’re in business—you can test your program by typing nudge rabbit. More likely, though, you typed something wrong and the damn moo will bleat unhelpfully. If you’ve never programmed at all, you’ll be amazed at the anality of the computer; it cannot make the simplest corrections (e.g. you typed player:location.announce instead of player.location:announce; or you spelled name as nmae). 61 | 62 |

So, I edit my verbs with the editor, which you enter like this: 63 | 64 |

65 |

@edit rabbit:nudge 66 |

67 | 68 |

This will take you into a vintage-1970s line editor. You can type the program in the same way, but preceding each line with a quote mark, and omitting the final period: 69 | 70 |

71 |

"player:tell( "You nudge the rabbit. It squeaks in dismay." ); 72 |
"player.location:announce( player.name, " nudges the rabbit. It squeaks in dismay." ); 73 |

74 | 75 |

Type list to list the program so far. Type list 3 to list just line 3; del 3 to delete it; s/fred/joe/3 to replace fred with joe on line 3. For more commands, see the Programming Manual. 76 | 77 |

You type save to attempt to compile it. The moo may well bleat again; but the big advantage of @edit over @program is that you don’t have to retype your program (an amusement which quickly gets old), just correct it. 78 | 79 | 80 |

What was that masked command?

81 | 82 | It’s worth understanding those player commands, because you’ll be using them over and over. player:tell prints a message to the player, which is the character that entered the command. Player.location:announce prints a message to everyone in the player’s room except the player. 83 | 84 |

As in the example, you often want these two messages to differ. The message to others should include the player’s name, not ‘you’; you get at it with player.name. 85 | 86 |

What is printed appears in parentheses. You can print several things at once, including numbers, text, variables, and more exotic things. You just include them all in the parentheses, separated by commas. 87 | 88 |

Text appears in quotes: "This object rocks." If you’re not a programmer, it’s very easy to leave out the quotes, or just one of them. Don’t do that; the moo will complain. 89 | 90 |

Like most statements, the command ends in a semicolon. 91 | 92 | 93 |

Remembering values

94 | 95 | The first step to making the verb more interesting is usually to make the object remember certain values between iterations. For instance, the Glass Dildo remembers whether it’s on or off. Remembered things are called properties, and you define them like this: 96 | 97 |
98 |

@property dildo:on 1 99 |
@property radio:lastuser #233 100 |
@property topiary:form "a giant fish" 101 |

102 | 103 |

Unlike ‘real’ programming languages, moo doesn’t require variables to be typed or declared; it just figures out what they are from what you put in them: in the example, respectively, a number, an object (object references are always preceded by # in moo), and a string (that’s programmerese for ‘text’). 104 | 105 |

Now, within a verb’s code, you refer to the object’s properties with this: e.g. 106 | 107 |

this.on = 1;
108 | this.lastuser = player;
109 | this.form = iobjstr; 110 |

111 | 112 |

From this you can figure out that player.name is a property—all players have a name—and player.location is another property, one that points to the room that currently contains the player. The Database Manual gives a full listing of all the properties that various canned objects (like players, rooms, containers, and exits) have. 113 | 114 | 115 |

Varying the description

116 | 117 | Very possibly your property is so important you want it to be part of the object’s description—you want it to be included when the player looks at your object. (Note that this is the only thing you can do with my Slow Glass object!) This is not hard to do, though it will seem cryptic. First, you @describe the object as usual—this should provide the invariant part of the description. Then you tell the moo that the object has a variable description: 118 | 119 |
120 |

@verb glass:description this none this 121 |

122 | 123 |

Now you edit the :description verb. An example might be a radio:description verb: 124 | 125 |

126 |

basic = pass(@args); 127 |
if (this.on == 1) 128 |
  basic = basic + " The radio is on." ; 129 |
else 130 |
  basic = basic + " The radio is off." ; 131 |
endif 132 |
return basic; 133 |

134 | 135 |

We are overriding the default action of the description command, normally done by the parent object (the generic thing, generic container, or whatever). The first line executes the default routine—pass passes the parameters of our call to the parent—in this case returning the default description to us, the one we set using @describe. 136 | 137 |

Now we add to this description. The + operator of course adds two strings together into a longer string. Note that we’re testing our property, this.on. Don’t forget the semicolons. 138 | 139 |

Finally, we return the completed string, basic, because description expects a string value to be returned (passed back for printing). 140 | 141 |

Note that description doesn’t use player:tell; this will be done for us somewhere else. 142 | 143 |

Verb parameters 144 | 145 |

If you’re used to programming, moo verbs are frustratingly limited in parameter handling. (Actually the limitation is on the parser rather than the verbs themselves.) Basically you can only write verbs on the following models: 146 | 147 |

148 |

Eat pill
149 | Turn radio on
150 | Give hernia to zompist
151 | Poke k-man with sharp stick 152 |

153 | 154 |

In effect all verbs have three parameters (called direct object, preposition, and indirect object). One of the objects must refer to the object, and the preposition is limited to a short list of predefined words. 155 | 156 |

When you define a verb, you do it backwards. You’re going to say "eat pill", but you define it as "pill:eat". 157 | 158 |

When you create the verb, you always have to specify all three parameters. An unused parameter is none; one that refers to the object itself is this; one that refers to something else is any. The above verbs would be defined this way: 159 | 160 |

161 |

@verb pill:eat this none none
162 | @verb radio:turn this on none
163 | @verb hernia:give this to any
164 | @verb stick:poke any with this 165 |

166 |

Each definition has one this parameter. You may be tempted to leave it out (@verb pill:eat none none none), but then the parser will never be able to call your verb! Think about the commands again, e.g. Eat pill. If it’s a command for the pill object, the word pill has to occur somewhere in the command-- otherwise, the parser simply does not have any reason to pass the command to the pill at all. 167 | 168 |

For the simplest commands, then, the parameter list should read this none none. 169 | 170 |

What if I defined it wrong? You can use @rmverb. I tend to forget the parameters at first, then have to user @rmverb to get rid of the uncallable verb, as in this session: 171 | 172 |

173 |

@verb pill:eat 174 |
"D’oh! 175 |
@rmverb pill:eat 176 |
@verb pill:eat this none none 177 |

178 | 179 |

Some verbs, like turn on or act up, sound better with an objectless preposition. (Linguistically it’s a particle, but moo calls it a preposition, so there.) You want to say Turn radio on, not Turn radio. You do this with the this (preposition) none parameter list. Note that the parser is not smart enough to let you say Turn on radio. 180 | 181 |

Often you want two objects—normally because you want your object to act on some other object. In this case you must define a preposition in between (from the list in the Programming Manual). You can’t have a command that reads Give zompist money; it has to be Give money to zompist. That’s why I had to define odd syntax like Play piano with "Greensleeves" or Drive bus to east. 182 | 183 |

In this case you can either specify the exact preposition in the verb (this on any), or allow any preposition (this any any). 184 | 185 |

You can put the other object (the any parameter) either as direct or indirect object. It’s just a matter of what command sounds better. Compare the following verbs for the Sharp Stick: 186 | 187 |

188 |

@verb stick:poke any with this -> "Poke zompist with stick."
189 | @verb stick:poke this at any -> "Poke stick at zompist." 190 |

191 | 192 |

When you edit a verb, you don’t have to repeat the parameters, which is a blessing. You can @define a verb as shed:get any off this (quick quiz! What command will you use with that?), but to edit the code all you need to say is @edit shed:get. 193 | 194 |

Can I have multiple variations of a verb? Sure—just in case you really really want to be able to say both "poke zompist with stick" and "poke stick at zompist". Just @define each variation, with its own parameter list. Now you’ll have to specify the parameter list when you edit. A warning, though: I don’t think it’s worth it. It’s no use trying to pretend to the user that she’s typing ordinary English; and you’re just likely to confuse or frustrate yourself with the extra parameters and verb versions. 195 | 196 |

Can I have more parameters? Sort of. What you do is parse the third argument (the indirect object, available inside the program as the string iobjstr) yourself. For instance, maybe you want to say listplayers game from 1 to 10. You have to define this as @verb game:listplayers this from any. You’ll get "1 to 10" as the iobjstr, which you can then parse yourself. There are functions to help you do this. It’s a bit of a hassle, though, and your life will be easier if you define separate verbs or something. 197 | 198 |

I want a string as the third parameter. No problem; the parser takes care of this for you. The piano uses this: I defined ‘play’ as @verb piano this with any. If the player types Play piano with the music of Burt Bacharach, the parser takes the entire rest of the sentence, "the music of Burt Bacharach", as the indirect object, and places it in iobjstr. Since I simply want to treat it as a string, I don’t need to do anything special. 199 | 200 | 201 |

Error handling

202 | 203 | It’s not terribly difficult to write a program that handles correct input. The mark of a professional program, however, is how it handles incorrect input. If you don’t think about this, your verb will sometimes crash embarrassingly, or produce silly results, or even corrupt your objects. 204 | 205 |

The parser will handle some errors for you: misspelling required words (such as the names of your objects or verbs), leaving parameters out; referring to objects that aren’t there. 206 | 207 |

Some error conditions to think about: 208 | 209 |

    210 |
  • The state you’re setting is already set. E.g. you turn the dildo on, and it’s already on.
  • 211 |
  • The property you’re checking is empty. E.g. the topiary doesn’t have a form right now.
  • 212 |
  • You wanted an object as a parameter but got garbage. You can test this with (say) iobj == #0 for this.
  • 213 |
  • You wanted a player and got an object. You can test this with is_player(iobj).
  • 214 |
  • The property you’re remembering no longer applies. For instance, the piano remembers who’s hiding under it. But if that person has left the room, we have to consider the property to be null.
  • 215 |
  • You want the object (e.g. the sharp stick) to act on another object; what if the object supplied is yourself, or the object itself?
  • 216 |
217 | 218 | 219 |

In general these conditions are handled by if statements. For instance, a radio:on verb might look like this: 220 | 221 |

222 |

if (this.on == 1) 223 |
  player:tell( "The radio is already on!" ); 224 |
else 225 |
  player:tell( "You turn on the radio." ); 226 |
  this.on = 1; 227 |
endif 228 |

229 |

As usual, you can’t deviate from the exact syntax the moo wants. The condition has to be in parentheses; there’s no semicolon after the if, else, or endif; the endif must exist, even for a one-line statement. 230 | 231 |

Very confusing if you’re not a C/C++ programmer: testing a value requires a double equals sign (this.on == 1); setting one requires a single equals (this.on = 1). You can look at a misbehaving verb for minutes on end without noticing an error like this. (Not-equals is !=, by the way.) 232 | 233 |

The else part is optional. You can also have any number of elseif (another condition) blocks before the else. 234 | 235 | 236 |

Some notes for C++ programmers

237 | 238 | You’ll see a strong C influence on the moo language. Compound statements, such as if/endif, however, look more like Basic. 239 | 240 |

The language is object-oriented, with inheritance. Note that the @create command specifies the base class of the newly created object. Verbs correspond to methods (but you use object:method, not object::method), and properties to data members (using the same . as in C/C++). 241 | 242 |

Can you define a verb that’s not called by a player, but only by other verbs? Sure. It uses a special parameter list: this none this, which the player cannot enter (if there’s no preposition the parser just quits). The dartboard’s hit command works like this. It’s defined by @verb dartboard:hit this none this and called by the dart:toss routine with the line 243 | 244 |

#278:hit(); 245 |

246 | 247 | #278 is the object number of the dartboard (object numbers are a special data type; think of them as pointers to objects). You can use this anywhere, to make sure the moo understands you whether or not the dartboard is around. It’s better programming practice, of course, to avoid using the same number all over—it means you couldn’t use the object in another moo, for instance. If we needed to refer to it often, it’d be better to put the dartboard number in a property of the darts, for instance. 248 | 249 |

There’s fairly nice list (array) support, and lists can include any other type, including other lists, as members. 250 | 251 |

One little gotcha: the C construction cond ? texpr : fexpr morphs in moo into cond ? texpr | fexpr. 252 | 253 |

Most things the player can do on the command line can also be done by the programmer within a verb-- sometimes with a slightly different verb or syntax. For a full exploration of this, see the Database Manual. 254 | 255 |

Don't worry about prettyprinting your program. Moo stores it in an interpreted form, and supplies its own indents and spacing when you next edit it. 256 | 257 | 258 |

A few examples

259 | 260 | You may be interested in how I got some of the effects in the existing SpinnMoo toys. You can inspect the program code with @edit to see exactly how something was done. 261 | 262 |

Dildo. The dildo remembers its state in an on property. It also remembers the last user; I defined a lastuser property pointing to an object, and set it to player when appropriate. The delayed action is accomplished using a fork command; see the Programming Manual. 263 | 264 |

Piano. The piano isn’t really a container; it just remembers who is hiding under it (using a hider property). The description is modified to make this seem more real. Note the error handling in the hide command: there can only be one hider at a time, so the previous hider (if it’s not the player!) must be ejected. We cannot prevent the hider from leaving the room, so the description and other commands must check to see that the hider is really still there. We do this by checking if hider.location == this.location-- if the hider is in the same room as the piano. The play command uses random to determine the player’s style and the onlookers’ reaction. 265 | 266 |

Slow glass. This is mostly implemented in a huge description command. There are three properties that increment (at different speeds, so they don’t overlap) on each call: location, weather, and time of day. There’s also a direction, 1 or –1, which determines whether location is incremented or decremented. 267 | 268 |

Readme. The readme object simply contains couple of list properties, items and signed. The first contains the items added to the bulletin board; the second contains the players’ names. 269 | 270 |

Dartboard. This was the most complicated programming task. The dartboard remembers the current scores (as pair of lists, the players and their scores) and contains verbs to reset the scores, set the target, print out the scores, or retrieve the darts, which may have wandered to another room. There is also a hidden verb, hit, which is called by the darts when you toss them. This verb makes heavy use of the random function to vary the results. It throws three darts, totals them up, adjusts the scores, determines winners, and on the second and higher rounds passes the darts to the next player. 271 | 272 |

VW Microbus. This required wizard status to fully implement; but the actual coding is straightforward. The bus is actually a separate room which stays in one place; the "bus" object you can pick up and carry around (and which moves around the moo as the bus is driven) is not even a container, and its enter verb just teleports the player to the separate room. The room has a single exit, whose destination point is reset as the player drives. The bus room itself is, of course, not inside another room at all; we just remember where it is as a property. The ways() function tells us what exits a room has; the Database Manual can be ransacked for tidbits like this. The difficulty here was not so much writing the code, as in being careful about what needed to be done. For instance, as the bus moves, the bus object not only has to be moved, but messages have to be printed in the previous and new locations so users see the bus coming through. 273 | 274 |


275 | 276 |

Home [ Page o' SpinnStuff ] 277 | 278 | 279 | 280 | 281 | -------------------------------------------------------------------------------- /tutorials/src/winding-duck-non-html5.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | Lambda MOO Programmer's Tutorial 7 | 8 | 9 | 10 |

11 | Lambda MOO Programmer's Tutorial 12 |

13 |

14 | This tutorial was taken from http://netlab.gmu.edu/muve/html/Winding-Duck.html 16 | and is included in this repository for posterity. It is 17 | non-HTML5 for now. 18 |

19 |
    20 |
  • 21 | Chapter 1: A Simple 22 | Object
    23 |
    37 |
  • 38 |
  • 39 | Chapter 2: A More Complex 40 | Object
    41 |
    52 |
  • 53 |
  • 54 | Chapter 3: Other Programming 55 | Issues
    56 |
      57 |
    • 58 | Description 59 |
    • 60 |
    • 61 | Introduction 66 | This is a programming example of reasonable 67 | complexity. We are going to demonstrate property use, 68 | forking, and generic object use. 69 |

      70 | Our example is a wind-up toy. The basic things that a 71 | wind-up toy does is get wound, and then slowly wind 72 | its way down, hopping or rolling or twirling along. 73 |

      74 |

      75 | First, let's create a couple of objects to work from. 76 |

      77 |
       78 |   @create $thing named Generic Wind-Up Toy,Toy
       79 |   You now have Generic Wind-Up Toy (aka Toy) with object number #12221
       80 |   and parent generic thing (#5).
       81 | 
       82 |   @create #12221 named Wind-Up Duck,Duck
       83 |   You now have Wind-Up Duck (aka Duck) with object number #12222 and
       84 |   parent Generic Wind-Up Toy (#12221).
       85 | 
      We'll refer to these as Toy and Duck, the aliases we gave in 86 | the @create command. 87 |

      88 | Simple Wind 89 |

      First, we need some way to tell if the toy has 90 | been wound up. We'll create a property called "wound" 91 | on the generic toy. 92 |
       93 |   @property toy.wound 0
       94 |   Property added with value 0.
       95 | 
      Before we can write our program, we need a verb... We want to 96 | give commands like "wind duck" so we give it an argument list of 97 | "this". The variable "this", when used inside a verb, refers to the 98 | actual object (e.g. the duck) on which the verb called. Its place 99 | in the argument list designates how the verb will be found by the 100 | built-in parser. 101 |
      102 |   @verb toy:wind this
      103 |   Verb added.
      104 | 
      Now we can make a simple program to wind the toy. 105 |
      106 |   @program toy:wind
      107 |   this.wound = this.wound + 2;
      108 |   player:tell("You wind up the ", this.name,".");
      109 |   player.location:announce(player.name, " winds up the ", this.name,".");
      110 |   .
      111 | 
      Remember that "this" takes the place of whatever we will type 112 | in place of "this" in our command line, so when the program runs, 113 | it will change the .wound property of the duck. "Player" is the 114 | person who types the command. These are built in variables 115 | available in every program, they just take on different meanings 116 | depending on who types the command and what object is used in the 117 | command. The idea behind adding 2 to this.wound is that you can 118 | wind it a bunch of times to make it go longer. 119 |

      120 | Let's try it on our duck: 121 |

      122 |
      123 |   wind duck
      124 |   You wind up the Wind-Up Duck.
      125 | 
      Everyone else sees: 126 |
      127 |   yduJ winds up the Wind-Up Duck.
      128 | 
      129 |

      130 | Simple Messages 131 |

      Next, we want to make the duck actually move 132 | around. Remember that we're doing all the programming 133 | on the generic toy, and using the specific instance of 134 | the duck to test things out. To be properly generic, we 135 | need to define different properties that can hold 136 | message strings for the motions of the toy. So let's 137 | define two messages. One for the toy to start moving, 138 | and another for it to print as it continues to wind 139 | down. 140 |
      141 |   @property toy.startup_msg ""
      142 |   Property added with value "".
      143 | 
      144 |   @property toy.continue_msg ""
      145 |   Property added with value "".
      146 | 
      We named these with "_msg" in order for the messages to show 147 | up in @messages, and be settable with the @message_name object is 148 | "text" command. (See help @messages for more details.) Let's set 149 | values of these messages on the duck. While we're at it, we should 150 | give our duck a description! 151 |
      152 |   @startup duck is "waddles about and starts rolling forward."
      153 |   You set the "startup" message of Wind-Up Duck (#12222).
      154 |   @continue duck is "swivels its neck and emits a >> Quack <<"
      155 |   You set the "continue" message of Wind-Up Duck (#12222).
      156 |   @describe duck as "A yellow plastic duck with wheels at the bottom and a knob
      157 |   for winding."
      158 |   Description set.
      159 | 
      160 |

      161 | Simple Drop 162 |

      We should be ready to roll now... We're going to 163 | have the wind up toy start up when the player drops it. 164 | This introduces another concept, that of specializing 165 | an existing verb. The generic thing ($thing) defines 166 | the verb :drop. Normally, if we type "drop duck" the 167 | verb on $thing gets invoked on the duck (even though 168 | it's a few levels removed from $thing). If we define a 169 | :drop verb on the generic toy, we don't want to have to 170 | type in all the code from $thing:drop; we just want to 171 | have our new code executed after the normal drop gets 172 | done. In order to get both things, we use the primitive 173 | pass(@args). 174 |

      175 | This is the slipperiest concept in MOO programming, 176 | yet the basis for the entire object oriented nature 177 | of the MOO. An easy way to think of pass is like 178 | another verb call: it just does the "basic thing" 179 | that this verb normally does. We can add our 180 | specialization code both before and after this basic 181 | thing. The @args part means just to use the same verb 182 | arguments in the base case as we do in our 183 | specialization. More sophisticated users of pass may 184 | wish to change these arguments; for now, just take it 185 | as gospel. Sometimes you won't want to use pass at 186 | all, if you don't want the basic thing to happen, but 187 | want to completely override the default behavior. 188 | This is OK, you just have to think about what you 189 | want when deciding whether and where to put the call 190 | to pass(@args). 191 |

      192 |

      193 | We want the dropping to happen before any of our 194 | startup, so we call pass(@args) right away. We better 195 | check if the toy has been wound up, and not do 196 | anything special if it hasn't. Next, we print out our 197 | startup message, and schedule some tasks to be run a 198 | little later to print out the progress of our toy. 199 | Let's pick every 15 seconds to print out a message, 200 | and print out as many messages as we have in 201 | this.wound. Each time it actually prints a message, 202 | it will "unwind" a little, so we decrement 203 | this.wound. Note that the stuff between the fork and 204 | the endfork doesn't get done until the fork actually 205 | starts, and the forks start 15, 30, 45, etc. seconds 206 | after all the code in our drop verb has finished. 207 |

      208 |

      209 | Our @verb command looks a little funny, because we're 210 | forced into using some more advanced features of MOO 211 | verbs. If you have a verb named foo*bar, then in 212 | order to invoke that verb, the user must type at 213 | least "foo", and may type any part of "bar" that they 214 | like; the * is putting in an abbreviation. 215 | $thing:drop has these abbreviations built in, so you 216 | can just type "d object" to drop something. Also, 217 | drop has a synonym, "throw" (more properly, th*row). 218 | Putting both verbs in a double-quoted string is the 219 | way to say "two names for this verb". Really, any 220 | number of synonyms are possible. We'll continue to 221 | refer to this verb as "drop", even though we could 222 | just as truthfully refer to it as "throw". We use 223 | these names because we want to name our verb exactly 224 | the same as the one on $thing, so people who prefer 225 | to use "throw" will still get our specializations. 226 |

      227 |
      228 |   @verb toy:"d*rop th*row" this
      229 |   Verb added.
      230 | 
      231 |   @program toy:drop
      232 |   pass(@args);
      233 |   if (this.wound)
      234 |     this.location:announce_all(this.name, " ", this.startup_msg);
      235 |     for x in [1..this.wound]
      236 |       fork (x * 15)
      237 |         this.location:announce_all(this.name," ", this.continue_msg);
      238 |         this.wound = this.wound - 1;
      239 |       endfork
      240 |     endfor
      241 |   endif
      242 |   .
      243 | 
      It's worth noting that 0 in MOO is also "false" as well as 244 | being "zero", which is convenient. Thus we didn't have to use 245 | (this.wound!=0) in our if statement, but could use just 246 | (this.wound). 247 |

      248 | Now, let's drop our duck and see how it works! 249 |

      250 |
      251 |   drop duck
      252 |   You drop Wind-Up Duck.
      253 |   Wind-Up Duck waddles about and starts rolling forward.
      254 |   Wind-Up Duck swivels its neck and emits a >> Quack <<
      255 |   Wind-Up Duck swivels its neck and emits a >> Quack <<
      256 | 
      It actually waited 15 seconds in between each of those 257 | messages. I tried it again, but this time I typed @forked right 258 | away to see that the tasks had been scheduled: 259 |
      260 |   @forked
      261 |   Queue ID Start Time Owner Verb (Line) [This]
      262 |   -------- ---------- ----- ------------------
      263 |   1231283976 Jul 23 15:03:28 1991 yduJ #12221:drop (6) [#12222]
      264 |   577459244 Jul 23 15:03:43 1991 yduJ #12221:drop (6) [#12222]
      265 | 
      266 |
      267 |

      268 | Chapter 2: A More Complex 269 | Object 270 |

      How could we improve on our wind-up toy? Or, 271 | rather, what are some of its problems? 272 |

      273 | What happens when someone picks up the toy while it's 274 | going? What if you try to drop it somewhere you're 275 | not allowed to? Perhaps we should only allow someone 276 | to wind it up if they are holding it. What if someone 277 | winds it five thousand times? Perhaps we should allow 278 | programmers to make more complicated messages on 279 | their child objects. 280 |

      281 |

      282 | We'll address each of these issues in chapter 2. 283 |

      284 |

      285 | Complex Wind 286 |

      Let's start with requiring someone to be holding 287 | the duck in order to wind it. We take the code from 288 | before, and stick an if/else/endif around it. 289 |
      290 |   @program toy:wind
      291 |   if (this.location == player)
      292 |     this.wound = this.wound + 2;
      293 |     player:tell("You wind up the ", this.name,".");
      294 |     player.location:announce(player.name, " winds up the ", this.name,".");
      295 |   else
      296 |     player:tell("You have to be holding the ", this.name,".");
      297 |   endif
      298 |   .
      299 | 
      That was simple enough... again 'this' is whatever wind-up 300 | toy was wound, and 'this.location' is where the toy is now. If it 301 | is in the player's inventory then this.location will be equal to 302 | the variable 'player'. 303 |

      304 | Let's complicate the code once more, and then we'll 305 | be done with the :wind verb. We should have a maximum 306 | number of turns the toy will allow, otherwise a 307 | malicious player could spend a few hours winding the 308 | toy, and then unleash it on an unsuspecting public. 309 | If we wanted to be really fancy, we could make the 310 | toy break if they wound it too far, and require then 311 | to repair it with a screwdriver (which we would 312 | create for the purpose), but let's leave that as an 313 | exercise for the reader. 314 |

      315 |

      316 | We create a property for the maximum number of turns, 317 | so different toys can have different maximums. We'll 318 | give it a default value of 20, though, so it doesn't 319 | have to be set for each new toy. 320 |

      321 |
      322 |   @property toy.maximum 20
      323 |   Property added with value 20.
      324 | 
      Now we insert another set of if/else/endifs inside our 325 | current set. 326 |
      327 |   @program toy:wind
      328 |   if (this.location == player)
      329 |     if (this.wound < this.maximum)
      330 |       this.wound = this.wound + 2;
      331 |       player:tell("You wind up the ", this.name,".");
      332 |       player.location:announce(player.name, " winds up the ", this.name,".");
      333 |       if (this.wound >= this.maximum)
      334 |         player:tell("The knob comes to a stop while winding.");
      335 |       endif
      336 |     else
      337 |       player:tell("The ",this.name," is already fully wound.");
      338 |     endif
      339 |   else
      340 |     player:tell("You have to be holding the ", this.name,".");
      341 |   endif
      342 |   .
      343 | 
      In order to add to the feel of the toy, we put in another 344 | test: if, after adding 2 to the wound property, it has reached the 345 | maximum, we tell the player they've come to the end. The more 346 | completely you can describe an object's actions and responses to 347 | actions, the richer the feel of the Virtual Reality. 348 |

      349 | Complex Messages 350 |

      Earlier we showed how to make user settable 351 | messages on an object. Now we will show how to enable 352 | another programmer make more complicated messages on 353 | eir windup toy. 354 |

      355 | A simple yet very useful method is to define a verb 356 | for each message property, which just returns that 357 | property, and then have all the uses of that property 358 | use the verb instead. For example: 359 |

      360 |
      361 |   @verb toy:continue_msg this none this
      362 |   @program toy:continue_msg
      363 |   return this.continue_msg;
      364 |   .
      365 | 
      However, it seems almost silly to make a whole bunch of 366 | verbs, one for each property... And indeed it is. The built-in 367 | variable "verb" is always set to the name that this verb was 368 | invoked with, so we can get all the messages in one verb, with this 369 | idiom: 370 |
      371 |   @verb toy:"wind_down_msg continue_msg startup_msg going_msg" this none this
      372 |   @program toy:continue_msg
      373 |   return this.(verb);
      374 |   .
      375 | 
      There's a lot of stuff to explain in this little bit of code! 376 | First, the syntax of the @verb command, with the several messages 377 | listed in a quoted string, indicates that each of those names is an 378 | alias for the verbname, and the verb can be invoked by any of those 379 | names. Second, the builtin variable "verb" is set to whichever name 380 | was actually used. This enables the property reference to be a 381 | simple construction from the verb name, using the expression syntax 382 | for property references, 383 | .(), where in this case expression is a simple 384 | variable reference. 385 |

      386 | Here's an example of how we can customize just one 387 | of those messages programmatically with the wind-up 388 | duck. In the continue_msg we'll make the duck quack 389 | once, twice, or thrice, randomly. We leave the 390 | other messages alone, as we'll just be using static 391 | text. 392 |

      393 |
      394 |   @verb duck:continue_msg this none this
      395 |   @program duck:continue_msg
      396 |   times = {"once","twice","thrice"}[random(3)];
      397 |   return "swivels its neck and quacks " + times + ".";
      398 |   .
      399 | 
      400 |

      401 | Complex Drop 402 |

      Let's tackle the drop verb now. There are a lot 403 | of problems. First, what happens if someone picks up 404 | the toy while it's still going? The messages still 405 | appear! What should happen? Two ideas come to mind: 406 | 1. It should fully discharge its windings, as real 407 | life windup toys are wont to do; 2. It should just 408 | stop running, as though the player had grabbed the 409 | windup knob to prevent it from further discharge. 410 | We'll choose option 2 for our example. Option 1 would 411 | be easiest to do by specializing the existing "take" 412 | verb to reduce the .wound property to 0, printing 413 | appropriate messages. 414 |

      415 | The major problem with our drop verb is that it 416 | schedules all its tasks at one time. Once 417 | scheduled, they *will* run, whether or not the 418 | situation has changed. To solve this problem, we 419 | only schedule one task at a time, making it the job 420 | of that task to figure out whether it has wound 421 | down, and schedule another task. It should also 422 | check if it has been picked up; if so it should 423 | neither print messages nor schedule the next task. 424 |

      425 |

      426 | Having called this section "Complex Drop", we're 427 | going to defer the complexity to an auxiliary verb, 428 | and make Drop itself simpler. 429 |

      430 |
      431 |   @program toy:drop
      432 |   pass(@args);
      433 |   if (this.wound)
      434 |     this.location:announce_all(this.name, " ", this:startup_msg());
      435 |     fork (15)
      436 |       this:do_the_work();
      437 |     endfork
      438 |   endif
      439 |   .
      440 | 
      We will use the special arguments "this none this" in our 441 | @verb command. This is a special kluge that means this verb is not 442 | a command, and cannot be typed at the command line. It also won't 443 | show up in @examine commands. This helps to keep down the clutter 444 | of @examine, and because this verb is only an internal function, it 445 | wouldn't make sense to type as a command line. 446 |
      447 |   @verb toy:do_the_work this none this
      448 |   if (this.wound)
      449 |     if ($object_utils:isa(this.location,$room))
      450 |       this.location:announce_all(this.name," ", this:continue_msg());
      451 |       this.wound = this.wound - 1;
      452 |       if (this.wound)
      453 |         fork (15)
      454 |           this:do_the_work();
      455 |         endfork
      456 |       else
      457 |         this.location:announce_all(this.name, " ", this:wind_down_msg());
      458 |       endif
      459 |     endif
      460 |     if (this.wound < 0)
      461 |       this.wound = 0;
      462 |     endif
      463 |   endif
      464 | 
      Let's go through this step by step. First we check if we're 465 | still wound up. Theoretically this should always be true, but it's 466 | best to check. Assuming .would is true (that is, nonzero), we check 467 | to see if we're in a room. The utility function 468 | $object_utils:isa(x,y) checks to see if an object X will behave as 469 | an object Y, that is, if Y is in the object's ancestor tree. We do 470 | this because we only want to make our noises if we're wandering 471 | around in a room. A player won't be a room, and so won't pass this 472 | test. Now, we print our message, and decrement this.wound to say 473 | we've wound down once. Next we think about scheduling the next 474 | task. We check this.wound again, because if we subtracted the last 475 | bit, we don't want to bother scheduling it. Note that we call this 476 | very same verb, this:do_the_work. Just for extra feeling, we put in 477 | a new message to be printed when the toy has actually wound down. 478 | Oops! Better add that property... And finally, at the very end, 479 | we've gotten all anal retentive with error checking and made sure 480 | that this.wound never gets into negative numbers. (Notice that if 481 | it *did* get into negative numbers, it would never stop. So this 482 | check is not entirely pointless!) 483 |
      484 |   @property toy.wind_down_msg ""
      485 |   Property added with value "".
      486 | 
      487 |   @wind_down duck is "hiccups once and stops rolling."
      488 |   You set the "wind_down" message of Wind-Up Duck (#12222).
      489 | 
      490 |
      491 |

      492 | Chapter 3: Other Programming 493 | Issues 494 |

      495 |

      496 | Description 497 |

      It might be nice if you could tell by looking at 498 | the toy that it was moving, and not have to wait for 499 | it to tell you so. Like "drop", this is done by 500 | specializing an already existing verb, called 501 | description, by calling pass(@args) and then doing 502 | some additional stuff. There are a number of relevant 503 | differences between this and most other verbs, 504 | however. 505 |

      506 | Most verbs do something, that is, when you type a 507 | command, it produces a set of messages, and changes 508 | some properties on some objects. Some verbs, 509 | however, are not commands, and don't even print any 510 | messages. Instead, they return a value to the 511 | calling verb, for that verb to use to produce 512 | messages or change properties. These are analogous 513 | to "functions" in other languages. Description is 514 | one such verb. By default, that is, in the basic 515 | case, it returns the property 'this.description'. 516 | Verbs like 'look' call description on objects and 517 | print out the strings they return. So when we 518 | specialize description, we won't be calling 519 | pass(@args) all by itself, and then printing out 520 | more text, instead we'll call pass(@args), add text 521 | to that, and return the whole pile. 522 |

      523 |

      524 | Again because we're trying to write generically, we 525 | create a property to hold a message to be added to 526 | the description when the toy is running, and we set 527 | it for the duck. 528 |

      529 |
      530 |   @property toy.going_msg ""
      531 |   Property added with value "".
      532 | 
      533 |   @going duck is "The duck is rolling forward with a slight waddle."
      534 |   You set the "going" message of Wind-Up Duck (#12222).
      535 | 
      The description verb again needs the "this none this" args, 536 | since it is just an internal verb called by programs. 537 |
      538 |   @verb toy:description this none this
      539 |   Verb added.
      540 | 
      541 |   @program toy:description;
      542 |   basic = pass(@args);
      543 |   if (this.wound)
      544 |     return basic + " " + this:going_msg();
      545 |   else
      546 |     return basic;
      547 |   endif
      548 |   .
      549 | 
      Pretty simple code, huh? First we call pass. We store away 550 | that text in a variable 'basic', because we'll need it later. Then 551 | we check to see if we're wound up. If we are, we return the basic 552 | information plus our going_msg. We put in a couple of spaces to 553 | separate the sentences. Otherwise, we're quiet, and we just return 554 | the basic string. What does it look like? 555 |
      556 |   look duck
      557 |   A yellow plastic duck with wheels at the bottom and a knob for winding.  The
      558 |   duck is rolling forward with a slight waddle.
      559 | 
      560 |

      561 | Permissions 562 |

      To allow other people to create their own 563 | wind-up toys, we set the generic toy world-readable 564 | and `fertile': 565 |
      566 |   @chmod toy rf
      567 |   Object permissions set to rf.
      568 | 
      This isn't really good enough, due to the screwinesses of the 569 | MOO permission system. Generally speaking, when you create an 570 | object, you become the owner of all its properties as well. This is 571 | called "having c permissions". But that means that someone else 572 | (including the owner of the *verb* on the object) can't change the 573 | property. To get around this problem, we set the wound property !c 574 | (meaning "not c"). Then the wound property always belongs to the 575 | owner of the generic toy, and not to the creator of any specific 576 | instance. 577 |
      578 |   @chmod toy.wound !c
      579 |   Property permissions set to r.
      580 | 
      It tells us what the permissions ended up being, after 581 | stripping off the "c", which is "r" for readable. 582 |
    • 583 |
    584 |
  • 585 |
586 | 587 | 588 | --------------------------------------------------------------------------------