Converting User Modules from Ygdrasil 0.1.X to 0.4


Overview

The facility previously handled by ygNetKeys is now handled by ygNetMessages. The ygNetMessages class is very similiar to ygNetKeys except that keys can now be compound data types. By making a direct correlation between these compound data types and the messages given to each node, the ygNetMessages class can handle the parsing of messages. After parsing out the data and placing it into the compound data type for network delivery, the message() function is called. At this point, the node member variables already have the necessary values and the message function only needs to intiate any processing related to those messages. This new functionality of message() is similiar to that of acceptNetKey(). In fact, acceptNetKey() is no longer necessary because message() is called on the client node after the new compound data type has been received.

All messages that are registered with ygNetMessages will store the default value and are included in graph printouts and saved to file. As such, any messages that would be necessary to establish the node state when reloading the scene should be registered with ygNetMessages whether the node actually transmits data via network keys or not.

Minimal Procedure

Nodes written for previous versions of Ygdrasil can be made to work with version 0.4 by following a few simple steps. However, the full procedure should be followed in order to generate nodes that take advantage of new features such as saving to file, graph message viewing, and event message argument access.

Step 1

Run the updateClasses program located in the tools directory on both your source code and header file. This shell script will change all instances of ygNetKeys.h, addNetKey, netKeyChanged, dontBroadcastNetKey, unreliableKey to the new function naming scheme.

Step 2

Move conditional code for each key from acceptNetKey() into the message() function. Change the name of any key names that are the same as message names. Make a single conditional that traps all remaining keys to prevent generating errors on client nodes:

void userNode::message(const ygMessage& msg)
    {
    //load a filename
    if (msg == "file")
        {
        filename_ = msg.argString(0);
        loadFile(filename_);
        netMessageChanged("file_key");
        }
    //process changed key
    else if (msg == "file_key")
        {
        loadFile(filename_);
        }
    //trap keys that dont require processing
    else if (msg == "color_key" || msg == "alpha_key" || msg == "size_key")
        {
        return;
        }

Step 3

Delete the acceptNetKey method from the source code and header file.

Full Procedure

Step 1

Run the updateClasses program located in the tools directory on both your source code and header file. This shell script will change all instances of ygNetKeys.h, addNetKey, netKeyChanged, dontBroadcastNetKey, unreliableKey to the new function naming scheme.

Step 2

Identify which messages are needed to define the node state. For example, the ygObject node takes file(string,[cache]) but also allows a cache([bool]) message. I decided that the cache information should move with the filename and as such only the file message was registered with the cache boolean included. The cache message is left to be parsed by message() as it was previously. Also, some nodes, such as ygSwitch, take multiple messages to affect the same state change. In the case of ygSwitch, the toggle, on, and off messages all affect the same state variable. The boolean that holds the state was registered with the off message. The remaining messages are parsed in the usual manner by message() and call netMessageChanged() when necessary. When possible, boolean data is represented as only the message name (i.e. off, wall, etc.). Therefore, off was chosen to represent the node state so that the printout/save will look like ygSwitch() or ygSwitch(off).

Step 3

Register messages with ygNetMessages by making calls to addNetMessage. Most node state variables will already have an associated member variable allocated (i.e. p_->netOn for ygSwitch). Initialize each member variable to the default value before registering its associated message. Make one call to addNetMessage for each message data type. For example, the ygEnvironment node has the message fog:

addNetMessage("fog",&p_->fogType,YG_NET_INT);
addNetMessage("fog", &p_->fogColor, YG_NET_VEC3);
addNetMessage("fog", &p_->fogOnset, YG_NET_FLOAT);
addNetMessage("fog", &p_->fogOpaque, YG_NET_FLOAT);


To facilitate parsing of strings associated with integers, make a call to addNetMessageMap for each integer/string pair:

addNetMessageMap("fog","off",PFFOG_OFF);
addNetMessageMap("fog","lin",PFFOG_PIX_LIN);
addNetMessageMap("fog","linear",PFFOG_PIX_LIN);
addNetMessageMap("fog","exp",PFFOG_PIX_EXP);
addNetMessageMap("fog","exp2",PFFOG_PIX_EXP2);


To facilitate creating messages that switch a boolean state to a non-default value (i.e. cache, wall, fly, etc.) the YG_NET_BOOL_NEGATE data type has been added. The negate of the default value will be used if no argument is given when the message is sent:

p_->floor = false;
p_->wall = false;
addNetMessage("floor", &p_->floor, YG_NET_BOOL_NEGATE);
addNetMessage("wall", &p_->wall, YG_NET_BOOL_NEGATE);


Only those messages registered by nodes that are to be created on networked clients will be sent to the repeater. The function dontSendNetMessage can be used to prevent the automatic delivery of message related data to client nodes. As with the previous version, the netMessageChanged method can be used to deliver message contents at a later time.

Step 4

Re-write the message() method for the node keeping in mind that your message related variables have already been parsed into their respective member variables. Messages that do not have the required number of arguments will have their values filled in with the default value. In general, error messages are no longer generated when the wrong number of arguments are given. If no processing is necessary for the message, simply add a return statement so that ygNode can still generate an error if the message is not recognized by any subclass. The following example is from ygObject:

void ygObject::message(const ygMessage& msg)
    {
    //load the given filename with optional caching
    if (msg == "file")
        {
        loadFile(filename_);
        }
    //set the default object caching mode
    else if (msg == "cache")
        {
        if (msg.numArgs() > 0)
            cache = msg.boolArg(0);
        }

In situations where it is impractical to unify the messages with the key data (i.e. ygTransform), the message() method should include a conditional based on the hidden key data name. The dontSaveNetMessage function indicates that the message/key should not be included in the graph/save printout. Be sure to update the visible message parameters when the hidden variables are updated on the client (i.e. position and orientation should mirror COORD_NETKEY). The following example is from the ygTransform node:

addNetMessage("position",&p_->netCoord.xyz,YG_NET_VEC3);
dontSendNetMessage("position");
addNetMessage("orientation",&p_->ori,YG_NET_VEC3);
dontSendNetMessage("orientation");
//create a simple unreliable coordinate network key
addNetMessage(COORD_NETKEY, &p_->netCoord, YG_NET_COORD);
dontSaveNetMessage(COORD_NETKEY);
unreliableMessage(COORD_NETKEY);

void ygTransform::message(const ygMessage& msg)
    ...
    //set the key update interval for remote users
    else if (msg == "updateInterval")
        {
        return;
        }
    else if (msg == COORD_NETKEY)
        {
        setPosition(p_->netCoord.xyz);
        setOrientation(p_->netCoord.hpr[1],p_->netCoord.hpr[2],p_->netCoord.hpr[0]);
        }

Step 5

The method resetNetMessage facilitates the reseting of message related variables to their default values. If the value has changed then the new value will be sent to any client nodes automatically. This is only relevant for nodes that are to be created on client sites. The following example is from ygGeometry:

void ygGeometry::reset(void)
    {
    //set floor to false
    resetNetMessage("floor");
    setFloor(floor);
    //set wall to false
    resetNetMessage("wall");
    setWall(wall);

Step 6

Delete the acceptNetKey method from the source code and header file.

Notes

Two other data types have been added that may be useful. The YG_NET_VECTOR data type is a vector<float> template instance. An example of a node that uses this data type is the value node. Also, the YG_NET_STRING_ADD and YG_NET_STRING_DELETE types are useful for situations where a list of names is maintained in a string. An example of a node that uses this data type is the visibility node. Finally, future versions of the code will likely include a YG_NET_FILE and YG_NET_NODE data type to aid in developing a GUI interface.