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!
We recommend that you discuss this article on Think, but if you really want to you can leave a comment right here as well: