clockwerk-opensim – Blame information for rev 1
?pathlinks?
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 | } |