My use of link messages always follows a pretty well-defined pattern. I use the integer parameter as the command (and for any new script/protocol I generate a random integer in the range -2^-31 to 2^31-1 as the starting command number), and the string and key parameters as arguments to the command. Usually most parameters go into the string parameter in CSV format, and when appropriate I use the key parameter for a special purpose such as additional routing (which script the command is aimed at) or which resident initiated the action that the command is servicing.
For a few applications I have had need to route commands to specific scripts, and had multiple helper scripts to distribute the work load. When demand is high, it is even best to break all the helper scripts into multiple prims to guarantee that you don't run into the limit on maximum queued link messages (currently 64).
For example, if you need to send out 128 commands in a one second period, and each script has a one second delay servicing a command, you are going to lose messages (e.g. the first script picks up a command, services it, 127 messages fly by while it is delayed, 63 of which are dropped because the queue is filled, and one of those dropped is probably one that was aimed at the first script again). If instead you separate those scripts into groups between 8 different prims and send the link message only to the prim with the target script in it, you'll only be sending out 16 messages per second to each prim (and thus each script in that prim), and the scripts can easily keep up without the queue overflowing.
Here is an example of how to do this. This hasn't yet been compiled so it might need some minor syntax fixes, but it should give you the idea. Probably in a real application I would send messages to scripts in a round-robin fashion rather than randomly, but this works for an example script:
//// Main Script
// params not used
integer HELPER_QUERY_LINK = -741215098;
// key param is script name
integer HELPER_REGISTER_LINK = -741215099;
// string param is message
// key param is target helper script
integer EXAMPLE_COMMAND = -741215100;
float SETUP_TIME = 2.0;
string EXAMPLE_MESSAGE = "Hello Prim!";
float EXAMPLE_MESSAGE_DELAY = 0.1;
integer messaging = FALSE;
integer nextTargetHelper = 0;
// Each element has the form primNum:scriptName
list helperScripts = [];
integer nHelperScripts = 0;
queryHelperScripts()
{
llMessageLinked(LINK_SET, HELPER_QUERY_LINK, "", NULL_KEY);
}
registerHelperScript(integer primNum, string scriptName)
{
string entry = ((string)primNum)+","+scriptName;
if (llListFindList(helperScripts, [ entry ]) >= 0)
{
// Already registered
return;
}
helperScripts = (helperScripts=[])+helperScripts+[ entry ];
}
sendCommandToHelper(integer helperNum, integer cmd, string paramStr)
{
string entry = llList2String(helperScripts, helperNum);
integer commaPos = llSubStringIndex(entry, ",");
integer primNum = llGetSubString(entry, 0, commaPos-1);
string scriptName = llGetSubString(entry, commaPos+1, -1);
llMessageLinked(primNum, cmd, paramStr, (key)scriptName);
}
default
{
state_entry()
{
queryHelperScripts();
llSetTimerEvent(SETUP_TIME);
}
timer()
{
if (messaging)
{
// Send message to next helper script
sendCommandToHelper(nextTargetHelper, EXAMPLE_COMMAND, EXAMPLE_MESSAGE);
nextTargetHelper = (nextTargetHelper+1)%nHelperScripts;
} else
{
llOwnerSay("Master script ready. Starting message distribution.");
llSetTimerEvent(EXAMPLE_MESSAGE_DELAY);
}
}
link_message(integer senderPrim, integer cmd, string stringParam, key keyParam)
{
if (cmd == HELPER_REGISTER_LINK)
{
registerHelperScript(senderPrim, (string)keyParam);
}
}
}
//// Helper Script
// params not used
integer HELPER_QUERY_LINK = -741215098;
// key param is script name
integer HELPER_REGISTER_LINK = -741215099;
// string param is message
// key param is target helper script
integer EXAMPLE_COMMAND = -741215100;
float SETUP_TIME = 2.0;
sendHelperRegistration()
{
// Keep all helpers from responding all at once
llSleep(llFrand(0.5*SETUP_TIME));
llMessageLinked(LINK_SET, HELPER_REGISTER_LINK, "", llGetScriptName());
}
default
{
state_entry()
{
// On reset of all scripts, it is unknown whether a helper or the main script
// will be reset first. Therefore, send registration both when this script is
// reset AND when requested by the main script. Redundant registrations are
// detected by the main script anyway.
sendHelperRegistration();
}
link_message(integer senderPrim, integer cmd, string stringParam, key keyParam)
{
if (cmd == HELPER_QUERY_LINK)
{
sendHelperRegistration();
} else if (cmd == EXAMPLE_COMMAND)
{
if ((string)keyParam != llGetScriptName())
{
// This script isn't the target destination
return;
}
llOwnerSay(
"Helper '"+llGetScriptName()+
"' from prim "+llGetLinkNumber()+
" received message: "+stringParam);
}
}
}