jump to navigation

Reflections on Writing an OpenSim Region Module February 27, 2009

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

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.

The Parallel Selves Message Bridge February 4, 2009

Posted by justincc in opensim, opensim-applications, opensim-modules, psmb, secondlife.
25 comments

Introduction

Late on Monday evening (GMT) I published my first external OpenSimulator region module (i.e. one that is not bundled as a core OpenSim component).  This module is the Parallel Selves Message Bridge, and it’s available in binary and BSD licensed source code form at the OpenSimulator forgeShenlei has already written about it from a user’s point of view – this post is to give some more background and technical information as to exactly what it is.  I’ll also write again soon about my experiences in creating the module itself and where I think OpenSim’s region module system needs to improve.

The Basics

The Parallel Selves Message Bridge (PSMB) can be used in a standalone OpenSimulator instance to exchange instant messages with people in another Second Life compatible target system.  The target system can be another OpenSim standalone, an OpenSim grid or even the Linden Labs Second Life grid.  The bridge works by relaying messages through an avatar account that you already have on the target system (hence the name Parallel Selves).

For instance, suppose that I have an avatar called Fenn Meredith on the Linden Labs grid.  Fenn has a very good friend called Lulworth Beaumont on that grid whom she would like to keep Instant Messaging throughout the day.  However, Fenn also wants to do some building work on a local standalone OpenSim instance and doesn’t have a powerful enough machine to keep two Second Life clients open at once.

How can Fenn keep chatting to Lulworth?  One way is to use a text only client to remain connected to the Linden Labs grid, such as Katherine Berry’s AjaxLife or Linden Lab’s own forthcoming SLim client.  But in this case Fenn really wants to be immersed in the building work that she is doing – he doesn’t want to have to flip between applications every time a new IM comes in.

This is where the Message Bridge comes in.  Once the module is installed (which involves copying a dll to OpenSim’s bin/ directory and making a few configuration changes), Fenn logs in to her local OpenSim.  She then types

/bridge co mySLPassword

into the client’s chat area (this chat is intercepted by the PSMB module – it doesn’t get spoken in world).  The module uses the password to login the existing Fenn Meredith account on the Linden Labs grid using libsecondlife.

psmblogin

Once that avatar is logged in, the friends list from the Linden Labs grid is imported into the local OpenSim instance, creating an entry for Lulworth Beaumont in the client’s Contacts window.  Fenn can then work in her OpenSim standalone but still send and receive instant messages from Lulworth using the normal Second Life client IM interface.

psmb-from-os

psmb-from-sl

Limitations

We’ve had this module running internally with Black Dress Technology for a few weeks now and it has been working pretty well.  However, it does have quite a few limitations, some minor that could be overcome with a bit more coding, some much more major.

Among the minor issues is the requirement that both the target the source system avatars have the same name.  The module also only allows one target system to be specified rather than multiple.  Problems like these can be overcome with a little bit more coding.

The more major issues stem from the fact that the module requires you to connect parallel avatars in order to relay Instant Messages.  This is a kludge – a much better way to transfer Instant Messages would be through a proper gateway that didn’t require a user account on each individual system.  As far as I recall the Linden Lab sponsored Architecture Working Group was looking into this, though it could be some time before we see any results.

The parallel avatar requirement makes it difficult to get this approach to work from an OpenSim grid (rather than from just an OpenSim standalone).  Quite a lot of extra coding could get it to work from a single region server on a grid.  A lot, lot more coding again could probably get it to work for an entire grid.  However, there are major trust concerns in getting this to work on a grid – region servers either end up with your target avatar password or have access to the connection you establish.  On a completely private grid this might be okay (presuming that you trust the grid operators completely or you run the grid yourself).  On a commercial closed grid or an open grid where third parties are operating region servers the trust issue is probably insurmountable.

The future

The PSMB module has proven useful already, and with a bit more work it could become useful to more people.  Even where it isn’t useful, there’s always a chance that it might add a tiny bit more impetus towards the eventual goal of a proper IM exchange system between virtual environments.

In the mean time, patches and feedback on the PSMB project page are most welcome.  I would consider the code to be at an alpha level right, so any information about bugs would be appreciated.

I can’t guarantee how much additional work I’ll be able to put in for features unless there’s something that I really need, but I always find that constructive user feedback and requests provide a great motivation for doing additional work.