Background
This document is an attempt to provide examples of how to use the
new object hierarchy. Please report any inaccuracies in this document
to vender@plains.nodak.edu.
The Basics
This document assumes that the reader has seen
Implementation.html and has access to a copy of the LambdaMOO
programmer's manual.
Get and Set
Using the get and set functions is vitally important. Although
the LambdaMOO server allows you to set property values directly, most of
the time you should use get and set. This is especially true if the
property you are manipulating is one of the local parameters for the
game players. The reasons are
- Important parameters are checked for type.
- Setting the property directly bypasses the notify_change and notify_update
mechanisms.
Doing so on an object is a scene with active players or objects
will result in discontinuity in the user experience, and probably
interfere with any agent objects watching that property.
- Certain properties are intended to have side effects, or may not
actually get stored on the object.
Scene Root
The server maintains a set of disjoint virtual spaces for player interaction.
The typical LambdaMOO approach is to make each of these spaces an
instance of class Room, and place the significant objects for that
space directly in the room. Normally
the location of each room is ignored, and typically set to $nothing. Because
The server maintains a set of disjoint virtual spaces which the players
move between. The normal LambdaMOO approach to this problem is to construct
each of these spaces as a room, and effectively maintain a very shallow
scene graph. The approach used by the Virtual Cell database is slighly
different.
- The root of each scene is required to be an instance of
class SceneRoot. These objects have no geometry
associated with them, and serve mainly to contain space.
- Inside of each scene root are placed the top level rooms
for that scene.
- The contents of each room are added recursively until the graph
is complete.
So, to build a scene to display a cell process, the sequence of operations
would be
- Create an instance of SceneRoot.
- Create an instance of Generic Room, and move it into the new SceneRoot.
Adjust the properties of the room to display the correct
VRML for the walls of the room as needed.
- Create appropriate instances of Generic (#242) as necessary to
describe the things inside the room. Move each object into the
room.
Building a reactive object
By default, server objects aren't interested in anything. In order to
receive updates on another object, the verb add_interest is used, as in
TargetObject:add_interest(InterestedObject) where
normally an object will specify itself as the interested object.
When updates are received, the notify_change verb is called as
InterestedObject:notify_change(What,property,
new value). Similarly for say and emote, notify_say and
notify_emote are called to propogate information.
To explain how the code is intended to work, the next few sections will
explain how "Generic Robot" was designed, and was tasks it expects.
Generic Robot
The following elements are essential for an object to be reactive
- Express interest in a given set of objects, and override
the default notify_change (because does nothing by default).
- Except for demo peices, an interest_list property is good idea
when verifying that changes aren't being missed.
Generic Robot has two verbs, "express_interest" and "verify_interests"
to help maintain this code.
- By default, an object automatically removes interest in its previous
location. Objects maintaining an interest list should override
set and check for setting of position. Generic Robot does this
automatically.
- By convention, Generic Robot calls respond in reaction to both
emotes and speech. When deriving agents from "Generic Robot",
the convention is that respond returns 0 when the response
isn't handled. "Generic Robot" also ignores its own speech and
emotes to avoid infinite loops.
Generic VRML Scripting
At the moment, the most flexible model of input and output from a VRML object
is implemented by the GenericIO subclass of Generic. Currently the
following conventions are used
- Two lists of name and type pairs must be in the parameters sent to the
client. The names of these lists are vrml_inputs and vrml_outputs.
The first element of the pair is the name, and the second is the local
type. vrml_specials is a subset of the vrml_inputs names denoting
values which must be set each time the node is moved or re-enters
the scene graph.
- Recognized types include SFBool, SFVec3f, SFInt32, and SFRotation.
Conversion are as follows (in order: VRML to LambdaMOO, LambdaMOO to VRML)
- SFBool: false to 0, true to 1. Non-zero integer to true, zero to false.
- SFInt32: converted to integer both ways.
- SFRotation: Converted to and from a list of four floats.
Ordered as x,y,z,theta.
- SFVec3f: Converted to and from a list of three floats.
Ordered as x,y,z.
- SFColor: Handled exactly like SFVec3f, with no checking of color
ranges.
- SFVec2f: Converted to and from a list of two floats.
Ordered as x,y.
- SFFloat: Converted to and from MOO float to VRML float.
- MFFloat: Converted to and from a list of floats.
- MFInt32: Contered to and from lists of integers.
- MFRotation: Converted to and from lists of SFRotation's.
- MFVec3f: Converted to and from lists of SFVec3f's.
- MFColor: Handled exactly like MFVec3f, with no checking of color
ranges.
- MFVec2f: Converted to and from lists of SFVec2f's.
- SFString: Converted to and from strings with quoting.
- MFString: Converted to and from lists of SFString.
- GenericIO will probably never support MFNode or SFNode event types
because it would entail converting between complete MOO objects
and partial geometries.
- SFImage, SFTime and MFTime are on the list for
possible implementation if they prove useful. The probable order of
implementation is as listed.
- If the server side representation lacks a property for a vrml_input,
it is assumed that the input represents an eventIn without corresponding
state information, or rather that the state information is covered by
one of the existing inputs. E.g., an input to add an element to a graph
would lack a property, with the input to initialize the graph would
have a server property.
- For each name in the inputs, the corresponding eventIn is found by
prepending "set_" to the name. Updates from the MOO, and the initial
values of the properties, will result in events.
- For each name in the outputs, the corresponding eventOut is found
by appending "_changed" to the name. Updates will be propogated to
the MOO by sending :update( , )
The simplest example of something useful in this case is the creation of
a multiuser rotation control. Assuming the VRML looked something like
PROTO RotatorControl [
eventOut SFRotation user_rotation_changed
] {
# Insert stuff here
}
RotatorControl {}
the appropriate vrml_output list would be { {"user_rotation", "SFRotation"} }.
The simplest example of using a simple PROTO node would be the case of
a VRML file looking like
PROTO GenericJoint [
eventIn SFRotation set_direction
] {
# Insert stuff here
# see http://caithness.cs.ndsu.nodak.edu/~vender/WRL/BradRoom/gen_joint.wrl
#
}
GenericJoint {}
The appropriate vrml_input would be { {"direction", "SFRotation"} }.
The VRML object will initialize to the last known value from the server,
and will follow any updates from that value. Any special animation of
transitions must be hand coded in the VRML file, however.