Core Utility Help Topics

$biglist $guest_log mail-resolution MR-writing
$container $help mail-resolve $no_one
core-index $housekeeper mail-system object-matching
$error $login MR-access $player_db
$exit $mail_agent MR-naming receiving-mail
$generic_db $mail_browser MR-reading $recycler
$generic_editor mail-format MR-searching $room
$generic_help mail-forwarding MR-sequences sending-mail
$generic_options $mail_recipient MR-subscribing


===== help $biglist ===============================

Generic BigList Utilities

$biglist is a collection of routines for maintaining huge persistent (sorted) lists in a format that is less likely to spam the server (which runs into a certain amount of trouble dealing with long ordinary lists --- btw we use `biglist' to refer to the huge data structure we're about to describe and `list' to refer to ordinary MOO lists {...}). The biglist in question lives on a particular object, to which we will refer in the discussion below as the `home' object, and its various elements appear as leaves of a tree whose nodes are kept in properties of the home object. It should be noted that the home object does not need to be (and in fact should *not* be) a descendant of $biglist one; $biglist merely provides utilities for manipulating the properties on the home object that are used in a particular biglist manipulation.

All of the utilities below refer to `caller' to locate the home object. Thus verbs to manipulate a given biglist must be located on or inherited by its home object itself. The home object needs to define the following verbs

:_make(@args) => new property on home object with value args
:_kill(prop) delete a given property that was created by :_make
:_get(prop) => home.prop
:_put(prop,@args) set home.prop = args
:_ord(element) given something that is of the form of a biglist element return the corresponding ordinal (for sorting purposes)

If you never intend to use :find_ord, then this can be a routine that always returns 0 or some other random value.

See $generic_biglist_home or $big_mail_recipient for examples.

Those of the following routines that take a biglist argument are expecting either {} (empty biglist) or some biglist returned by one of the other routines

:length(biglist) => length(biglist) (i.e., number of elements)
:find_nth(biglist,n) => biglist[n]
:find_ord(biglist,k,comp) => n where n is the largest such that home:(comp)(k,home:_ord(biglist[n])) is false, or the smallest such that home:(comp)(k,home:_ord(biglist[n+1])) is true.

Always returns a value between 0 and length(biglist) inclusive.
This assumes biglist to be sorted in order of increasing :_ord values with respect to home:(comp)().
Standard situation is :_ord returns a number and comp is a < verb.

  :start(biglist,s,e)  => {biglist[s..?],@handle} or {}
  :next(@handle)       => {biglist[?+1..??],@newhandle} or {}
These two are used for iterating over a range of elements of a biglist The canonical incantation for doing
        for elt in (biglist[first..last])
        handle = :start(biglist,first,last);
          for elt in (handle[1])
          handle = :next(@listdelete(handle,1));
The following all destructively modify their biglist argument(s) L (and M).

:set_nth(L,n,value) => L[n] = value
replaces the indicated element
:insert_before(L,M,n) => {@L[1..n-1],@M,@L[n..length(L)]}
:insert_after (L,M,n) => {@L[1..n], @M,@L[n+1..length(L)]}
takes two distinct biglists, inserts one into the other at the given point returns the resulting consolidated biglist
:extract_range(L,m,n) => {{@L[1..m-1],@L[n+1..]}, L[m..n]}
breaks the given biglist into two distinct biglists.
:delete_range(L,m,n[,leafkiller]) => {@L[1..m-1],@L[n+1..]}
:keep_range (L,m,n[,leafkiller]) => L[m..n]
like extract_range only we destroy what we don't want.
:insertlast(L,value) => {@L,value}
inserts a new element at the end of biglist.
If find_ord is to continue to work properly, it is assumed that the home:_ord(elt) is greater (comp-wise) than all of the :_ord values of elements currently in the biglist.
:kill(L[,leafkiller]) destroys all nodes used by biglist.
Calls home:leafkiller on each element.

===== help $big_mail_recipient =============================

Generic Large-Capacity Mail Recipient (#15) is owned by Hacker (#37).
Aliases: Generic Large-Capacity Mail Recipient
Generic Large Capacity Mail Recipient
Since any modifications to large lists entail copying the entire list over, operations on ordinary mail recipients having large numbers of messages, that actually change the content of .messages will take inordinately long. Thus we have this version which makes use of the $biglist package, scattering the messages onto numerous properties so that write operations involving only a few messages will not require recopying of the entire list. In nearly all respects it behaves as the ordinary Mail Recipient, except that it is faster for certain kinds of operations. Certain unimplemented verbs, like :date_sort(), and :messages() currently return E_VERBNF. To convert an existing $mail_recipient-child (call it #MR) into a $big_mail_recipient-child the basic procedure is ;; #MR:messages(); @rmm 1-$ from #MR @unrmm expunge @chparent #MR to $big_mail_recipient ;#MR:receive_batch(; Obvious Verbs: @mail-n*ame $big_mail_recipient is @rm-mail-n*ame/@remove-mail-n*ame from $big_mail_recipient @add-mail-n*ame to $big_mail_recipient


===== help $container =============================

The Generic Container (for programmers)

In addition to the command verbs described under `help containers' and the _msg properties described in `help container-messages', the following verbs and properties are available for use within programs

.opened == TRUE iff the container is open
.dark   == TRUE iff the contents of the container may be seen
.opaque -- describes the correlation between .open and .dark
   == 0  container is always !dark
   == 1  container is dark iff it is closed
   == 2  container is always dark
changes the .opaque value for the container => newvalue or E_PERM or E_INVARG

opens/closes the container (updates .open and .dark) according to newvalue => newvalue or E_PERM

what the :open command uses to test whether the player should be able to open the container. By default this refers to .open_key (set by @(un)lock_for_open), but the object owner is free to customize this.

N.B.: There is no way to directly set .dark; .dark can be changed only by changing one of .opaque or .opened. Use :set_opaque(0) and :set_opaque(2) to have .dark change independently of the value of .opened.

===== help core-index =============================

Retorna a lista de comandos do Core Utility Help Topics (#22) - mostrada no início deste documento.


===== help $error ================================

The Error Generator, $error, may be used to automatically generate errors. This is particularly useful if you are working in a !d verb but have occasion to -want- to crash with traceback. To raise a specific error, use $error:raise(error type) -- for example, $error:raise(E_PERM) will produce traceback resulting from a Permission Denied error.

Random notes about $error:

+ The complete list of errors is stored in $error.names.
+ The seemingly useless :accept() verb on $error is so that error:E_RECMOVE and $error:E_NACC will be guaranteed success (success meaning, of course, a termination by traceback).  + There is, unfortunately, no way to raise the error E_NONE.

===== help $exit =================================


An exit can be renamed by either the owner of the exit or the owner of its source.

The standard verbs that are called in exit movement are:

:move(object)  - moves the object via this exit
:invoke()      - equivalent to :move(player)
When an exit is invoked on a particular object (via exit:move(object)), the following occurs.

(1) The exit may be locked against the object, in which case we print the nogo messages and quit.

(2) (room=exit.dest):bless_for_entry(object) is called. Assuming that exit is recognized by room as being a legitimate entrance (i.e., is in room.entrances), this will enable room:accept(object) to return true.

(3) object:moveto(room) is called and the various messages (see `help exit-messages') are :announced/:told. Note that this, in accordance with the way the builtin move() (and hence the default :moveto()) works, we get a call to room:accept(object) which checks for the room itself being locked against the object, and otherwise returns true if the blessing in the previous step worked. The move is performed, here:exitfunc(object) and room:enterfunc(object) are called. In particular, room:enterfunc clears the blessing bestowed in (2) now that it is no longer needed.

In general, the move may fail, in which case we :announce the (o)nogo_msgs.


===== help $generic_db ===========================

Generic Database

This holds a collection of {string key, datum} pairs, where datum can beanything. At most one datum may be associated with any given string. Data may be anything (lists, strings, numbers, objectids). If you like, you can think of this as an array indexed by strings.
Verbs supplied include

:find(string) => datum, $ambiguous_match or $failed_match
:find_key(string) => full string key, $ambiguous_match or $failed_match
:find_exact(string) => datum or $failed_match (no partial matches)
:find_all(string) => list of all data corresponding to matching strings
:find_all_keys(string) => list of all matching strings
:insert(string,datum) if the string is already present in the db, changes the associated datum and returns {old_datum};
otherwise enters a new {string,datum} pair and return 0.
:delete(string) if there is a datum associated with string, remove this association and return {datum}; otherwise return 0.
:delete2(string,datum) if the given datum is associated with string, removes that association and return {datum}, if some other datum is associated with string, just return {other datum} otherwise return 0.
:clearall([4|3]) removes all associations from the database. optional argument changes the type of the database (4 is normal, 3 is a kludge for when the data are simply boolean flags i.e., this is a set of strings rather than a string-indexed array; more on this below)
count [entries|chars] in this provide some vague statistics about how big this thing is.

N.B. As entries get made, properties belonging to $generic_db.owner will be created on the db object itself. These properties will be created having flags as specified by .node_perms, which by default is "r", but can be changed to "" should you want to ensure that randoms don't have access to the raw information.

Implementation notes

The representation is as a `trie', a tree in which each internal node corresponds to a prefix shared by two or more strings in the db.
Each internal node is kept in a property named " "+<prefix>, where <prefix> is a prefix shared by all strings in the subtree under this node.
The property value is a 4 element list

this.(" "+<prefix>)[1] = <common>
maximal continuation shared by all strings beginning with prefix i.e., all these names actually begin with <prefix>+<common>

this.(" "+<prefix>)[2] = <continuations>
string of all characters <c> that can follow <prefix>+<common> for which there is more than one string in the db beginning with <prefix>+<common>+<c>

this.(" "+<prefix>)[3] = <exact_matches>
list of all strings in this subtree for which the character (or lack thereof) following the <prefix>+<common> substring suffices to determine the string.

this.(" "+<prefix>)[4] = <data>
list of data corresponding to the strings in [3].

Child nodes are this.(" "+<prefix>+<common>+<c>) for all <c> in this.(" "+<prefix>)[2]. The root node is this.(" ").
If, e.g., there are 2 or more strings in the db beginning with a, there will be a node this.(" a").
If all of these strings actually begin with "ani", then this.(" a")[1]=="ni".
The db consisting of the 5 correspondences

  {"animal", #1}
  {"anime",  #2}
  {"anil",   #3}
  {"anile",  #4}
  {"banal",  #5}

would be represented

this.(" ")    =={"",  "a",  {"banal"},         {#5}}
this.(" a")   =={"ni","lm", {},                {}}
this.(" anim")=={"",  "",   {"animal","anime"},{#1,#2}}
this.(" anil")=={"",  "",   {"anil","anile"},  {#3,#4}}
In some cases one may merely wish to hold a collection of strings without trying to associate a particular datum with each string. One may then instead set up a db without the fourth field on each of the properties. In this case the datum is taken to be the found string itself and that is what gets returned by :find*() in the event of a successful search.

:find and :find_key are then equivalent as are :find_all and
:find_all_keys. To setup the db this way, do a :clearall(3).
:clearall(4) reverts to the above described type of db with a separately kept datum. Note that you can't change the type without emptying the db. 3 and 4 are currently the only db types allowed.

===== help $generic_editor =========================

The Generic Editor enables a player to edit a list of strings. While one might contrive to use it directly, it is rather intended as a parent for some actual editor. It supplies the following commands:
say         <text>                      w*hat       
emote       <text>                      abort       
lis*t       [<range>] [nonum]           q*uit,done,pause 
ins*ert     [<ins>] ["<text>]           
n*ext,p*rev [n] ["<text>]               
del*ete     [<range>]                   
f*ind       /<str>[/[c][<range>]]       
s*ubst      /<str>1;/<str>2;[/[g][c][<range>]]
m*ove,c*opy [<range>] to <ins>          
join*l      [<range>]                   
fill        [<range>] [@<col>]          
$editor_help.(cmdname) descrbes cmdname
$editor_help.insert descrbes insertion points (<ins>)
$editor_help.ranges descrbes range specifications (<range>)

You'll notice that nowhere does it say how to load in a given list of strings or how and where one may save said list away when one is done editing. These commands are supplied by the child editor object. The generic editor contains only the code for editing lines, though it defines additional functions for use by the children:

returns the index (player in iff text has been loaded from somewhere, otherwise returns 0.

Note that, by default, there is a difference between

        having nothing loaded                (:text(who)==0) and 
        having loaded something with no text (:text(who)=={}).
If you don't care about this distinction in a particular case, just do (player in instead of this:loaded(player).
If you don't want your editor to make this distinction at all, do
        @stateprop texts={} for <youreditor>
which changes the initial value of :text() to {}

In all functions below, 'who' is the index returned by :loaded(player)

BTW, be careful about using 'player' in non-user (i.e., +x this-none-this) verbs --- much better to have the user verb get the index with :loaded() and then pass that around.

Also be careful about suspend() and verbs that call suspend(). In particular, the player's index in the .active list can change during the suspend interval, so you must be sure to obtain the index (e.g., using :loaded()) again after the suspend() returns.

For your non-user verbs, we have

returns E_PERM if the caller is not an editor verb and E_RANGE if 'who' does not point to a valid session.

which should take care of the more egregious security holes (but maybe not the less egregious ones). For getting and loading text, we have

the current text string list or 0 if nothing loaded yet.

loads the given list of strings as the text to be edited. this also resets the 'changed' flag and pushes the insertion point to the end. and various flags and properties (all of the set_* routines return E_PERM when not called from an editor verb, E_RANGE if who is out of bounds, E_INVARG if something is wrong with the 2nd arg, or the new value, which may not necessarily be the same as the 2nd arg (e.g., set_insertion(..,37) on a 5 line text buffer returns 6).

has the text been altered since the last save/load? (the child editor gets to define what "save" means).

Any child editor command that is considered to save the text should do a

Note that if the changed flag is 0, the session will be flushed when the player leaves the editor, so you may also want certain commands to do set_changed(who,1)...

room where the player came from.

can be used to change the room the player will return to when finished editing. Since origin gets set even in cases where the player teleports into the editor you probably won't usually need to do this.

current insertion point.

linenumber needs to be a positive integer and will get

whether the current editing session has been made globally readable.

change the readability of the current editing session. This is used by the publish/perish verbs.

We also provide

If the player has a previous unsaved (i.e., :changed()!=0) session, we return to it, moving the player to the editor. If the player is already in the editor, this has no effect other than to print a few nasty messages. In any case a :changed() session must be aborted or set_changed(,0) before anything else can be started

Otherwise, we pass the arguments (which are assumed to be the result of some munging of the command line) to :parse_invoke(), move the player to the editor and load whatever parse_invoke() specified. The only interpretation the generic editor makes on the arguments is that if the boolean value of the first is true, this indicates that the player wanted to load something as opposed to resume a previous session. Usually a command calling :invoke will have a true (i.e., nonzero number, nonempty list or string) first arg iff the command line is nonempty, in which case 'args' works fine for this purpose.

If the command parses sucessfully (:parse_invoke() returns a list), we move the player to the editor if necessary and then call :init_session() to set things up.

The child editor is assumed to provide

given :invoke()'s arguments, determines what the player wants to edit. It either returns 0 and reports syntax errors to player, or it returns some list that :init_session() will understand.

where spec is something that was returned by :parse_invoke(). Loads the text and sets the stateprops (below) to indicate that we are working on whatever it is we're suppose to be working on.

returns a string X as in "You are working on X." This is called by the 'w*hat' command, among other things.

Child editors may have their own properties giving state information for the various editing sessions. The value of each such property will be a list giving a value for each player in the editor. For each such property, you should, once the editor object has been created, initialize the property to {} and do one of

    @stateprop <propname>                 for <editor>
    @stateprop <propname>=<default-value> for <editor>
               (0 is the default <default-value>)
Henceforth, adding and deleting new editing sessions will amend the list held by the given property. The value of the property for a given session can be obtained via this.<propname>[player in] and can be changed with a corresponding listset() call. The usual idiom for an editor command is
      ... various references to  this.<propname>[who] ...
To remove such a property from the list of such state properties:
    @rmstateprop <propname> from <editor>
Note that you can only do this with properties defined on the child editor itself.

Sometimes you may wish to @stateprop a new property on an editor where active editing sessions exist. @stateprop will fail if the property in question does not hold a list of the correct length (== length(; one value for each editing session). You need to either give the @flush command to clear out all sessions and boot all players currently in the editor or somehow manually initialize the property to a list of appropriate values and pray that nobody enters/exits the editor between the property initialization and the @stateprop command --- this problem can be avoided by doing an eval() that does all of the initializations (beware of suspends()) and calls :set_stateprops directly. Incidentally, the @flush command may be used at any time to clean out the editor or to remove all sessions older than a given date.

There are also numerous _msg properties that may be customized

    @depart            announced at the origin when :invoke() is called. 
    @return            announced at the origin the player is returned
    @nothing_loaded    printed when user attempts editing 
                       before anything has been loaded.
    @no_text           response to 'list' when :text()=={}
    @no_change         printed by 'what' when :changed()==0
    @change            printed by 'what' when :changed()==1
    @no_littering      printed upon leaving the editor with :changed()==1.
    @previous_session  printed by :invoke() when player tries to start a 
                       new session without aborting or saving the old one
The general procedure for creating a child editor:

. @create $generic_editor named <editor>

. define additional <editor> verbs/properties
At the very least you need 'edit' and 'save' commands.
Usually you can get away with just having 'edit' call :invoke();
Presumably, you'll need at least a command to load text from somewhere as well as a command to save it back out.

. define a verb (somewhere) to invoke the editor
This could be just a one-liner that calls <editor>:invoke(args,verb). Either that or

. you have to set up an exit somewhere whose destination is <editor>
. you have to advertise the object number so that people can teleport to it. . @stateprop x for <editor>

. if you want the 'abort' command to boot the player from the editor do <editor>.exit_on_abort = 1;

. set <editor>.commands to be the list of additional commands defined by <editor>.
Each element of the list is itself a list of the form {name,args}. set <editor>.commands2 to be the list of commands that should appear in the `look' listing, and should be a list of strings appearing as names in .commands on either <editor> or some editor ancestor. look at $verb_editor or $note_editor for an example.

. If you want to have help text for new verbs you define, create a child of $generic_help and add properties to this object for each of the topics that you want to provide help text.
Finally, set <editor>.help = {this object} so that the help system knows to consult this object.

===== help $generic_help ========== SEE The Help System

===== help $generic_options ========================

Generic Option Package

It occasionally happens that one has a command or set of commands for which one wishes to provide several options/flags that a player can set to customize the command's behavior for him/herself. Making each option a separate property is a bit expensive, especially when the option in question is merely a boolean flag that gets set to false in most cases. This package provides an alternative, as well as providing a uniform set of commands for setting these flags/options and checking that the values given are of appropriate types.

Instead of needing several properties, only one is required to store a list containing values for all of the options. An "option package" (pkg, below) is then an object of this class, which provides routines for manipulating such lists.

The set of option names is divided into a set of "real" options, those whose names will actually appear in a given list, and "extras" which are either synonyms for or represent combinations of real options.

 pkg:add_name(name)      adds name to .names  (remove it from .extras if there)
 pkg:add_name(name,1)    adds name to .extras (remove it from .names if there)
    => 1 - ok, 0 - already added, E_INVARG - illegal name, E_PERM

 pkg:remove_name(name)   remove name from either .names or .extras
    => 1 - ok, 0 - not present, E_PERM

For setting or retrieving values we have
    => value (or 0 if name isn't a real option)
    => revised options (or string error message if something goes wrong)
By default, a given option can only be a boolean flag, having one of the values 0 (absent from the list), or 1 (present in the list). :set translates 0/""/{} to 0 and any other non-object value to 1.

One may however designate a wider range of possible values for an option "foo" by either installing one of

    -- list of allowed types, 
       e.g., {NUM,STR}   => must be a number or a string
       e.g., {OBJ,{OBJ}} => must be an object or a list of objects
    for anything fancier use:

    => string error message or {value munged as desired}
In general, the only restriction on option values is that 0 is the only false value; setting an option to "" or {} sets it to 0. Every option defaults to 0, and no matter what you install as .type_foo or :check_foo(), 0 will always be a legal value for option "foo".

When presented with an option that is in .extras, :set will typecheck the value as described, however, then :actual(name, value) will be called to obtain a list of {name-of-real-option, value} pairs indicating which combination of real options should be set.

Other verbs
    parses the command line arguments of a @whatever_option command
    => {optionname, value}  if the player wants to set an option
    => {optionname}         if the player wants to view an option
    => string error message  otherwise

  one may install pkg:parse_foo to parse arguments for option "foo" 
    !foo     => {"foo",0}  (:parse_foo not called)
    foo=     => {"foo",0}  (:parse_foo not called)
    -foo     => {"foo",0}  (:parse_foo not called)
    +foo     => pkg:parse_foo("foo",1)
    foo=word => pkg:parse_foo("foo","word")
    foo word1 word2    => pkg:parse_foo("foo",{"word1","word2"})
    foo is word1 word2 => pkg:parse_foo("foo",{"word1","word2"})

 pkg:show(options,name|list of names)
    => list of strings describing the current value of the named option(s).
       calls     pkg:show_foo(options,list of names) or
       refers to pkg.show_foo
       to describe option "foo"
(see sources for details... at some point I'll finish writing this... --Rog)

===== help $guest_log =============================

records guest connect/disconnect events.

.max_events  -- maximum number of connect/disconnect events kept
.connections -- actual list of events, the most recent ones first
    each element is of the form
      {object, is_connect, time, site}
    object is the particular guest that (dis)connected
    is_connect 1 or 0 according as this is a connect or not.

  => site name of WHO at the particular time
     (or 0 if WHO was not logged in then)

  prints a listing of the last n events