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