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.
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.
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.
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;
}
Delete the acceptNetKey method from the source code and header file.
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.
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).
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.
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]);
}
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);
Delete the acceptNetKey method from the source code and header file.
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.