opensim-development – Blame information for rev 1

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