Mocha: Ishar MUD Online Creation Language


Mocha is an online creation language designed for and currently in use on the mud Ishar, a multi-player, real-time fantasy game. The language allows the creators of the virtual world to design special effects for their own sections of the world and for general use by other creators. The design and original implementation of the language, both in its interpreted form and in its compilation to C, are the work of Steven S. Lumetta, a graduate student at the University of California at Berkeley, and any comments or questions should be directed to him. All aspects of Mocha, including the source code and all documentation, are covered by the GNU public license, although the source code is not yet publicly available (and no date is set for its release).


Intermediate material



Advanced material


Table of Contents

Introduction
Types and Signatures
The Body of a Handler
Variable Declarations
Routines
The Data Definition
Built-in Operations
Types
The List Type
Built-in Commands
The Event Handlers
Valid Values, Exceptions, and Hidden Entities
The MUD Function Interface
Code Examples
Search for keywords
(Cyberverse documents only):


Introduction

Mocha allows a creator to define special reactions and behaviors for rooms, mobiles, and objects. Simple examples include rooms in which players are unable to see, talking mobiles, and objects which change form when a certain spell is cast upon them. Each behavior is written into a named document called a function. The name of the function is used within the virtual world to identify the behavior described by the function. Mocha functions are further grouped into libraries. One library exists for each area in the MUD, and other libraries can be added by the gods. The latter libraries are named for the type of behaviors they provide: a "trap" library for generic trap functions, an "example" library for sample Mocha code, etc.

Each Mocha function is associated with rooms, objects, or mobiles. Once someone writes a function, any creator can (with permission) use the name of the function to mark items of the appropriate type (e.g., rooms for a room function) as having that particular behavior. Mocha functions are generally customizable, so that a single function can manage many similar behaviors.

Mocha works by allowing a creator to define special responses to events in the virtual world. When certain things happen, the MUD signals an event by telling everything that might want to know about the event. For example, when a player types, "say hello," the MUD tells the room, any mobiles in the room, any objects worn or carried by the player, and any objects in the room that the player is trying to say hello. Normally, none of these things care about such an event, and the MUD handles it in the usual fashion, in this case by telling everyone in the room what was said. If, by chance, a nearby mobile responds to being greeted, that mobile might react to the event by preparing to speak. Responding to a specific event is called handling the event, and the section of the function which describes the way an item responds is called a handler for that event.

The MUD signals a vast number of events, including nearly every command in the game as well as a number of events with more context. Each event has a name, used by a creator to mark the corresponding handler in a function. The say command, for example, uses the name command_say. Other command event names have a similar form: they begin with "command_" and end in the name of the command. Many other events begin with "do_."

When a player quaffs a potion, for example, the MUD first signals a command_quaff event to everything nearby. If nothing handles this event, the server figures out which potion is to be quaffed and signals a do_quaff event to the potion. Since a potion is most likely to exhibit any unusual behavior only when quaffed, most creators prefer to provide a handler for the do_quaff event rather than for the command_quaff event when writing a potion function. In the typical case, a handler for command_quaff must first check whether the player intends to quaff the potion or something else, then check whether the player is in a proper position to quaff the potion. By handling do_quaff instead, the creator allows the MUD to take care of these considerations and needs only to specify the actual effects of quaffing the potion.

When the MUD calls a handler, it passes along several pieces of information about the event. Each piece is called a parameter of the handler, and together, along with their order, they form the handler's interface. A handler for a typical command event, for example, receives the item with the special behavior, the person issuing the command, and the arguments passed to the command. If the player Asylenth types "get 2.key bag," a room function's handler for command_get would receive a reference to the room, a reference to Asylenth, and a package of information including the strings "2.key" and "bag." The handler can then use these pieces of information to make decisions and define behavior.

When a handler is finished, it must return a piece of information to the MUD to tell the MUD what to do next. A handler can return one of three distinct values. One value tells the MUD to continue with the usual processing of the event; a second tells the MUD to stop immediately; and the third tells the MUD that a player associated with the event has been killed.

At this point, we are ready to start talking about the actual syntax of Mocha, or how the text of a function must appear.


Types and Signatures

We have been talking about moving around pieces of information in a fairly abstract way, but Mocha is actually very strict about what information is sent or received. The strictness not only helps to eliminate some possibilities for confusing mistakes on the part of creators, but also prevents creators from causing the MUD to crash.

Each piece of information, whether it be a parameter to a handler, a value returned from a handler, or one of the others we will discuss below, must have a type. The type of a handler's return value, for example, is known as special. The named derives from the common and perhaps antiquated MUD synonym for a function: a special, which in turn is an abbreviation of "a special function." The name of a second type comes from mathematics: an integer represents any whole number, be it positive, negative, or zero. Since Mocha is running on a digital computer, an integer can really only represent numbers up to two billion, but you'll probably never notice that limit. The following list describes the simple types handled by Mocha:

arguments
represents the package of information passed to a MUD command, including the number and the first and second arguments to the command. When a player types "get 2 meat.pie 3.blue.bag," for example, the handler parameter of type arguments is used to obtain the integer 2 and the strings "meat.pie" and "3.blue.bag."

boolean
represents a boolean logical value, either true or false. This type is the one typically used when making decisions in code: if some condition is true, then do one thing, otherwise (if it is false), do another thing.

dice
represents a set of dice (e.g., 2D4+1). Mocha allows a creator to find the average, to roll the dice, to dissect them into three integers, etc.

integer
represents a whole number from -2,147,483,648 to 2,147,483,647 (i.e., a 32-bit signed integer). Real and rational numbers (e.g., pi and 2.3) are not available in Mocha, although a creator can represent a wide range of fractional values with integers.

special
represents a value returned from a handler. The value ignored tells the MUD to continue with normal event processing, the value handled tells it to cancel normal processing, and the value fatal tells it that a player associated with the event has been slain.

spell
represents a magical spell and a casting level. Mocha allows a creator to have a player or mobile cast a spell or to break it up into the spell name (a string) and the casting level (an integer).

string
represents a word or phrase (e.g., "the door opens"). Strings are often used to locate items and to describe various effects to players.

In addition to these simple types, Mocha handles a number of more complex types. The following types share two common features. First, information of any of these types can have the value none, indicating that the information refers to nothing. Second, because these types are associated with things in the virtual world, information stored between execution of handlers for a function can change. Specifically, the information can change from having a valid reference to referring to nothing (having the value none). For the type person, for example, this change occurs when the person is slain. The following list describes some of the more complex types in Mocha:

entity
represents an object, a person, or a room. These three things have much in common in the virtual world, and the entity type allows a creator to deal with these common features. Mocha also allows you to change the type of an entity to the appropriate subtype.

exit
represents an exit in the virtual world. Mocha allows a creator to check and set various properties of the exit, to find the room from which the exits permits egress, and other useful tasks.

object
represents an object in the virtual world.

person
represents a person (a player or a mobile) in the virtual world.

room
represents a room in the virtual world.

We postpone discussion of the remaining Mocha types for now. Given an understanding of types, we can now define the signature of a handler: the written prologue to a handler that includes the return type, the handler name, and the interface. For a mobile function, the signature for command_say appears as follows:
special
command_say (person mob, person pc, arguments args)
On the first line is the return type--always special for a handler. The second line begins with the name of the handler and continues with the interface, written as a comma-separated list of parameters surrounded by parentheses. Each parameter consists of a type and a name for the piece of information. The name is generally mnemonic--it helps someone reading the function to understand what it represents. In the signature shown above, the first parameter is a reference to the mobile with the behavior defined by the function. If this were instead a room function, this parameter would instead appear with type room and a name more appropriate for a room, perhaps "rm." Similarly, for an object function, we might write object obj for the first parameter. The second parameter refers to the person trying to say something, and the third gives the arguments to the command.


The Body of a Handler

After the signature, a creator must write the body of the handler. Mocha makes use of a syntax and style much like the programming language C with a few pieces drawn from the language C++. The body of a handler consists of some number of variable declarations followed by some number of statements, all surrounded by a pair of braces.

Ignoring the variable declarations for now, let us write an extremely simple example of a handler. Let's say that we want a certain mobile to do twice the normal amount of damage plus one. Forget for now the fact that we might simply change the mobile's damage dice rather than writing a function--we'll make the behavior more complex later. To intervene when the mobile makes an attack, we must catch the do_attack event, which has the following signature:

special
do_attack (person mob, person vict, object wpn, integer type, integer amt,
           integer quality)
For this handler, mob refers to the mobile; vict refers to the person being attacked; wpn refers to the weapon being used in the attack and has the value none when the mobile is unarmed; type is the type of damage being done (drawn from a list of values representing different types); amt is the amount of damage, with 0 indicating a miss, and quality is a measure of the success of the attack. Unlike most handler parameters, several of do_attack's parameter are returned to the MUD, allowing a handler to change the amount, type, and quality of damage while allowing the MUD to take care of sending the damage messages and killing the victim if appropriate.

For our purposes, the only important value is the amount of damage, which we intend to double, then add one. The following handler accomplishes the task:

special
do_attack (person mob, person vict, object wpn, integer type, integer amt,
           integer quality)
{
    amt = 2 * amt + 1;

    return ignored;
}
The body of our handler consists of two statements and a blank line. Mocha ignores white space (spaces, tabs, and carriage returns) except in strings and names. We could not, for example, have written amt as "a m t."

The first statement is an assignment statement. The expression on the right hand of the equal sign is assigned to the parameter on the left hand side. In this case, Mocha finds the number represented by amt, multiplies it by two, adds one, and pushes it back into the amt parameter. Note that an assignment is irrevocable--we can no longer read the original value of the parameter on the left hand side (although in this case we could recompute it fairly easily using the current value).

The second statement returns a value to the MUD. In this case, we return the value ignored, telling the MUD that it should go ahead and process the attack with the altered damage value. Each statement is followed by a semicolon. If we were to test this handler in the MUD, we would find (perhaps to our surprise) that mobiles using this function never miss an attack. Even when we make the odds of hitting incredibly low, they manage to do a single point of damage every time they try to hit. Why is this so?

Looking back to the definition of the do_attack event, we see that when the mobile misses an attack, the MUD still signals the event, but sends 0 as the amount of damage. Our handler blindly doubles the 0 and adds 1, resulting in a hit for a single point of damage. To prevent these specious hits, we must add a conditional statement:

special
do_attack (person mob, person vict, object wpn, integer type, integer amt,
           integer quality)
{
    if (amt > 0)               // This is the new line.
        amt = 2 * amt + 1;

    return ignored;
}
The conditional statement appears in the first line of the handler body; to the right of the condition is a C++-style comment: when Mocha sees two adjacent slashes outside of a string, it ignores the rest of the line. A conditional statement begins with the keyword if, followed by a condition (a piece of information with type boolean), and completed by another statement. Note that no semicolon separates the first two parts of the conditional statement from the statement making up the third part. Also note that the latter statement is indented to indicate that it is only executed when the condition is true.

The condition in this case is that the amount of damage is greater than zero--that is, that the mobile has actually hit the victim. If the mobile misses, amt represents the value zero and the condition evaluates to false. When the condition is false, the statement following the condition is ignored. As we expect, the mobile hits at a normal rate after our change.


Variable Declarations

Variable declarations are very much like parameters: each specifies a type and a name and represents a piece of information that will be used with

Routines

Routines are like handlers in form, but do not correspond to any event. Instead, routines are used to perform tasks for handlers. If, for example, I want the same effect for quaffing or drinking from a container, I can define a single routine, perhaps called "drink_or_quaff," and have both the do_quaff and the do_drink handlers make use of that routine.

The data definition for a function specifies the information used by the function during its operation. A room that remembers the person who last occupied it, for example, would do so by declaring a space for that information in its data definition. The data definition is divided into four sections, each of which has a distinct meaning. The first section defines constant values for use in the handlers and routines. In many cases, functions become more readable when symbolic names such as "attack_delay" replace numeric values, such as 18. The reason is hopefully obvious. The second section defines public values: values which can be overridden by adding extra descriptions to items making use of the function. I might, for example, declare "attack_delay" as a public variable rather than a constant, setting the default value to 18. If someone else uses my function and wants whatever is attacking to attack more quickly, they can add an extra description called ".ATTACK_DELAY" and set the text to a smaller number. One copy of attack_delay exists for each virtual number using the function, so other people's selection of options will not affect my items. The third section defines private data: data which cannot be overridden by extra descriptions, but which also create only one copy per virtual number. A group of mobiles with a shared group of enemies might make use of private information. The fourth section defines the data needed for each instance of an item. Since only one room with a given virtual number exists, this section does not appear in room functions. Instance data might be used for a sac of poison that must remember who drank it (to apply damage over time); clearly such a function with only one place to store the name of the drinker would not work.


Mocha is a little like C, a little like C++, and a lot like neither.

With Mocha, a creator can attach individual functionality to any mobile, object, or room. Functions can be further customized through the use of extra descriptions, allowing each function to be used with a variety of items.

Each function in Mocha is given a unique name and is stored in a file with that name. Files are then further grouped into libraries: one library exists for each area in the MUD; other libraries can be added by the gods and might include, for example, a "trap" library for generic trap functions.

A Mocha file consists of a data description and a number of event handlers. An event handler is called whenever that particular event occurs. The "command_kill" event, for example, is called whenever someone attempts to attack another person in the presence of the item using the function in which the handler is defined.

The data description defines and names the variables used for the event handlers in the file. The description can instead name another Mocha file, in which case all of the event handlers and data are inherited from the named file. Individual event handlers can then be overridden, allowing a creator to slightly alter an existing function without rewriting it entirely.


The Data Definition

<function type> {
        int num_tries = 3;
    public:
        action[PVd] to_victim = "@n strikes you %d times!\n";
    private:
        integer max_uses = 4;
    instance:
        person victim;
};
The function type defines the type of the function and must be person, object, or room.

The data definition includes four possible sections which must be defined in the correct order. The first is constants, which allows the creator to use symbolic names for values. In the example above, the name "num_tries" has been assigned the integer value 3. Whenever "num_tries" is encountered in the event handlers, it is treated as the integer value 3. The second section, from "public:" to "private:" is a list of variables unique to the item type (e.g., having a single copy for each type of mobile rather than a copy for each instance of the mobile). The value of a public variables is automatically overridden by extra descriptions. If, in the example, the item had an extra description with keyword ".TO_VICTIM," the string used for that item would be taken from the extra description rather than the default shown above. The third section of the data definition, from "private:" to "instance:," is again related to types of items rather than individual items, but cannot be overridden by extra descriptions. The last section, after the "instance:" marker, is a list of variables created for each instance of an item. Were the definition above used with a mobile, each instance of the mobile would have its own victim.

A special construction in the public section allows a creator to define symbolic names for a particular integer variable. The variable should be initialized with "one_of { <value name 1>, <value name 2>, ...}." The names are then defined as constants for the event handlers, and the default value for the variable is the first value mentioned in the one_of construction.

base <base function>
This form derives the function type and data definition from the file <base function>. All event handlers are also inherited, but can be overridden by simply redefining them later in the file.


Built-in Operations

  • Arithmetic operators
    +
    plus/join strings
    -
    minus
    *
    times
    /
    divide
    %
    modulo

  • Comparison operators
    ==
    equality
    !=
    inequality
    <
    less than
    <=
    less than or equal
    >=
    greater than or equal
    >
    greater than

  • Assignment operators
    =
    assign
    +=
    add and assign
    -=
    subtract and assign
    *=
    multiply and assign
    /=
    divide and assign
    %=
    take modulo and assign
    &=
    bitwise and and assign
    |=
    bitwise or and assign
    ^=
    bitwise xor and assign
    ++
    increment and assign (pre- or post-)
    --
    decrement and assign (pre- or post-)

  • Logical operators
    &&
    logical and
    ||
    logical or
    ^^
    logical xor
    !
    logical not

  • Bitwise operators
    &
    bitwise and
    |
    bitwise or
    ^
    bitwise xor
    ~
    bitwise not

  • Conditional operator
    ? :
    conditional

With the exception of +, arithmetic, assignment, and bitwise operators require integer arguments and return integer results. The + operator can also be used to join two strings into a single string. Comparison operators require two arguments of the same type and return boolean results; ordering comparisons (all but equality and inequality) require integer arguments. Logical operators require boolean arguments and return boolean results. The conditional operator requires a boolean condition and results of the same type. Note that these operators do allow for implicit casts---one can compare an entity with a person, for example.

Types

Types include: The type "action" is followed by an action specifier: a set of characters surrounded by braces. Actions are used with the "show_action" function, and the action specifier depends on the way the action is to be used.
P -- a player appears in the show_action call
V -- a victim appears in the show_action call
O -- an object appears in the show_action call
s -- a string appears in the show_action call
d -- an integer appears in the show_action call

P, V, and O should appear at the start of the specifier. d and s may appear more than once, but the order must match the order of the "%d" and "%s" escapes in the action string.


The List Type

In addition to the basic types, the data definition can contain lists of most basic types. For example, you might use an "integer list" to record the virtual numbers of items sold in a shop or an "action[P] list" to hold the lines spoken by a mobile.

Constant lists are specified as a number of items separated by commas and placed in braces (e.g., {1, 2, 3}). Unlike the similar spell constant value, which takes the form {<level>, "<spell name>"}, constant list elements must all have the same type.

void <list>.append (<list type> value)
adds a value to the end of a list

void <list>.delete (<list type> value)
removes the first instance of a specified value from a list, or does nothing if the list does not contain the value; use remove if you know the position, as delete must perform a search

void <list>.destroy
destroy empties a list

boolean <list>.has_member (<list type> value)
checks whether a value is in a list or not

integer <list>.index (<list type> value)
finds the position of a given value, or returns no_match if the value is not in the list

void <list>.insert (integer position, <list type> value)
pushes a value into a list at a particular position; the position must be between 1 and the length of the list + 1 (which is the same as append)

integer <list>.length
returns the number of elements in a list

<list type> <list>.member (integer position)
returns the member of a list at a given position

void <list>.remove (integer position)
removes the member of a list at a given position

void <list>.replace (<list type> old_value, <list type> new_value)
replaces the first instance of old_value in a list with new_value, or does nothing if the old_value does not appear in the list; if you know the position of the element to be replaced, use set instead, as replace must perform a search

void <list>.set (integer position, <list type> value)
replaces the element at a particular position in a list with value

All list positions are 1-based. Any attempt to use positions outside of the list bounds (with the exception of insert, as noted above) results in a runtime error. Lists are currently limited to 100 elements, and trying to grow them beyond that limit also results in a runtime error.


Built-in Commands

compound statements

A single statement can always be replaced by an arbitrary number of statements surrounded by braces (i.e., {}).

conditionals

if (<boolean expression>) ...
else ...

The above construction evaluates the boolean expression and then executes either the first statement or the optional statement following the "else" marker.

return

The return command is used to return a value to the caller of the event handler or function. For event handlers, a creator can return the value "ignored" to indicate that normal handling should occur, "handled" to skip normal handling, or "fatal" to indicate that the player has died (normal handling is skipped in this case as well).

show_action

show_action is really more of a function than a command, but since it takes a variable number of arguments, we describe it here. The first argument to show_action is an action variable. The next few arguments specify the recipients of the action message and should be chosen from the following:

After the recipient arguments, if 'P' appears in the action specifier, then the next argument is a player (an expression of type person). If 'V' appears in the action specifier, the next argument is the victim (also an expression of type person). Next is an object provided that 'O' appears in the specifier. The remainder of the arguments are integers or strings in the order given by the action specifier.

Also see the room_echo function.

loops

repeat (<integer expression>) ...
repeat the body of the loop some number of times

for each <object variable> in (<expression>) ...
repeat the body of the loop for each object inside of another object, person (inventory), or room; this includes all objects inside of objects as well

for each <object variable> on (<expression>) ...
repeat the body of the loop for each object on top of another object or person (equipment); this includes all objects inside of the first level of objects as well
for each <person variable> in (<expression>) ...
repeat the body of the loop for each person in a room

for each <person variable> on (<expression>) ...
repeat the body of the loop for each person on top of another object or person (rider); this includes all persons on top of the first level of people as well

for visible <object variable> in (<expression>) ...
repeat the body of the loop for each object inside of another object, person (inventory), or room; this includes only objects immediately inside the other object or atop an object inside the other object

for visible <object variable> on (<expression>) ...
repeat the body of the loop for each object on top of another object or person (equipment); this includes only objects visible at the level of the other item

for each player <person variable> ...
repeat the body of the loop for each player

for each connection <person variable> ...
repeat the body of the loop for each connection to the MUD

Inside of a loop, a creator may use the "break" command to terminate the loop and the "continue" command to jump to the next iteration of the loop.

switch

To handle multiple cases, a creator can use the "switch" command in place of multiple if-else commands. Switch takes an integer expression argument and executes the case corresponding to the value of the expression. Cases must be labeled with one or more constant integer values or the special label "default," which handles all values (and must appear only on the last case if it appears). The syntax in Mocha is as follows:

switch (<integer expression>>) {
    1, 3 {
        ...
    }
    default {
        ...
    }
}

min, max, and range

min, max, and range are integer functions returning respectively the minimum of two expressions, the maximum of two expressions, and the value of an expression bounded by two other expressions.


The Event Handlers

The most common event handler is the command, which has the following prototype:

special
command_<command_name> (<func_type> item, person pc, arguments args)

Here the <command_name> might be anything from kneel to follow to kill. The pc is the person who issued the command, the item is the item carrying the function (room, mobile, or object), and the arguments are the additional arguments to the command.

special
do_attack (person mob, person pc, integer type, integer amt)

The mob has attacked the pc and inflicted damage. Type and amt can be changed.


Valid Values, Exceptions, and Hidden Entities

Before a handler calls a function, it should check that all of the values to be passed to the function are valid. Some functions allow the value none to be passed in place of some arguments, while others do not. Other functions require that an integer parameter appear in a list of constants. All functions reject entities which have been destroyed. If a function rejects one of the arguments passed by a handler, it signals an exception and stops executing the handler. A message is sent to the author of the function and the last editor of the function describing the problem. One message will be sent each time the handler fires and signals an exception. To prevent exceptions, a handler should check the validity of arguments using the following functions:
boolean ent.valid;
Returns true if the entity ent is valid (not destroyed and not none), false otherwise.

boolean evt.valid;
Returns true if the event evt is valid (not cancelled and not none), false otherwise.

boolean ex.valid;
Returns true if the exit ex is valid (not destroyed and not none), false otherwise.

boolean spl.valid;
Returns true if the spell spl is valid (not a non-existent spell), false otherwise.

In addition to checking for invalid items, a handler may also want to check whether or not an entity should be touched to prevent annoying immortals with "nohassle" flags. While the only way for a handler to affect such an entity is for the entity to initiate some action in the presence of the handler (i.e., functions such as find_person will never return someone with a nohassle flag), the person might still prefer not to be subject to the handler's antics. The following functions help a handler to make such decisions:

boolean ent.usable;
Returns true if the handler has the permission of the entity ent (or ent's owner) to affect it, otherwise returns false.

boolean nohassled (person pc);
A special form of usable for persons--returns the same as given above.


The MUD Function Interface


Code Examples

suck_blood -- a specialized mobile attack
troll_leader -- a speaking mobile
venom sac -- a poison function and its tutorial