├── .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 |
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 |
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 |
Server calls the managing objects "do_login_command" with the
23 | input string
24 | The input string fails to match any known verbs
25 | do_login_command defaults to a verb named "?" to handle the input
26 | "?" displays the contents of $login.welcome_message, along with a command
27 | summary
28 |
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 |
parse input into basic tokens delimited by spaces
40 | Check for input and output prefixes
41 | If input or output prefixes aren't detected, try executing
42 | "do_command" on the object associated with that connection from before
43 | If "do_command" doesn't exist, or returns a false value, perform the
44 | normal verb matching proceedure and execute the located verb as an
45 | input task.
46 | If all else fails, call "huh", or as a last resort report
47 | "I didn't understand that." to the connection
48 |
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 |
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 | The data types for LambdaMOO are
33 |
34 | Integers Signed whole numbers.
35 |
36 |
37 | Object Objects in the LambdaMOO database are referred to
38 | by their object number. #ObjectNumber denotes an object number
39 | (for example #0 is object number 0). The object number is
40 | essentially a pointer to an object, since there is no such
41 | notion as passing an object by value for the LambdaMOO system.
42 | toobj(...) will convert either strings like "#42" or 42 to objects,
43 | and both result in #42.
44 |
45 |
46 | Strings Characters in LambdaMOO are strings of length 1.
47 | Like lists, LambdaMOO strings are immutable.
48 |
49 |
50 | Error Various run time errors, such as security violations,
51 | type mismatches, unassigned variables, etc. Consult the
52 | programmer's manual for more information.
53 |
54 |
55 | Lists In LambdaMOO, lists are a the fundamental
56 | data type for constructing things like arrays and sets.
57 | The elements in a list can be assigned to destructively,
58 | but in order to change the length of a list it is necessary to
59 | do something like
60 | MyList={@MyList,NewElement}
61 | Elements of a list can be of any type allowed for variables in
62 | LambdaMOO, including lists themselves.
63 | See the section on Lists for more details.
64 |
65 |
66 | Floating point numbers Signed real numbers.
67 | It is important to note that in LambdaMOO,
68 | 33 is not equal to 33.0.
69 |
70 |
71 |
72 | Data Type typeof(...)== Conversion Function> Comments
73 | Integers INT or 0 tonum(...) or toint(...) Signed whole numbers
74 | Object OBJ or 1 toobj(...) Reference number for an object
75 | Strings STR or 2 tostr(...) String or character
76 | Error ERR or 3 none Error condition. Used for various runtime errors
77 | Lists LIST or 4 none Immutable list. Used for arrays and sets
78 | Floating point number FLOAT or 9 tofloat(...) Signed floating point number
79 |
80 | Creating Objects
81 | The three major ways of creating objects in LambdaMOO are
82 |
83 | @create ParentObject named "Object Names"
84 |
85 | This first method is used by human beings to create objects,
86 | and is something which would be entered on the command line.
87 | For example:
88 | @create $thing named "my very own object"
89 | will create an object derived from $thing (Generic Thing)
90 | and set the new object's name to my very own thing
91 |
92 |
93 |
94 | create(ParentObject [,OwnerObject])
95 | This method is intended for programmatically creating new
96 | objects by invoking the interpreter's built in create
97 | function. Both ParentObject and OwnerObject are required to
98 | by object references .
99 | If OwnerObject is not specified, then the object will own itself
100 | (see object ownership
101 | in the security section ). The return type of create will
102 | be the object reference of the newly created object, or else
103 | the appropriate error code.
104 | For example:
105 | create($npc,player)
106 |
will create an object derived from $npc and owned by the
107 | player object which caused this verb to be executed.
108 |
109 |
110 | $recycler:_create(Parent, [,OwnerObject)
111 | This method is also intended for programmatically creating new objects
112 | but in this case it differs from the built in function in a subtle
113 | manner. When create() is used, the object reference will always be
114 | unique. $recycler:_create attempts to objects discarded with
115 | $recycler:_recycle(NoLongerNeededObject) by reconstructing a previously
116 | used object into its new role. In a perfect world this verb is
117 | indistinguishable from the create function, except that its friendlier to
118 | longer lived systems which create many temporary objects.
119 |
120 |
121 |
122 | Creating properties on an object
123 | The major ways to add properties to an object in LambdaMOO are
124 |
125 | @property ObjectName.PropertyName [PropertyValue]
126 | This first method is intended for use by human beings when constructing
127 | objects before they are put into use. If PropertyValue is omitted,
128 | the property is initialized to 0.
129 |
130 |
131 | add_property(TargetObject,PropertyName,InitialValue,Info)
132 | This method invokes the built in add_property function to add
133 | the property PropertyName to the object TargetObject. For more
134 | information, please consult the online help using
135 | @help add_property
136 |
137 |
138 | Use tkMOOlite or MacMOOSE
139 | Andrew Wilson's tkMOOlite or the MacMOOSE programs each have
140 | nice interactive object browser's which allow a programmer to
141 | interactively add properties and verbs to an object.
142 |
143 |
144 |
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 | The major ways of adding verbs to an object in LambdaMOO are
163 |
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 | @examine ing 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 |
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 |
[ 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 | [ 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 |
63 | Chapter 1: A Simple Object
64 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 |
495 | 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 | 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 |
--------------------------------------------------------------------------------