Massive3 App Prog Intro

Download as pdf or txt
Download as pdf or txt
You are on page 1of 7

MASSIVE-3 / HIVEK Application Programming: Basics

Chris Greenhalgh, 24th June 1999.

Agents and processes


In MASSIVE-3, each process or application has exactly one Agent object. Logically the Agent represents the
process’s thread of execution. The Agent also handles all communication to the process, e.g. receiving network
messages on behalf of its local environment replicas (see below).
The Agent provides support for a main (single threaded) execution loop, each iteration of which corresponds to
one (local) frame of the evolution of all local environments. For user clients this will also correspond to
rendering one graphical frame. The Agent also supports timer-based callbacks, and callbacks on network
events (via associated NetworkEventHandler and Network objects for the process).
Callbacks of Socket object events should normally be registered directly with the NetworkEventHandler object,
which is in turn invoked periodically by the Agent main loop.
A Simple Agent
A minimal application would be as follows, including only a Network, NetworkEventHandler and Agent
objects. The default main loop callbacks handle normal environment events and network interactions.

// SimpleAgent.C (A.1)
// Create Network, NetworkEventHandler and Agent
Network network;
NetworkEventHandler eventHandler(&network);
Agent agent(&eventHandler, "SimpleAgent");
// Run main loop
agent.addDefaultMainloopMethods();
agent.mainloop(0, 500000);

Frame and Timer Callbacks


Any code within the process which needs to perform operations each frame, or after a given time, or with a
given period must register a main loop callback with the local Agent. The registered callback will then be
executed by the Agent at the appropriate time.
Each main loop callback has an associated callback "levels", which determines the order in which they are
called. The standard levels are:
• AGENT_CBLEVEL_FIRST - before anything else.
• AGENT_CBLEVEL_SENDING - normally used to process sending message queues before other time-
intensive frame activities.
• AGENT_CBLEVEL_NETWORK - process network events, i.e. drop into select.
• AGENT_CBLEVEL_PENDING - process pending event queues, i.e. local updates due to remote or
delayed events.
• AGENT_CBLEVEL_INTERACTION - handle user interaction, e.g. with screen.
• AGENT_CBLEVEL_RENDER - display new view of world state (post interaction and network updates).
• AGENT_CBLEVEL_LAST - after everything else, before the next iteration.
The following code fragment registers a routine to be called every frame.

// SimpleAgent.C (A.2)
agent.addMainloopMethod(AGENT_CBLEVEL_INTERACTION-1,
exampleFrameCB, (void*)0);

where exampleFrameCB is as follows:

MASSIVE-3 Application Programming: Basics (07/06/99, 3:36 ) 1


void exampleFrameCB(AgentMainloopCBLevel cbLevel,
Time &frameStartTime,
void *userData) {
// etc.
}

The following code fragment registers a routine to be called as soon as possible after 5 seconds has elapsed.
The callback occurs only once and the timer is then automatically removed.

// SimpleAgent.C (A.3b)
Time delay(5,0);
agent.addMainloopMethod(AGENT_CBLEVEL_PENDING+1,
oneShotTimerCB, (void*)0,
delay, 0);

The following code fragment registers a routine to be executed every second, which is removed (from within
the callback) after 10 iterations. Note that the callback frequency will never be more than once per frame, even
if the frame time is longer than the requested interval.

// SimpleAgent.C (A.4b)
Time interval(1,0);
agent.addMainloopMethod(AGENT_CBLEVEL_PENDING+1,
repeatingTimerCB, (void*)&agent,
interval, 1);

Environments
An Environment is a shared database, and represents all or a portion of a virtual world. One Agent can create
a new Environment (or many Environments) and other Agents can then join that environment.
When an Agent joins an Environment it get a complete local copy of that Environment and its contents. Any
changes to one copy (replica) of an Environment will be reflected in all other copies of the Environment
(provided that they are legal operations, and subject to delays in network communication).
Simple Data Items
An Environment is a set of trees of individual data items. Each data item is an instance of a subclass of
ItemData, and is associated with the Environment via an instance of class EnvironmentItem. The basic data
item types are:
• GeometryData - a filename or URL indicating a 3D geometry to be rendered (currently this must be in the
DGL format, as generated by dgl_convert from AC3D, VRML 1 or some VRML 2 format files).
• EntityData - a 3D homogeneous matrix transformation, plus a cuboid extent (currently little used). A sub-
tree starting from an Entity typically corresponds to a virtual artefact or "thing". To be visible it must have
at least one child (or descendant) item that is a geometry. Entitys can also be nested, for example to create
articulated sub-components. An Entity’s transformation is composed with those of it ancestors (parent and
parent’s parent, etc.) to determine its final position and effect. Note that a homogeneous 3D transform can
represent any combination of translations, rotations, shears and scales. Refer to a text book on 3D graphics
for details.
• AttributeData - a text name and value (in ASCII, currently), which is used to annotate Entitys (e.g.
identify user embodiments) and communicate other information (e.g. audio session details).
• SwitchData - a note containing a single integer, which causes only that child node to be rendered. This
allows an Entity to change between different Geometrys for its appearance. Note that child nodes are
numbered in the order in which they are added, and count from 0.
The four item types can be combined to construct annotated and potentially articulated complex worlds or
portions of worlds.
This program segment could be added to the first example to create some simple contents within the a new
Environment. Note that the Environment is named, so that other Agents can locate it and join it.

MASSIVE-3 Application Programming: Basics (07/06/99, 3:36 ) 2


// SimpleEnvironment.C (E.1a) – create an Environment
agent.setEnvironmentFactory(new AudioEnvironmentFactory());
EnvironmentID envID;
Environment *env;
env = agent.createEnvironment(envID, "SimpleEnvironment");

// Make a star object (E.1b)


ItemID obj1ID; // Entity ID so we can specify it as a parent
Matrix3D transform; // position/transform to use
transform.setTranslate(0.0, 1.0, -5.0); // y=1, z=-5
obj1ID = env->addNewEntity(NULL, &transform);

env->addNewGeometry(&obj1ID, "star.dgl");
env->addNewAttribute(&obj1ID, "AName", "AValue");

Note that any graphical client program must be able to access the named geometry file in order to draw the
scene correctly.
Item IDs and Updates
Every item has a globally unique item identifier of class ItemID. In order to read, change or delete an item an
Agent must know its ItemID (it must also own the item, which will be considered later). For example, the
following code fragment locates the current value of an item by its ID. Note that the return value will be NULL
if the item is not found. Also, the item’s type must be checked explicitly before any type-specific access should
be performed.

// SimpleEnvironment.C - Find an item’s value from its ID (E.2)


ItemData *itemValue = env->itemGet(obj1ID);

if (itemValue==NULL) {
// error – item is not present
} else if (itemValue->type!=ITEM_ENTITY) {
// error – its type is not what we expect
} else {
// type-specific access
EntityData *entity = (EntityData*)itemValue;
// etc.
}

Note that it is an error to keep references to ItemData object which are part of the Environment; they will be
freed with no warning if the item is updated or deleted. Instead, an application should always keep the item ID,
and loop up the current value afresh each time it is required.
This alternative code fragment locates the EnvironmentItem record which ties the item to the Environment.
This allows the application to access additional internal information (e.g. sequencer values), or to iterate over
the children of the item. Again, note that the return value will be NULL if the item is not present (e.g. has
already been deleted).

// SimpleEnvironment.C - (E.3)
EnvironmentItem *envItem = env->envItemGet(obj1ID);
if (envItem==NULL) {
// error - item is not present
} else if (envItem->getItem()==NULL) {
// error – item has no valid ItemData present
} else {
// access item or EnvironmentItem...
ItemData *itemValue = envItem->getItem();
AgentID owner = envItem->getOwner();
// etc.
}

MASSIVE-3 Application Programming: Basics (07/06/99, 3:36 ) 3


The following code fragment creates an Entity, updates (moves) it, and then deletes it (you would not normally
do this all together!).

// SimpleEnvironment.C - Create, update and delete an Entity(E.4)


ItemID obj2ID;
transform.setTranslate(-5.0, 0.0, 0.0);
obj2ID = env->addNewEntity(NULL, &transform);

transform.setTranslate(5.0, 0.0, 0.0);


env->updateEntity(obj2ID, &transform);

env->itemDeleteTree(obj2ID);

Item Locks and Ownership


Every item is owned by exactly one Agent at any given time. Initially this is the Agent which first creates the
item, although this can change (as described below). Only the Agent which owns the item can update or delete
it. If another Agent attempts to update or delete it their request will be ignored.
By default, items are created with a hard lock. This means that the owner will never normally change. For
example, a user will be unable to pick up or move the item.
Alternatively an item can be created with a soft lock or a control lock. A soft locked item can be owned by any
Agent. For example, a user can pick up the item and do whatever they want to it (even delete it).
A control locked item’s owner does not normally change, however there is a mechanism which allows other
Agents to ask the owning Agent to change the item; this makes use of an UpdateRequestData item which
represents the request. This item is added as a temporary child of the control locked item. A callback will be
needed to detect this request in to respond to it (see sections on callbacks, below).
This code fragment creates two items, one soft locked and one control locked.

// SimpleEnvironment.C - Create SOFT locked Entity (E.5)


ItemID obj3ID;
transform.setTranslate(-5.0, 1.0, -5.0);
obj3ID = env->addNewEntity(NULL, &transform, NULL, NULL, LOCK_SOFT);

// Create CONTROL locked Entity (E.5)


ItemID obj4ID;
transform.setTranslate(5.0, 1.0, -5.0);
obj4ID = env->addNewEntity(NULL, &transform, NULL, NULL, LOCK_CONTROL);

Process Bound Items


Items are also created by default with a flag called "PROCESS_BOUND"; this causes the items to be deleted
when the creating process leaves the Environment or terminates. In some cases (e.g. user embodiments); in
other cases it is not (e.g. objects which have been added permanently to another Environment, such as a new
building).
This process bound flag is an ItemData flag, and can be over-riden when the item is created or updated.
This code fragment creates an item without the process bound flag set (note the use of non-default argument
values).

// SimpleEnvironment.C - Create item not process bound (E.6)


ItemID obj5ID;
transform.setTranslate(-5.0, 3.0, -5.0);
obj5ID = env->addNewEntity(NULL, &transform, NULL, NULL, LOCK_SOFT, 0);

Joining and Iterating an Environment


In the previous section we created an environment and then put some things into it. The following alternative
code fragment joins an existing (named) environment, and then iterates all items in the environment,
irrespective of their position in the item hierarchy.

MASSIVE-3 Application Programming: Basics (07/06/99, 3:36 ) 4


// SimpleEnvironment2.C – (E.7) – create and iterate Environment.
agent.setEnvironmentFactory(new AudioEnvironmentFactory());
Environment *env;
env = agent.joinEnvironment("SimpleEnvironment");
if (env==NULL) {
// error - failed
}

// iterate whole of environment (E.7b)


EnvironmentItemIterator iter;
EnvironmentItem *envItem;
env->iterate(&iter);
while ((envItem = iter.next())!=NULL) {
// do something with envItem
}

The following code fragment uses a recursive procedure to perform a depth-first traversal of the item hierarchy
of an environment.

// SimpleEnvironment2.C - perform depth-first iteration (E.8)


EnvironmentItemIterator iter;
EnvironmentItem *envItem;
env->iterateTopLevel(&iter);
while ((envItem = iter.next())!=NULL) {
recursivelyIterateItem(envItem, 1);
}

where recursivelyIterateItem is:


void recursivelyIterateItem(EnvironmentItem *envItem, int depth)
{
EnvironmentItemChildIterator childIter;
EnvironmentItem *child;
envItem->iterateChildren(&childIter);
while ((child = childIter.next())!=NULL) {
recursivelyIterateItem(child, depth+1);
}
}

Note that the code fragments, when used immediately after joining an Environment (as shown) may show few
or no items present; this is due to the way that Environments are progressively loaded (streamed) into a joining
application. This is slightly different to the delay in showing objects in a graphical user client, which also has
to progressively load geometry files before they can be rendered.
Changes and Callbacks
To detect when items and/or an Environment change a process must either:
• set up an Agent timer (or frame) callback, and periodically re-check the Environment’s contents, as above;
or
• set up an environment callback, which will call a designated function when a significant change occurs.
In most cases the second approach (environment callback) is preferred for reasons of efficiency and simplicity.
An environment callback is established by instantiating an object of class EnvCallback. Destroying this object
removes the callback. When a callback is created you must specify the events for which it is to be invoked.
This is specified in terms of a number of properties of the event. Each event is characterised by the following
properties:
• the item to which it applies,
• the type of the event (e.g. add item, update item, delete item), and
• the type of the item to which it applies (e.g. Entity, Geometry).

MASSIVE-3 Application Programming: Basics (07/06/99, 3:36 ) 5


When creating an EnvCallback the event type(s) and item type(s) are specified directly as bit masks (see
example, below). With the regard to the item to which an event applies, this can be specified more flexibly: an
(existing) filter item is specified, plus another bit mask, the bit of which correspond to generations of
descendants of that item. For example, bit 0 signifies events applied to the filter item itself, bit 1 signifies
events applied to the filter item’s children, bit 2 signifies events applied to its children’s children, and the
special value (~0) (all bits set) specifies any descendent item. If the filter item is unspecified (or a null item
ID) then it is considered to be the environment itself, and its children are considered to be the top-level items
in the Environment.
The following code fragment monitors updates to and deletion of an item. Note the call to freeOnEnvDelete
which causes the EnvCallback to be deleted automatically when the item is deleted (or if the process leaves that
Environment).

// SimpleEnvironment.C – (E.9)
EnvCallback *callback1 =
new EnvCallback(env, itemChangeCB, (void*)0,
EVENT_UPDATE | EVENT_DELETE | EVENT_DELETE_BOUND,
&obj3ID,
ENV_CALLBACK_OBJECT,
ITEM_ANY);
callback1->freeOnEnvDeleted();

where itemChangeCB is:

void itemChangeCB( Environment *env,


EnvCallback *callback,
EventType type,
EnvironmentItem *envItem,
Event *event,
void *userData ) {
// etc.
}

The following code fragment monitors the addition, update and deletion of child items of type UpdateRequest;
these are used (normally with control locked items) to indicate a request to change a locked item (as described
already).

// SimpleEnvironment.C (E.10)
EnvCallback *callback2 =
new EnvCallback(env, itemUpdateRequestCB, (void*)0,
EVENT_ADD_ANY | EVENT_UPDATE |
EVENT_DELETE | EVENT_DELETE_BOUND,
&obj4ID,
ENV_CALLBACK_CHILDREN,
ITEM_UPDATE_REQUEST);

Notifications
As well as creating and manipulating items in the Environment, Agents can also send stateless events to an
Environment. The standard fields of a notification are:
• "type" - a string which identifies what kind of event it is (an empty string is the default – not NULL);
• "qualifier" - an integer which further identifies the kind of event (0 is the default);
• "info" - a string which can contain arbitrary information dependent on the kind of event is (an empty
string is the default – not NULL);
• "agent1" - an AgentID of an Agent named in the event, typically the source or subject;
• "agent2" - an AgentID of another Agent named in the event, typically the destination (if there is one);
• "item1" - an ItemID of an item named in the event, typically the obect of the event;

MASSIVE-3 Application Programming: Basics (07/06/99, 3:36 ) 6


• "item2" - an ItemID of another item named in the event (if there is one);
• "data" - a pointer to an instance of class of Serializable, which can be arbitrary data associated with the
event, according to the event’s kind.
All items have a null default.
The following code fragment is used to send key press notifications.

// SimpleEnvironment.C – (E.11) - An imaginary 'a'


AgentID thisAgentID = env->getOwnerID();
env->notification(new EventNotificationData("/user/keypress", 'a',
"a", thisAgentID,
nullAgent, nullItem,
nullItem, NULL));

A callback can be registered for notification events in the same way as above. If item1 is specified then this is
considered to be the item to which the event applies (for the purposes of callback filtering). In addition, the
EnvCallback can include an EventNotificationData object which acts as a filter to select events: if fields of this
object are non-default values then they must be matched by the event before the callback will be triggered.
Type, qualifier, item1, item2, agent2 and agent2 are checked. The type field is checked as a prefix (i.e. it must
only match the start of the event’s type); this allows the use of hierarchically structured event name spaces for
filtering purposes.
The following code fragment establishes a callback which responds to all key press notification events.

// SimpleEnvironment.C – (E.12)
AgentID nullAgent;
ItemID nullItem;
EventNotificationData *keyFilter =
new EventNotificationData("/user/keypress", 0, "",
nullAgent, nullAgent, nullItem,
nullItem, NULL);
EnvCallback *callback3 =
new EnvCallback(env, keyPressCB, (void*)0,
EVENT_NOTIFICATION);

MASSIVE-3 Application Programming: Basics (07/06/99, 3:36 ) 7

You might also like

pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy