Passing data between a script and a region module with modSendCommand()

July 16, 2010
By

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.

13 Responses to Passing data between a script and a region module with modSendCommand()

  1. [...] 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 [...]

  2. rjs on July 18, 2010 at 10:22 pm

    Very nice tut. Seems as though the possibilities are endless.

    Thanks for posting on this Justin.

  3. Skidz Tweak on August 23, 2010 at 2:24 am

    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 :)

  4. justincc on August 24, 2010 at 2:25 am

    @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.

  5. Skidz Tweak on August 25, 2010 at 6:31 am

    @justincc – Thanks for the tips – will check them out

  6. FrankF on July 5, 2011 at 12:30 am

    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

  7. justincc on July 7, 2011 at 10:26 pm

    @FrankF I don’t know why you’re seeing that. I suggest posting the full stack trace on the opensim users mailing list.

  8. FrankF on July 28, 2011 at 3:43 pm

    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.

  9. admin on July 28, 2011 at 7:45 pm

    @FrankF – This can be done within the normal Linden Scripting Language (LSL). I suggest that you Google the various LSL websites.

  10. FrankF on August 4, 2011 at 10:15 pm

    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.

  11. admin on August 5, 2011 at 5:53 am
  12. Greg on October 21, 2011 at 12:06 am

    @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.

Leave a Reply

Your email address will not be published. Required fields are marked *

*

ABOUT ME

Hi, I'm Justin Clark-Casey, an OpenSim core developer and professional software engineer.

On the Linden Lab grid I go by the name of Lulworth Beaumont. On other grids I'm just plain old Justin Clark-Casey.

I'm currently working as a freelance consultant specializing in OpenSimulator and related technologies. If you're interested in learning more then please read the Hire Me page.

I've also written a paper on distributed virtual environments, which examines how virtual worlds could become more like the web, rather than individual silos of users and content.

I often attend the OpenSim Office Hours on a Tuesday on Wright Plaza at OSgrid.org