clockwerk-opensim – Blame information for rev 1

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 vero 1 /*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27  
28 using System;
29 using System.Collections.Generic;
30 using System.Reflection;
31 using log4net;
32 using Mono.Addins;
33 using Nini.Config;
34 using OpenSim;
35 using OpenSim.Framework;
36 using OpenSim.Region.Framework.Interfaces;
37 using OpenSim.Region.Framework.Scenes;
38  
39 namespace OpenSim.ApplicationPlugins.RegionModulesController
40 {
41 public class RegionModulesControllerPlugin : IRegionModulesController,
42 IApplicationPlugin
43 {
44 // Logger
45 private static readonly ILog m_log =
46 LogManager.GetLogger(
47 MethodBase.GetCurrentMethod().DeclaringType);
48  
49 /// <summary>
50 /// Controls whether we load modules from Mono.Addins.
51 /// </summary>
52 /// <remarks>For debug purposes. Defaults to true.</remarks>
53 public bool LoadModulesFromAddins { get; set; }
54  
55 // Config access
56 private OpenSimBase m_openSim;
57  
58 // Our name
59 private string m_name;
60  
61 // Internal lists to collect information about modules present
62 private List<TypeExtensionNode> m_nonSharedModules =
63 new List<TypeExtensionNode>();
64 private List<TypeExtensionNode> m_sharedModules =
65 new List<TypeExtensionNode>();
66  
67 // List of shared module instances, for adding to Scenes
68 private List<ISharedRegionModule> m_sharedInstances =
69 new List<ISharedRegionModule>();
70  
71 public RegionModulesControllerPlugin()
72 {
73 LoadModulesFromAddins = true;
74 }
75  
76 #region IApplicationPlugin implementation
77  
78 public void Initialise (OpenSimBase openSim)
79 {
80 m_openSim = openSim;
81 m_openSim.ApplicationRegistry.RegisterInterface<IRegionModulesController>(this);
82 m_log.DebugFormat("[REGIONMODULES]: Initializing...");
83  
84 if (!LoadModulesFromAddins)
85 return;
86  
87 // Who we are
88 string id = AddinManager.CurrentAddin.Id;
89  
90 // Make friendly name
91 int pos = id.LastIndexOf(".");
92 if (pos == -1)
93 m_name = id;
94 else
95 m_name = id.Substring(pos + 1);
96  
97 // The [Modules] section in the ini file
98 IConfig modulesConfig =
99 m_openSim.ConfigSource.Source.Configs["Modules"];
100 if (modulesConfig == null)
101 modulesConfig = m_openSim.ConfigSource.Source.AddConfig("Modules");
102  
103 Dictionary<RuntimeAddin, IList<int>> loadedModules = new Dictionary<RuntimeAddin, IList<int>>();
104  
105 // Scan modules and load all that aren't disabled
106 foreach (TypeExtensionNode node in AddinManager.GetExtensionNodes("/OpenSim/RegionModules"))
107 AddNode(node, modulesConfig, loadedModules);
108  
109 foreach (KeyValuePair<RuntimeAddin, IList<int>> loadedModuleData in loadedModules)
110 {
111 m_log.InfoFormat(
112 "[REGIONMODULES]: From plugin {0}, (version {1}), loaded {2} modules, {3} shared, {4} non-shared {5} unknown",
113 loadedModuleData.Key.Id,
114 loadedModuleData.Key.Version,
115 loadedModuleData.Value[0] + loadedModuleData.Value[1] + loadedModuleData.Value[2],
116 loadedModuleData.Value[0], loadedModuleData.Value[1], loadedModuleData.Value[2]);
117 }
118  
119 // Load and init the module. We try a constructor with a port
120 // if a port was given, fall back to one without if there is
121 // no port or the more specific constructor fails.
122 // This will be removed, so that any module capable of using a port
123 // must provide a constructor with a port in the future.
124 // For now, we do this so migration is easy.
125 //
126 foreach (TypeExtensionNode node in m_sharedModules)
127 {
128 Object[] ctorArgs = new Object[] { (uint)0 };
129  
130 // Read the config again
131 string moduleString =
132 modulesConfig.GetString("Setup_" + node.Id, String.Empty);
133  
134 // Get the port number, if there is one
135 if (moduleString != String.Empty)
136 {
137 // Get the port number from the string
138 string[] moduleParts = moduleString.Split(new char[] { '/' },
139 2);
140 if (moduleParts.Length > 1)
141 ctorArgs[0] = Convert.ToUInt32(moduleParts[0]);
142 }
143  
144 // Try loading and initilaizing the module, using the
145 // port if appropriate
146 ISharedRegionModule module = null;
147  
148 try
149 {
150 module = (ISharedRegionModule)Activator.CreateInstance(
151 node.Type, ctorArgs);
152 }
153 catch
154 {
155 module = (ISharedRegionModule)Activator.CreateInstance(
156 node.Type);
157 }
158  
159 // OK, we're up and running
160 m_sharedInstances.Add(module);
161 module.Initialise(m_openSim.ConfigSource.Source);
162 }
163 }
164  
165 public void PostInitialise ()
166 {
167 m_log.DebugFormat("[REGIONMODULES]: PostInitializing...");
168  
169 // Immediately run PostInitialise on shared modules
170 foreach (ISharedRegionModule module in m_sharedInstances)
171 {
172 module.PostInitialise();
173 }
174 }
175  
176 #endregion
177  
178 #region IPlugin implementation
179  
180 private void AddNode(
181 TypeExtensionNode node, IConfig modulesConfig, Dictionary<RuntimeAddin, IList<int>> loadedModules)
182 {
183 IList<int> loadedModuleData;
184  
185 if (!loadedModules.ContainsKey(node.Addin))
186 loadedModules.Add(node.Addin, new List<int> { 0, 0, 0 });
187  
188 loadedModuleData = loadedModules[node.Addin];
189  
190 if (node.Type.GetInterface(typeof(ISharedRegionModule).ToString()) != null)
191 {
192 if (CheckModuleEnabled(node, modulesConfig))
193 {
194 m_log.DebugFormat("[REGIONMODULES]: Found shared region module {0}, class {1}", node.Id, node.Type);
195 m_sharedModules.Add(node);
196 loadedModuleData[0]++;
197 }
198 }
199 else if (node.Type.GetInterface(typeof(INonSharedRegionModule).ToString()) != null)
200 {
201 if (CheckModuleEnabled(node, modulesConfig))
202 {
203 m_log.DebugFormat("[REGIONMODULES]: Found non-shared region module {0}, class {1}", node.Id, node.Type);
204 m_nonSharedModules.Add(node);
205 loadedModuleData[1]++;
206 }
207 }
208 else
209 {
210 m_log.WarnFormat("[REGIONMODULES]: Found unknown type of module {0}, class {1}", node.Id, node.Type);
211 loadedModuleData[2]++;
212 }
213 }
214  
215 // We don't do that here
216 //
217 public void Initialise ()
218 {
219 throw new System.NotImplementedException();
220 }
221  
222 #endregion
223  
224 #region IDisposable implementation
225  
226 // Cleanup
227 //
228 public void Dispose ()
229 {
230 // We expect that all regions have been removed already
231 while (m_sharedInstances.Count > 0)
232 {
233 m_sharedInstances[0].Close();
234 m_sharedInstances.RemoveAt(0);
235 }
236  
237 m_sharedModules.Clear();
238 m_nonSharedModules.Clear();
239 }
240  
241 #endregion
242  
243 public string Version
244 {
245 get
246 {
247 return AddinManager.CurrentAddin.Version;
248 }
249 }
250  
251 public string Name
252 {
253 get
254 {
255 return m_name;
256 }
257 }
258  
259 #region Region Module interfacesController implementation
260  
261 /// <summary>
262 /// Check that the given module is no disabled in the [Modules] section of the config files.
263 /// </summary>
264 /// <param name="node"></param>
265 /// <param name="modulesConfig">The config section</param>
266 /// <returns>true if the module is enabled, false if it is disabled</returns>
267 protected bool CheckModuleEnabled(TypeExtensionNode node, IConfig modulesConfig)
268 {
269 // Get the config string
270 string moduleString =
271 modulesConfig.GetString("Setup_" + node.Id, String.Empty);
272  
273 // We have a selector
274 if (moduleString != String.Empty)
275 {
276 // Allow disabling modules even if they don't have
277 // support for it
278 if (moduleString == "disabled")
279 return false;
280  
281 // Split off port, if present
282 string[] moduleParts = moduleString.Split(new char[] { '/' }, 2);
283 // Format is [port/][class]
284 string className = moduleParts[0];
285 if (moduleParts.Length > 1)
286 className = moduleParts[1];
287  
288 // Match the class name if given
289 if (className != String.Empty &&
290 node.Type.ToString() != className)
291 return false;
292 }
293  
294 return true;
295 }
296  
297 // The root of all evil.
298 // This is where we handle adding the modules to scenes when they
299 // load. This means that here we deal with replaceable interfaces,
300 // nonshared modules, etc.
301 //
302 public void AddRegionToModules (Scene scene)
303 {
304 Dictionary<Type, ISharedRegionModule> deferredSharedModules =
305 new Dictionary<Type, ISharedRegionModule>();
306 Dictionary<Type, INonSharedRegionModule> deferredNonSharedModules =
307 new Dictionary<Type, INonSharedRegionModule>();
308  
309 // We need this to see if a module has already been loaded and
310 // has defined a replaceable interface. It's a generic call,
311 // so this can't be used directly. It will be used later
312 Type s = scene.GetType();
313 MethodInfo mi = s.GetMethod("RequestModuleInterface");
314  
315 // This will hold the shared modules we actually load
316 List<ISharedRegionModule> sharedlist =
317 new List<ISharedRegionModule>();
318  
319 // Iterate over the shared modules that have been loaded
320 // Add them to the new Scene
321 foreach (ISharedRegionModule module in m_sharedInstances)
322 {
323 // Here is where we check if a replaceable interface
324 // is defined. If it is, the module is checked against
325 // the interfaces already defined. If the interface is
326 // defined, we simply skip the module. Else, if the module
327 // defines a replaceable interface, we add it to the deferred
328 // list.
329 Type replaceableInterface = module.ReplaceableInterface;
330 if (replaceableInterface != null)
331 {
332 MethodInfo mii = mi.MakeGenericMethod(replaceableInterface);
333  
334 if (mii.Invoke(scene, new object[0]) != null)
335 {
336 m_log.DebugFormat("[REGIONMODULE]: Not loading {0} because another module has registered {1}", module.Name, replaceableInterface.ToString());
337 continue;
338 }
339  
340 deferredSharedModules[replaceableInterface] = module;
341 m_log.DebugFormat("[REGIONMODULE]: Deferred load of {0}", module.Name);
342 continue;
343 }
344  
345 m_log.DebugFormat("[REGIONMODULE]: Adding scene {0} to shared module {1}",
346 scene.RegionInfo.RegionName, module.Name);
347  
348 module.AddRegion(scene);
349 scene.AddRegionModule(module.Name, module);
350  
351 sharedlist.Add(module);
352 }
353  
354 IConfig modulesConfig =
355 m_openSim.ConfigSource.Source.Configs["Modules"];
356  
357 // Scan for, and load, nonshared modules
358 List<INonSharedRegionModule> list = new List<INonSharedRegionModule>();
359 foreach (TypeExtensionNode node in m_nonSharedModules)
360 {
361 Object[] ctorArgs = new Object[] {0};
362  
363 // Read the config
364 string moduleString =
365 modulesConfig.GetString("Setup_" + node.Id, String.Empty);
366  
367 // Get the port number, if there is one
368 if (moduleString != String.Empty)
369 {
370 // Get the port number from the string
371 string[] moduleParts = moduleString.Split(new char[] {'/'},
372 2);
373 if (moduleParts.Length > 1)
374 ctorArgs[0] = Convert.ToUInt32(moduleParts[0]);
375 }
376  
377 // Actually load it
378 INonSharedRegionModule module = null;
379  
380 Type[] ctorParamTypes = new Type[ctorArgs.Length];
381 for (int i = 0; i < ctorParamTypes.Length; i++)
382 ctorParamTypes[i] = ctorArgs[i].GetType();
383  
384 if (node.Type.GetConstructor(ctorParamTypes) != null)
385 module = (INonSharedRegionModule)Activator.CreateInstance(node.Type, ctorArgs);
386 else
387 module = (INonSharedRegionModule)Activator.CreateInstance(node.Type);
388  
389 // Check for replaceable interfaces
390 Type replaceableInterface = module.ReplaceableInterface;
391 if (replaceableInterface != null)
392 {
393 MethodInfo mii = mi.MakeGenericMethod(replaceableInterface);
394  
395 if (mii.Invoke(scene, new object[0]) != null)
396 {
397 m_log.DebugFormat("[REGIONMODULE]: Not loading {0} because another module has registered {1}", module.Name, replaceableInterface.ToString());
398 continue;
399 }
400  
401 deferredNonSharedModules[replaceableInterface] = module;
402 m_log.DebugFormat("[REGIONMODULE]: Deferred load of {0}", module.Name);
403 continue;
404 }
405  
406 m_log.DebugFormat("[REGIONMODULE]: Adding scene {0} to non-shared module {1}",
407 scene.RegionInfo.RegionName, module.Name);
408  
409 // Initialise the module
410 module.Initialise(m_openSim.ConfigSource.Source);
411  
412 list.Add(module);
413 }
414  
415 // Now add the modules that we found to the scene. If a module
416 // wishes to override a replaceable interface, it needs to
417 // register it in Initialise, so that the deferred module
418 // won't load.
419 foreach (INonSharedRegionModule module in list)
420 {
421 module.AddRegion(scene);
422 scene.AddRegionModule(module.Name, module);
423 }
424  
425 // Now all modules without a replaceable base interface are loaded
426 // Replaceable modules have either been skipped, or omitted.
427 // Now scan the deferred modules here
428 foreach (ISharedRegionModule module in deferredSharedModules.Values)
429 {
430 // Determine if the interface has been replaced
431 Type replaceableInterface = module.ReplaceableInterface;
432 MethodInfo mii = mi.MakeGenericMethod(replaceableInterface);
433  
434 if (mii.Invoke(scene, new object[0]) != null)
435 {
436 m_log.DebugFormat("[REGIONMODULE]: Not loading {0} because another module has registered {1}", module.Name, replaceableInterface.ToString());
437 continue;
438 }
439  
440 m_log.DebugFormat("[REGIONMODULE]: Adding scene {0} to shared module {1} (deferred)",
441 scene.RegionInfo.RegionName, module.Name);
442  
443 // Not replaced, load the module
444 module.AddRegion(scene);
445 scene.AddRegionModule(module.Name, module);
446  
447 sharedlist.Add(module);
448 }
449  
450 // Same thing for nonshared modules, load them unless overridden
451 List<INonSharedRegionModule> deferredlist =
452 new List<INonSharedRegionModule>();
453  
454 foreach (INonSharedRegionModule module in deferredNonSharedModules.Values)
455 {
456 // Check interface override
457 Type replaceableInterface = module.ReplaceableInterface;
458 if (replaceableInterface != null)
459 {
460 MethodInfo mii = mi.MakeGenericMethod(replaceableInterface);
461  
462 if (mii.Invoke(scene, new object[0]) != null)
463 {
464 m_log.DebugFormat("[REGIONMODULE]: Not loading {0} because another module has registered {1}", module.Name, replaceableInterface.ToString());
465 continue;
466 }
467 }
468  
469 m_log.DebugFormat("[REGIONMODULE]: Adding scene {0} to non-shared module {1} (deferred)",
470 scene.RegionInfo.RegionName, module.Name);
471  
472 module.Initialise(m_openSim.ConfigSource.Source);
473  
474 list.Add(module);
475 deferredlist.Add(module);
476 }
477  
478 // Finally, load valid deferred modules
479 foreach (INonSharedRegionModule module in deferredlist)
480 {
481 module.AddRegion(scene);
482 scene.AddRegionModule(module.Name, module);
483 }
484  
485 // This is needed for all module types. Modules will register
486 // Interfaces with scene in AddScene, and will also need a means
487 // to access interfaces registered by other modules. Without
488 // this extra method, a module attempting to use another modules's
489 // interface would be successful only depending on load order,
490 // which can't be depended upon, or modules would need to resort
491 // to ugly kludges to attempt to request interfaces when needed
492 // and unneccessary caching logic repeated in all modules.
493 // The extra function stub is just that much cleaner
494 //
495 foreach (ISharedRegionModule module in sharedlist)
496 {
497 module.RegionLoaded(scene);
498 }
499  
500 foreach (INonSharedRegionModule module in list)
501 {
502 module.RegionLoaded(scene);
503 }
504 }
505  
506 public void RemoveRegionFromModules (Scene scene)
507 {
508 foreach (IRegionModuleBase module in scene.RegionModules.Values)
509 {
510 m_log.DebugFormat("[REGIONMODULE]: Removing scene {0} from module {1}",
511 scene.RegionInfo.RegionName, module.Name);
512 module.RemoveRegion(scene);
513 if (module is INonSharedRegionModule)
514 {
515 // as we were the only user, this instance has to die
516 module.Close();
517 }
518 }
519 scene.RegionModules.Clear();
520 }
521  
522 #endregion
523  
524 }
525 }