Getting Started with Region Modules
From OpenSimulator
Hello World
This brief tutorial is intended to get people started with developing applications with/for opensim using region modules and the opensim API. The module available here writes "HELLO" in prims on every region of your opensim instance, and makes them move every 2 seconds or so. Please note:
- The opensim API is rapidly changing. The code available here works for SVN 7176, which corresponds to opensim 0.6.0. Please use a fresh install of 7176 to run this example. The example assumes the default configuration of opensim out-of-the-box. If you try to run this example on custom configurations, you may get errors and/or warnings. Those errors/warnings can be easily fixed in the code of the example, once you understand how to work with the opensim API. But since I can't predict all combinations of configuration settings out there, the assumption here is a fresh install of opensim SVN 7176. Deviate at your own risk.
To get started:
- Download and install opensim svn 7176, and build it as normal. Run it once, so you create a default region and a default user. Then shut it down.
- Get this zip file: http://www.ics.uci.edu/~lopes/opensim/HelloWorld.zip. Unzip it somewhere.
- Before you go changing the code of the application, see its effect inworld by doing this:
- Grab HelloWorld/bin/Release/HelloWorld.dll and dump it in opensim/bin
- Start opensim as normal, and login to it.
You should see the word HELLO spelled out in prims, and moving every so often.
Are you ready to explore the code now?
Hold on. Before we do that, let me give you the 30-second introduction to Visual C# for Java programmers. Visual C# is similar to Eclipse, if you ever used that. When it builds, it places the resulting dll or exe in whatever folder you tell it to (right-click on the project->properties). By default it places them in bin/Release. When you compile it with debugging, that goes into bin/Debug. In spirit, the dll is equivalent to a jar file. That's what you want to produce and pass around. The PDB file can be ignored, unless you want to debug. The HelloWorld example uses the defaults of VC#, so every time you build it without debugging, the dll is placed under HelloWorld/bin/Release. To build without debugging information, simply right-click on the solution in the Solution Explorer window and choose Build.
Another note: the solution file included in the zip is for VC# 2005. If you have VC# 2008 that's fine too. Just double-click on the solution file, and VC# 2008 will convert the whole thing.
OK, now we're ready. Go ahead and double-click HelloWorld.sln.
The only class in this example is called HelloWorldModule, and it starts like this:
using System; using System.Collections.Generic; using System.Reflection; using log4net; using Nini.Config; using OpenMetaverse; using OpenSim.Framework; using OpenSim.Region.Environment; using OpenSim.Region.Environment.Interfaces; using OpenSim.Region.Environment.Scenes;
namespace HelloWorld { public class HelloWorldModule : IRegionModule {
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
List<Scene> m_scenes = new List<Scene>(); Dictionary<Scene, List<SceneObjectGroup>> scene_prims = new Dictionary<Scene, List<SceneObjectGroup>>(); int counter = 0; bool positive = true;
#region IRegionModule interface public void Initialise(Scene scene, IConfigSource config) { m_log.Info("[HELLOWORLD] Initializing..."); m_scenes.Add(scene); } public void PostInitialise() { m_scenes[0].EventManager.OnFrame += new EventManager.OnFrameDelegate(OnTick); foreach (Scene s in m_scenes) DoHelloWorld(s); } public void Close() { } public string Name { get { return "Hello World Module"; } } public bool IsSharedModule { get { return true; } } #endregion
... }
I know you're eager to get to the part where objects are created and moved around, but I'm afraid that's the trivial part. In order to be able to write those functions effectively, you will need to understand a lot more of the engineering of these modules, and that's the part that's not so trivial, especially if you aren't familiar with VC#. So let me go through the code above very slowly.
- The first part consists of a collection of "using" declarations. If you come from Java, those declarations are equivalent to "import" declarations. And you know what that means in Java: the jar files must be reachable for compilation to succeed. Same here: the dlls for those elements must be reachable for this project to build. Luckily for you, I have included those dlls in the zip file, so you don't need to add them. They are all happily bundled in HelloWorld/bin/Release. Go ahead and look there. However, as you start getting cozy with this code and you start wanting more, more, more, you will need more from the OpenSim API and even from other libraries. When that comes, you will need to add more dlls to your project. That is done through VC#, not on the file system directly! To add libraries ("References"), right-click on "References" in the Solution Explorer, and choose "Add Reference". A small window will pop up with a few tabs. If the library you need is under the System namespace, you want to interact with the .NET tab. If the library you need is from OpenSim you need to interact with the "Browse" tab; then, navigate to your installation of opensim 7176 bin, and pick the dlls you need.
- The second part is the namespace and class declaration. The only noteworthy thing here is the ": IRegionModule" part. What that means is that our class HelloWorldModule implements the IRegionModule interface, which means that we have to implement the 5 methods of that interface, namely: Initialise (yes, it's the British spelling...), PostInitialise, Close, Name, and IsSharedModule. But that's not all. OpenSim treats IRegionModule classes in a very special way. When OpenSim starts, it looks into all the dlls it can reach (under its bin) in search for classes that implement the IRegionModule interface. All of those classes are then acquired and run by OpenSim as if they were part of OpenSim. And this is the key to OpenSim application development: you can add your own code as a plug-in that runs natively on the server. Yeepie!
- The third block above is the declaration of the m_log variable. This variable helps us output messages into a log. I'm just using a common idiom that is used all over OpenSim and that uses log4net. If you come from Java, I'm sure this rings a bell... if you've never seen this, you don't need to understand it, just use it to output messages both onto the console and onto the OpenSim.log file.
- The fourth block of code is the declaration of a few instance variables for our HelloWorld example, and this is where we start getting acquainted with the OpenSim API.
- m_scenes will hold references to all the scenes. "What's a scene?", I hear you asking. A scene is OpenSim's representation of the contents of a region. If your opensim has only one region, there will be only one scene object; if it has more, there will be as many.
- scene_prims is a dictionary that associates scenes with a list of objects of type SceneObjectGroup. In the Java world this would probably be a Hashtable; in the .NET world it's a Dictionary. scene_prims will hold references to the prims that we will instantiate for constructing the world HELLO in each scene. An important note: if you look inside OpenSim's Scene class, you will see that it has a list of entities corresponding to all objects and agents present in the scene. As we place prims in scenes, including these HELLO prims, they will be placed on that list. However, what we are doing here is constructing a set of prims that are special for our application and that we want to track throughout our application. As such, we want to hold references to those, and only those. That's what scene_prims is for.
- The other two variables, counter and positive, are explained later.
- The fifth block is the implementation of the IRegionModule methods. There's nothing much here, and this is how it should be: encapsulate all of your code in your own functions. Let's go through the most important of these methods:
- Initialise (I still can't get over this British spelling): this method is called by OpenSim when it is discovering all the IRegionModule classes in dlls. OpenSim sends us two parameters: a scene object and a configuration object. The configuration object allows us to browse through the OpenSim configuration, if we need that info. The scene object is the very important scene information that we want to hold on to; the scene object is our code's main link to OpenSim, it's how we get to access and modify things in the world and beyond.