MRM: Language Extensions and Libraries

Edited by: SP

Link

MRM now supports language extensions - you can import your own external .NET DLLs. Here's how you do it, with a few best practises thrown in at the end...

As of r9240, MRM now has proper support for language extensions - these come in two key flavours - the first is the ability to import outside .NET DLLs which allow you to load foreign libraries into your code, this could take the form of more advanced math libraries or other useful tools. Those libraries will be loaded with the same security context as your MRM - so when CAS is implemented in MRM for security, you will need to be aware that they will be affected too.

The syntax for importing a library is fairly simple - just place within your code, below the //MRM:C# line, a line which reads like this:

//@DEPENDS:MyLibrary.dll

Dependencies are requirements - if the dependent DLL cannot be found, it will throw a compile error, even if no code utilizes this extension.

Accessing Region Modules

The second form of language extension - the preferable one is to connect with other OpenSim region modules. Internally in OpenSim when a region module wishes to communicate with another module, it is done via a shared interface - one module will define an interface it can be accessed by (eg, IMRMModule)

Communicating with an MRM is done in a very similar fashion - you must create an interface that the script can utilize to connect to your module. This is best illustrated with an example, so printed below is a "Hello World" module, it's structure is pretty simple.

The Module Code

using Nini.Config;
using OpenSim.Region.Framework.Interfaces;
using OpenSim.Region.Framework.Scenes;
 
namespace OpenSim.Region.OptionalModules.Scripting.Minimodule.Test.Extensions
{
    internal interface IMRMExtensionTestModule
    {
        string ReturnHelloWorld();
    }
 
    class MRMExtensionTestModule : IRegionModule, IMRMExtensionTestModule
    {
        #region IMRMExtensionTestModule Members
        // This is the implementation for the above
        // interface and will be availible to the script
        public string ReturnHelloWorld()
        {
            return "Hello World!";
        }
        #endregion
 
        #region IRegionModule Members
        
        public void Initialise(Scene scene, IConfigSource source)
        {
            // Request IMRMModule to access the MRM Host...
            scene.RequestModuleInterface().
                // and register our custom extension.
                RegisterExtension(this);
        }
 
        public void PostInitialise()
        {
            
        }
 
        public void Close()
        {
            
        }
 
        public string Name
        {
            get { return "MRM Test Extension Module"; }
        }
 
        public bool IsSharedModule
        {
            get { return true; }
        }
        #endregion
    }
}

A sample MRM using the above

//MRM:C#
//@DEPENDS:MyModule.dll
using OpenSim.Region.OptionalModules.Scripting.Minimodule;
 
// Change this to your MyModule.dll namespace:
using OpenSim.Region.OptionalModules.Scripting.Minimodule.Test.Extensions;
 
namespace OpenSim
{
    class MiniModule2 : MRMBase
    {
        public override void Start()
        {
            // Get our extension
            IMRMExtensionTestModule testExtension = Host.Extensions.Get();
 
            // Say Hello
            Host.Object.Say(
                testExtension.ReturnHelloWorld()
                );
 
            // or the same thing in one line:
 
            Host.Object.Say(
                Host.Extensions.Get().ReturnHelloWorld()
                );
        }
 
        public override void Stop()
        {
 
        }
    }
}

This code represents a fairly simple example - there are a few considerations to make however when writing MRM interfaces, especially if you desire to get those interfaces into the core OpenSim distribution.

Good practices when writing a module interface

These are based loosely on the rules I am employing in designing the default API.

  • Use MRM types where possible - if you need to get an object as a parameter, utilize IObject rather than pass a UUID or localID. You can convert between them pretty easily (SOPObject takes LocalID as a singular constructor, and you can fetch it off via IObject.LocalID).
  • Do not pass in internal OpenSim classes. This means do not ever return something like SceneObjectPart - this is because OpenSim internal classes change, and when they change your MRM will break. If you need to use one of these classes and MRM doesnt have a matching interface already, make a new interface and contact me for possible inclusion in the standard distribution.
  • Do not require IHost for anything. If you want the user to pass in the host object, ask them to do it - but do not require it. This is because someone may find it useful to utilize your function on a remote object. By using IObject you make your module more useful to programmers.
  • If you need a unique ID for the script instance, require a UUID to passed, then request the user uses the global variable "ID". This is a UUID that corresponds with the internal Inventory Item UUID of the MRM script.
  • Do not place abitrary limits on scripts. If you have to, make them configurable. An example of this is the mandatory sleeps in LSL on functions like llEmail. Assume instead that people writing scripts in MRM know what they are doing.
  • Follow MSDN C# Coding Guidelines. We use the MSDN Class Library coding standard in OpenSim, MRM by extension uses the same guidelines.

 

Happy Extending!

Article tagged: c# | scripting | mrm

We recommend that you discuss this article on Think, but if you really want to you can leave a comment right here as well:

0 comment(s) for “MRM: Language Extensions and Libraries”