October 18, 2011

Interobject Communication

The behaviors of several objects can be coordinated by having them exchange information using private chat protocols.   One object emits chat using llRegionSay(). Another object has a listen() event which receives messages on the same channel.


The sender would have code like this

integer commChannel = -837465; 
... 
     llRegionSay( commChannel, "start");
...

The receiver would have code like this 

integer commChannel = -837465; 
default { 
  state_entry() {
      llListen( commChannel, "", NULL_KEY, "");
  }
  listen( integer channel, string fromName, 
            key fromKey, string msg ){
    if ( channel == commChannel ) {
      if ( msg == "start" ) {
        llSay(PUBLIC_CHANNEL, "'"+fromName+"' says start");
      } else if ( msg == "stop" ) {
        llSay(PUBLIC_CHANNEL, "'"+fromName+"' says stop");
      }
    }
  }
}
____________________
Communication often consists pairs of messages that comprise a transaction.  For a ping-pong transaction which allows one object to find many other objects, the receiver would have code like this

  listen( integer channel, string fromName, key fromKey, 
                                      string msg ){
    if ( channel == commChannel ) {
      integer match = llListFindList( 
               [ "start", "stop", "ping" ], [msg] );
      if ( match == 0 ) {
        ....
      } else if ( match == 1 ) {
        ....
      } else if ( match == 2 ) {
          llRegionSayTo( fromKey, commChannel, "pong" );
      }
    }
  }


The receiver knows that the pong should go back to the emitter of the ping.  Therefore the pong emitter may use llRegionSayTo() to send the message to a specific object.


The pinger code would include a listen handle that receives the pong message.  To make a list of responder keys it could have code like this:

  list pongerKeyList = [];

  listen( integer channel, string fromName, 
                      key fromKey, string msg ){
    if ( channel == commChannel ) {
      integer match = llListFindList( [ "pong" ], [msg] );
      if ( match == 0 ) {
         pongerKeyList += fromKey;
      }
    }
  }
____________________
The receiver of a message can extract substantial information from the key of the sender using llGetObjectDetails().  

  listen( integer channel, string fromName, 
                   key fromKey, string msg ){
    if ( channel == commChannel ) {
      if ( msg == "pong" ) {
        list a = llGetObjectDetails( fromKey, 
                   [ OBJECT_NAME, OBJECT_OWNER, 
                     OBJECT_POS, OBJECT_ROT ] );
        llSay(PUBLIC_CHANNEL, "pong response: "+llList2CSV(a));
      }
    }
  }
____________________
Messages carry additional information by concatenating values using a special character pattern which does not appear within any values.   The message sender uses llDumpList2String() to build the message.  The receiver used llParseStringKeepNulls() to separate the message into a list of string fields.  These string fields are converted back into values using casts.    The ping sender would have code like this:

  touch_start( integer nTouch ) {
     float jitterPongMax = 4;
     llRegionSay( commChannel, llDumpList2String(
            [ "ping",
jitterPongMax , llGetUnixTime(), 
                       llGetPos() ], "|"));
  }


The ping receiver/pong sender would have code like this:

   listen( integer channel, string fromName, 

                     key fromKey,   string msg ){
    if ( channel == commChannel ) {
      list a = llParseStringKeepNulls( msg, ["|"], [] );
      string verb = llList2String(a,0);
      integer match = llListFindList( 
              [ "start", "stop", "ping" ], [verb] );
     if ( match == 0 ) {
       ...     
     } else if ( match == 1 ) {       ...
      } else if ( match == 2 ) { 
        float jitterMax = (integer)llList2String(a,0); 
        integer sendTime = (integer)llList2String(a,1); 
        vector pingerPos = (vector) llList2String(a,3);           
        llSleep( llFrand(jitterMax ) );

        llRegionSayTo( fromKey, commChannel, "pong" );
        ....      }
    }
  }
____________________
Notes:

  • If your system will be deployed across region boundaries, then use llShout(), which has a range of 100 meters.
  • If the messages pass between one agent's attachments, use llWhisper(), which has a range of 10 meters.
  • In the last example llSleep(llFrand(jitterMax)) prevents all the pongers from responding at the same time.  There are limits to how many messages can be queued by a region.
  • llParseStringKeepNulls() is used because string values may be zero length strings.
  • llListFindList() and subsequent integer comparisons is more efficient that a series of string comparisons.
Revised 6Jun2012
[end]