jump to navigation

Reflections on Writing an OpenSim Region Module February 27, 2009

Posted by justincc in opensim, opensim-dev, opensim-modules, opinion, psmb.
trackback

Introduction

opensim-logo-shortA couple of weeks ago I mentioned that I planned to post something about my experiences in writing the Parallel Selves Message Bridge module.  This is that post, albeit a little bit later than planned 🙂  It isn’t a region module writing tutorial – the OpenSim project wiki has one of those if you’d like to read about that.  Nor is it an explanation of the technical guts of how the bridge itself works (for which I recommend reading the source code – it’s actually not very complicated).  Instead, this is a reflection on what I encountered while hooking up a ‘third party’ module to OpenSim (since the PSMB isn’t included in the core OpenSim distribution).  I’m going to describe some of the basic process I went through, and then talk about what I found good, bad and ugly.  In other words, I’m telling you what I think about eating our own dogfood 🙂

The Basic Process

The first thing I did when writing the PSMB was to create a class that implemented OpenSim’s IRegionModule interface.  IRegionModule is very simple – it’s the means by which OpenSim passes you a Scene class that represents each region in the server.  Here’s how the PSMB handles this (with some bits cut out for space reasons).

public void Initialise(Scene scene, IConfigSource source)
{
    IConfig config
        = source.Configs["Parallel Selves Message Bridge"];            

    if (!config.Contains("target_system_name"))
    {
        m_log.Error(
            "[PSMB]: You must specify a target system name!");
            return;
    }

    if (!config.Contains("target_system_uri"))
    {
        m_log.Error(
            "[PSMB]: You must specify a target system uri!");
            return;
    }

    m_targetSystemName = config.GetString("target_system_name");
    m_targetSystemUri = config.GetString("target_system_uri");

    scene.EventManager.OnMakeRootAgent += OnMakeRootAgent;
    scene.EventManager.OnChatFromClient += OnChatFromClient;
    scene.EventManager.OnClientClosed += ClientLoggedOut;
}

Let’s focus on the bottom three lines for a second.  This is where the PSMB module is hooked up to certain events in the scene.  For instance, scene.EventManager.OnMakeRootAgent is fired whenever an avatar enters that region, whether by initial login, region crossing or teleport.  Here’s the OnMakeRootAgent() method that I’ve hooked it up to.

private void OnMakeRootAgent(ScenePresence presence)
{
    lock (m_activeBridges)
    {
        if (m_activeBridges.ContainsKey(
            presence.ControllingClient.AgentId))
        {
            Bridge bridge
                = m_activeBridges
                    [presence.ControllingClient.AgentId];
            bridge.OsClient = presence.ControllingClient;
        }
    }
}

As you can see, the fired event passes on a ScenePresence that you can use to find out more information about that avatar (for instance, its name).  In this case, I need the agentId so that I can make sure that the bridge keeps talking to the right client connection after the user has crossed a region border.

In my module I also wanted to know

  • when an avatar said something, so that I could intercept the commands to connect and disconnect the bridge (so I subscribed to OnChatFromClient)
  • when an avatar logged out, so that I could automatically disconnect the bridge (so I subscribed to OnClientClosed).
  • when an avatar sent an instant message so that I could reroute it to the target system if necessary (so I subscribed to IClientAPI.OnInstantMessage, not shown here).

This is all I needed to do to get all the event notifications that I needed.

The other part of the Initialise() method concerns configuration.  From IConfigSource I can get at any information in OpenSim.ini, so I used this so that people could configure the target system that they wanted for the bridge.

Receiving events from OpenSim is only one part of the equation, I also needed to invoke certain OpenSim services.  For instance, the bridge needs a ‘proxy’ user in the source system for every friend that the user has in the target system.  I did this in the PSMB with the following bit of code.

CachedUserInfo userInfo
    = m_scene.CommsManager
        .UserProfileCacheService.GetUserDetails(userId);

if (null == userInfo)
    m_scene.CommsManager.UserAdminService.AddUser(
        firstName,
        string.Format(
            "{0} ({1}),", lastName, Config.TargetSystemName),
        UUID.Random().ToString(),
        BRIDGE_GRID_EMAIL,
        1000,
        1000,
        userId);

In this case, the Scene class has a CommsManager that provides certain services (user information, inventory manipulation, etc.).  Using the CommsManager I first ask the UserProfileCacheService if a user I’ve been told about by the target system (represented by userId) exists in the source system.  If not, then I use the UserAdminService to add that proxy user.

As well as adding the proxy user, I also need to make the PSMB module send the user a ‘friend request’ so that their friends on the target system can be added to their local friends list.  In this case I needed to use a slightly different looking OpenSim facility

IFriendsModule friendsModule
    = m_scene.RequestModuleInterface<IFriendsModule>();
friendsModule.OfferFriendship(
    userId, OsClient,
    "Initial setup populating offer from the PSMB");

Here I’m actually invoking another region module (the Friends module) rather than a ‘service’.  These region modules are obtained by asking for something that implements the right interface (here IFriendsModule).  Once I have this interface, I can invoke the module to handle the friendship offer.

psmb-diagram1

The Good

As IRegionModule is nice and simple, it was easy to implement the basic module infrastructure.  Getting information from OpenSim was also without trauma – all the right events already existed and were giving me the appropriate information.  It was also easy to get configuration data, as you can see above.  Although I didn’t use them in this module since I didn’t need them, there are also fairly simple ways of adding extra commands that can show in the OpenSim console.

It’s also always possible to get hold of the services and other region modules that one needs to invoke, though it’s much more difficult to work out which ones you actually need (as discussed in further detail below).

Although I didn’t mention this above, it also wasn’t too difficult to copy the OpenSim build infrastructure in order to build the PSMB module indepedently from the core product.  This is a different approach to inserting the module code directly in a copy of the OpenSim source tree and building it there (which is what I suspect some other modules do).  To me, building the module separately is much cleaner, though it was a matter of trial and error to detect which OpenSim DLLs I needed.

The Bad

One particularly bad thing is that OpenSim passes in its Scene object directly, rather than via an interface.  This is handy for getting at all the methods one could need, but it also exposes a lot of code that is either

  • irrelevant to region modules or
  • misleading (for example, there is a Scene.AddSceneObject() which won’t do what you expect – Scene.AddNewSceneObject() is what you want!)

To some extent this could be resolved by passing in the scene with an IScene interface instead (and hence restricting the methods that can be invoked).  But really OpenSim would also need to properly sanitize methods on the objects that one can then retrieve from IScene (including services available from IScene.CommsManager).  Among other things, manipulating objects in the scene is particularly hairy and hard to do at the moment.  I’m quite lucky in that I don’t need to do any of that in the PSMB module.

All of this isn’t helped by the fact that large parts of the OpenSim codebase are currently undocumented.  That isn’t too much of a problem for me but it’s going to make life much more difficult for people who want to implement a module without getting intimate with large chunks of OpenSim core code.

A final thing that I would count as a ‘bad’ right now is that OpenSim code is in a considerable state of flux.  Indeed, after releasing the PSMB module I very quickly had to make another release in order to deal with a change in OpenSim namespaces.  I also know that the IRegionModule interface is due for an overhaul and that all the services mentioned above are probably going to be made into modules instead.

At the moment this kind of thing is inevitable and I really don’t think it could be any other way – OpenSim is an alpha project and isn’t ready to hammer down its interfaces.  But it’s worth bearing in mind that if you write a region module that you want to work over a wide range of OpenSim versions, then right now you’re going to have to be prepared to potentially do some rework when OpenSim versions are released.  This problem is worse if one is tracking OpenSim’s latest and greatest code (SVN trunk).

The Ugly

The PSMB module works by accepting connect and disconnect commands through the chat interface.  Instant Messages are intercepted and routed to and from the target system as appropriate.

However, OpenSim currently provides no way of letting a module signal that they have handled an event in such a way that it shouldn’t be passed on to other modules.  For instance, since connect commands contain passwords I don’t want them to be said out loud by OpenSim’s ordinary chat module.

To stop that happening I had to resort to completely replacing OpenSim’s ordinary chat and IM modules with my own versions.  This is deeply ugly since it makes configuration more difficult, and makes it virtually impossible for the PSMB module to interoperate with other code that also wants to filter certain events.

Conclusion

It’s unfortunate that I’ve made the bad and the ugly sections in combination longer than the good – really there are quite a few nice things about writing region modules that on the whole make them perfectly useable at the moment, as long as one is prepared to spend quite a long time studying both example code and the core OpenSim codebase itself.  The amount of time that one needs to spend is dependent on what one wants to do (doing something with chat is easy, manipulating scene objects is much harder).

I suspect that this will continue to be the case in the short term as there is quite a lot of OpenSim restructuring on the horizon.  Some of that will actually address the things that I’ve complained about (such as passing in Scene directly and the absence of any way to filter event propogation).

One thing that I do find perturbing, however, is that not many people write class or method documentation.  In some cases this is arguably overkill.  But when one wants to expose interfaces to people who aren’t OpenSim core coders then I think that it becomes essential.  My hope is that as the project matures more people will come around to this point of view.

Advertisements

Comments»

1. Shorty Phillips (Shortdog Philbin in V worlds) - February 28, 2009

Justin, is it possible to reconfigure PSMB module to connect, in conjunction with Master Avatar login?
Or any legitimate Avatar login to the standalone.
ie, wife and I, use home standalone hypergrid linked to UC Irvine and OSGrid.
As PSMB is only functional (for the time at least) with standalones, this is not the security problem it would be in gridmode.

2. daleinnis - February 28, 2009

Very good and clear and articulate posting Justin; thanks!

3. Stefan Andersson - February 28, 2009

Justin, thank you for your clear and well-written post. I do think that in addressing the bad and ugly, you have shown the true greatness of OpenSim – we have a long way to go, but thru community, we’re going to get there.

What is extra exciting (to me at least) is the very fact that OpenSim is in flux on almost all levels right now, which I choose to take as an indication of an imminent quantum leap.

Keep up the good work!

4. justincc - March 2, 2009

@Stefan, @daleiness – thanks guys

@Shorty – It should be possible to use the PSMB on a HyperGrid standalone just like a normal standalone.

5. Rob Smart - March 2, 2009

Thanks for the article Justin, it aligns with some of my recent experiences of module creation. An additional section to the Good, Bad and Ugly is the Impossible. The impossible being a road block i hit when trying to neatly seperate out a module i had written. My module has two os* scripting commands so that people can call the module in world, it proved impossible to separate these out from the main code base due to the way the scripting engine uses partial classes to implement scripting functions. So for now with my module im stuck with merging it into the main codebase locally everytime i need to update 😦

I agree with you on class and method documentation, it should become more commonplace, i’ve made a point of including it in the admittedly few patches i’ve submitted. It’s all very well saying “the documentation is the code” but that argument becomes tired very quickly. Knowing the intent of a class or method without having to cross reference several other classes to trace an execution path would be nice.

justincc - March 3, 2009

@Rob – Yes, I know what you mean about the os* methods- the fact that it’s currently impossible to add them in a modular fashion is a big problem. Unfortunately, I think that resolving this will require quite a lot of work and some design deliberation.

6. Stefan Andersson - March 3, 2009

Well, actually, I think it might not be that hard at all; I think we need to separate “the need and cons of extending the ossl language” and “the need and cons of invoking module code from ossl” – the first I think needs real thinking thru, the latter would be simply to add an global invocation function, something like osCallModule(“command”, args ) that would send that command to the modules registered command handler. How exactly perms and severities would be handled is another thing. Maybe the osCallModule sends the calling user to the module command so that it can do some perms checking.

7. Rob Smart - March 3, 2009

@Stefan that sounds like it might be a viable approach to exposing and calling module functions without tearing up the existing script engine implementation. We could also add a discovery method osDescribeModule(“modulename”) that lists off script functions a module provides.

This approach also provides a neater way to override existing methods rather than adding say osHttpRequest to override llHttpRequest behaviour.

The issues with this approach will most likely be how the results of invocations are returned, particularly for methods that are asynchronous.

However It certainly warrants some investigation.

8. Stefan Andersson - March 3, 2009

One of the reasons we should be tentative about modules extending ossl is that it will bring language rot really quick. If we had something like the ‘using’ or ‘import’ clause, we could make if ‘module functions’ and be done with it, but the language itself does not support that. That said, there’s nothing stopping us from introducing structural elements to the language, like callbacks/delegates or ‘module methods’. Also, there’s nothing stopping us from adding methods to IModule to support pretty flexible constructs. DDE, anybody?

9. Rob Smart - March 3, 2009

I’ve just discovered the ICommander interface and Commander module that “Generates a runtime C# class which can be compiled and inserted via reflection to enable modules to register new script commands”

Going to give this a whirl and see if it works for my dynamic module, everybody i’ve asked seems unaware of this existing functionality. The only thing i can find that uses it is the TerrainModule. If i get it working I’ll write up a short tutorial explaining its use.

10. Rob Smart - March 4, 2009

on initial inspection and attempts at using this it looks like the intention was for ICommander to be a way of both adding console commands from a module and adding scripting commands. However only the former seems to have been implemented. 😦

11. Jeff Barr’s Blog » Links for Friday, March 6, 2009 - March 7, 2009

[…] JustinCC: Reflections on Writing an OpenSim Region Module – “I’m going to describe some of the basic process I went through, and then talk about what I found good, bad and ugly. In other words, I’m telling you what I think about eating our own dogfood.“ […]

12. justincc - July 16, 2010

Sadly, PSMB is currently non-operational following various changes in OpenSim. I might bring it back at some stage but no promises! Of course, patches to bring it back up to scratch again would be welcome.


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: