Hi folks. As many of you may know, one of the most interesting things about OpenSimulator is the ability to extend it using region modules. Region modules plug in to the server and allow you to do pretty much anything with the world, such as sending text chat between different grids, controlling objects, moving avatars, implementing cellular automata and in the case of Realxtend, even adding meshes (for use with the Realxtend client). In fact, a large percentage of ‘core’ OpenSim functionality is implemented using region modules.
However, up until fairly recently it hasn’t been easy to communicate with these modules from in-world LSL/OSSL scripts. Implementing new script functions to call module methods – osDoSomethingCool(), for example – is very awkward because it requires direct patches to OpenSim code. OpenSim’s current script compilation mechanism doesn’t allow these to be set up from within a region module.
In principle, one could also call region module functions from in-world C# scripts. But these also require direct patching of OpenSim, this time to include the right DLL references from C# script assemblies.
In OpenSim 0.6.9, however, a new script function called modSendCommand() was introduced. This allows an LSL/OSSL script to post some arbitrary data to a ‘script command’ event. A region module can listen for this event and in turn send a reply. The script receives this reply via the in-world link_message event. Let’s go through an example.
modSendCommand() example
Enabling the command
The first thing is to enable modSendCommand() in OpenSim.ini. Make sure the line
AllowMODFunctions = true
is set to true and uncommented.
The Region Module
Secondly, we need a region module to listen for the data sent by modSendCommand() and send a reply.
using System;
using Nini.Config;
using OpenMetaverse;
using OpenSim.Region.Framework.Interfaces;
using OpenSim.Region.Framework.Scenes;
namespace ModSendCommandExample
{
public class MyRegionModule : IRegionModule
{
Scene m_scene;
IScriptModuleComms m_commsMod;
public string Name { get { return "MyRegionModule"; } }
public bool IsSharedModule { get { return false; } }
public void Close() {}
public void Initialise(Scene scene, IConfigSource source)
{
m_scene = scene;
}
public void PostInitialise()
{
m_commsMod
= m_scene.RequestModuleInterface<IScriptModuleComms>();
m_commsMod.OnScriptCommand += ProcessScriptCommand;
}
void ProcessScriptCommand(
UUID scriptId,
string reqId, string module, string input, string k)
{
if ("MYMOD" != module)
return;
string[] tokens
= input.Split(
new char[] { '|' }, StringSplitOptions.None);
string command = tokens[0];
switch (command)
{
case "Greet":
string name = tokens[1];
m_commsMod.DispatchReply(scriptId, 1, "Hello " + name, "");
break;
}
}
}
}
The module subscribes to the OnScriptCommand IScriptModuleComms event in PostInitialise(). When a script command event occurs, it checks that the module identifier is correct (MYMOD) and then splits the command into components delimited by a bar (|). This isn’t essential – it’s just one way of invoking arbitrary commands.
In this case, the command name is the first component. If it’s “Greet”, then a reply is sent back using the supplied name argument which is the second component.
The In-world Script
Here’s the in-world script that makes the call and receives the reply.
default
{
touch_start(integer total_number)
{
modSendCommand("MYMOD", "Greet|World", "");
}
link_message(integer sender_num, integer num, string message, key id)
{
if (sender_num == -1)
{
llSay(0, message);
}
}
}
As you can see, a touch triggers the command to the module. The script receives back the link_message event and says the data in-world.
You can download compilable region module source and script code for this example from here.
Conclusion
modSendCommand() isn’t a perfect mechanism by any means – I still think it would be much nicer to have explictly named script commands. Packing all the data into a single string seems pretty crazy considering that both script and region module are running in the same process. Nonetheless, I would say that being able to implement script command processing purely within the region module is a major plus. This way, the module can simply be dropped in a vanilla OpenSim’s bin directory with no extra patches required.
[...] This post was mentioned on Twitter by devi, Justin Clark-Casey. Justin Clark-Casey said: Passing data between a script and a region module with modSendCommand() – http://bit.ly/cTsPzA – Finally found time to write! #opensim [...]
Very nice tut. Seems as though the possibilities are endless.
Thanks for posting on this Justin.
Question… I am thinking of making a texture organizer.. I have done this in scripts, and find it slow in opensim. Would this be significantly faster? Could I use this to access prim inventory data, and manipulate prims in the link set? If so, whats the name of those objects I would use? I know I could just send linked messages to scripts, but would like to keep scripts to a min.
Also, if I ran two, in world organizer, would they share the same instance? I could think of some really fun uses for something like this
@Skidz – If you’re running this on a custom grid, have you tried dropping the ScriptDelayFactor? This will reduce all the standard delays on scripting functions.
In principle, you could do anything via a region module at the backend. The main problem is that the internals of OpenSim are currently extremely messy and poorly documented – it would take a lot of patience to find out how to do certain things. The best way to find out is generally by looking at existing region module code, or by looking at the automated test code in OpenSim.
@justincc – Thanks for the tips – will check them out
good example, it works well
but when I use the function osSetDynamicTextureData instead of llSay
I get an error like :
Allocated element is not free.
at HashGeneration.Set(Int32 bucketIndex, TKey, TValue value, Int64 size)
ANY IDEA????
THANKS FOR YOUR HELP
@FrankF I don’t know why you’re seeing that. I suggest posting the full stack trace on the opensim users mailing list.
I’ve got a question:
I want to add some prims when I touch the prim that has the script,
the trouble is what I dont know how add those prims with a script by default,
so that next time I touch one of the new prims it will add new ones.
Thanks for your help.
@FrankF – This can be done within the normal Linden Scripting Language (LSL). I suggest that you Google the various LSL websites.
Thanks for answering,
But I want to do it from my source code (HelloWorld.cs)
Add script to the new prim from the source code.
See http://lists.berlios.de/pipermail/opensim-users/2011-August/007709.html and earlier on in the list.
Thanks a lot,
this can help too:
http://forge.opensimulator.org/gf/project/extsim/scmsvn/?action=browse&path=%2Ftrunk%2FExtSimPlugin%2FModules%2FExtSim.cs&view=markup
@frank f
regarding your issue with the dynamic text- make sure you change your standalonecommon.ini and change your Modules section for asset caching to FlotsamAssetCache, instead of using CenomeMemoryAsset Cache.