123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516 |
- /*
- * Copyright (c) Contributors, http://opensimulator.org/
- * See CONTRIBUTORS.TXT for a full list of copyright holders.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * * Neither the name of the OpenSimulator Project nor the
- * names of its contributors may be used to endorse or promote products
- * derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
- using System;
- using System.Collections.Generic;
- using System.Reflection;
- using log4net;
- using Mono.Addins;
- using Nini.Config;
- using OpenSim;
- using OpenSim.Framework;
- using OpenSim.Region.Framework.Interfaces;
- using OpenSim.Region.Framework.Scenes;
- namespace OpenSim.ApplicationPlugins.RegionModulesController
- {
- [Extension(Path = "/OpenSim/Startup", Id = "LoadRegions", NodeName = "Plugin")]
- public class RegionModulesControllerPlugin : IRegionModulesController,
- IApplicationPlugin
- {
- // Logger
- private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
- /// <summary>
- /// Controls whether we load modules from Mono.Addins.
- /// </summary>
- /// <remarks>For debug purposes. Defaults to true.</remarks>
- public bool LoadModulesFromAddins { get; set; }
- // Config access
- private OpenSimBase m_openSim;
- // Our name
- private string m_name;
- // Internal lists to collect information about modules present
- private List<TypeExtensionNode> m_nonSharedModules = new List<TypeExtensionNode>();
- private List<TypeExtensionNode> m_sharedModules = new List<TypeExtensionNode>();
- // List of shared module instances, for adding to Scenes
- private List<ISharedRegionModule> m_sharedInstances = new List<ISharedRegionModule>();
- public RegionModulesControllerPlugin()
- {
- LoadModulesFromAddins = true;
- }
- #region IApplicationPlugin implementation
- public void Initialise (OpenSimBase openSim)
- {
- m_openSim = openSim;
- m_openSim.ApplicationRegistry.RegisterInterface<IRegionModulesController>(this);
- m_log.DebugFormat("[REGIONMODULES]: Initializing...");
- if (!LoadModulesFromAddins)
- return;
- // Who we are
- string id = AddinManager.CurrentAddin.Id;
- // Make friendly name
- int pos = id.LastIndexOf(".");
- if (pos == -1)
- m_name = id;
- else
- m_name = id.Substring(pos + 1);
- // The [Modules] section in the ini file
- IConfig modulesConfig = m_openSim.ConfigSource.Source.Configs["Modules"];
- if (modulesConfig == null)
- modulesConfig = m_openSim.ConfigSource.Source.AddConfig("Modules");
- Dictionary<RuntimeAddin, IList<int>> loadedModules = new Dictionary<RuntimeAddin, IList<int>>();
- // Scan modules and load all that aren't disabled
- foreach (TypeExtensionNode node in AddinManager.GetExtensionNodes("/OpenSim/RegionModules"))
- AddNode(node, modulesConfig, loadedModules);
- foreach (KeyValuePair<RuntimeAddin, IList<int>> loadedModuleData in loadedModules)
- {
- m_log.InfoFormat(
- "[REGIONMODULES]: From plugin {0}, (version {1}), loaded {2} modules, {3} shared, {4} non-shared {5} unknown",
- loadedModuleData.Key.Id,
- loadedModuleData.Key.Version,
- loadedModuleData.Value[0] + loadedModuleData.Value[1] + loadedModuleData.Value[2],
- loadedModuleData.Value[0], loadedModuleData.Value[1], loadedModuleData.Value[2]);
- }
- // Load and init the module. We try a constructor with a port
- // if a port was given, fall back to one without if there is
- // no port or the more specific constructor fails.
- // This will be removed, so that any module capable of using a port
- // must provide a constructor with a port in the future.
- // For now, we do this so migration is easy.
- //
- foreach (TypeExtensionNode node in m_sharedModules)
- {
- object[] ctorArgs = new object[] { (uint)0 };
- // Read the config again
- string moduleString = modulesConfig.GetString("Setup_" + node.Id, string.Empty);
- // Test to see if we want this module
- if (moduleString == "disabled")
- continue;
- // Get the port number, if there is one
- if (moduleString != string.Empty)
- {
- // Get the port number from the string
- string[] moduleParts = moduleString.Split(new char[] { '/' }, 2);
- if (moduleParts.Length > 1)
- ctorArgs[0] = Convert.ToUInt32(moduleParts[0]);
- }
- // Try loading and initilaizing the module, using the
- // port if appropriate
- ISharedRegionModule module = null;
- try
- {
- module = (ISharedRegionModule)Activator.CreateInstance(node.Type, ctorArgs);
- }
- catch
- {
- module = (ISharedRegionModule)Activator.CreateInstance(node.Type);
- }
- // OK, we're up and running
- m_sharedInstances.Add(module);
- module.Initialise(m_openSim.ConfigSource.Source);
- }
- }
- public void PostInitialise ()
- {
- m_log.DebugFormat("[REGIONMODULES]: PostInitializing...");
- // Immediately run PostInitialise on shared modules
- foreach (ISharedRegionModule module in m_sharedInstances)
- {
- module.PostInitialise();
- }
- }
- #endregion
- #region IPlugin implementation
- private void AddNode(TypeExtensionNode node, IConfig modulesConfig, Dictionary<RuntimeAddin, IList<int>> loadedModules)
- {
- IList<int> loadedModuleData;
- if (!loadedModules.ContainsKey(node.Addin))
- loadedModules.Add(node.Addin, new List<int> { 0, 0, 0 });
- loadedModuleData = loadedModules[node.Addin];
- if (node.Type.GetInterface(typeof(ISharedRegionModule).ToString()) != null)
- {
- if (CheckModuleEnabled(node, modulesConfig))
- {
- m_log.DebugFormat("[REGIONMODULES]: Found shared region module {0}, class {1}", node.Id, node.Type);
- m_sharedModules.Add(node);
- loadedModuleData[0]++;
- }
- }
- else if (node.Type.GetInterface(typeof(INonSharedRegionModule).ToString()) != null)
- {
- if (CheckModuleEnabled(node, modulesConfig))
- {
- m_log.DebugFormat("[REGIONMODULES]: Found non-shared region module {0}, class {1}", node.Id, node.Type);
- m_nonSharedModules.Add(node);
- loadedModuleData[1]++;
- }
- }
- else
- {
- m_log.WarnFormat("[REGIONMODULES]: Found unknown type of module {0}, class {1}", node.Id, node.Type);
- loadedModuleData[2]++;
- }
- }
- // We don't do that here
- //
- public void Initialise ()
- {
- throw new System.NotImplementedException();
- }
- #endregion
- #region IDisposable implementation
- // Cleanup
- //
- public void Dispose ()
- {
- // We expect that all regions have been removed already
- while (m_sharedInstances.Count > 0)
- {
- m_sharedInstances[0].Close();
- m_sharedInstances.RemoveAt(0);
- }
- m_sharedModules.Clear();
- m_nonSharedModules.Clear();
- }
- #endregion
- public string Version
- {
- get
- {
- return AddinManager.CurrentAddin.Version;
- }
- }
- public string Name
- {
- get
- {
- return m_name;
- }
- }
- #region Region Module interfacesController implementation
- /// <summary>
- /// Check that the given module is no disabled in the [Modules] section of the config files.
- /// </summary>
- /// <param name="node"></param>
- /// <param name="modulesConfig">The config section</param>
- /// <returns>true if the module is enabled, false if it is disabled</returns>
- protected bool CheckModuleEnabled(TypeExtensionNode node, IConfig modulesConfig)
- {
- // Get the config string
- string moduleString = modulesConfig.GetString("Setup_" + node.Id, string.Empty);
- // We have a selector
- if (!string.IsNullOrEmpty(moduleString))
- {
- // Allow disabling modules even if they don't have
- // support for it
- if (moduleString == "disabled")
- return false;
- // Split off port, if present
- string[] moduleParts = moduleString.Split(new char[] { '/' }, 2);
- // Format is [port/][class]
- string className = moduleParts[0];
- if (moduleParts.Length > 1)
- className = moduleParts[1];
- // Match the class name if given
- if (className != String.Empty &&
- node.Type.ToString() != className)
- return false;
- }
- return true;
- }
- // The root of all evil.
- // This is where we handle adding the modules to scenes when they
- // load. This means that here we deal with replaceable interfaces,
- // nonshared modules, etc.
- //
- public void AddRegionToModules (Scene scene)
- {
- Dictionary<Type, ISharedRegionModule> deferredSharedModules = new Dictionary<Type, ISharedRegionModule>();
- Dictionary<Type, INonSharedRegionModule> deferredNonSharedModules = new Dictionary<Type, INonSharedRegionModule>();
- // We need this to see if a module has already been loaded and
- // has defined a replaceable interface. It's a generic call,
- // so this can't be used directly. It will be used later
- Type s = scene.GetType();
- MethodInfo mi = s.GetMethod("RequestModuleInterface");
- // This will hold the shared modules we actually load
- List<ISharedRegionModule> sharedlist = new List<ISharedRegionModule>();
- // Iterate over the shared modules that have been loaded
- // Add them to the new Scene
- foreach (ISharedRegionModule module in m_sharedInstances)
- {
- // Here is where we check if a replaceable interface
- // is defined. If it is, the module is checked against
- // the interfaces already defined. If the interface is
- // defined, we simply skip the module. Else, if the module
- // defines a replaceable interface, we add it to the deferred
- // list.
- Type replaceableInterface = module.ReplaceableInterface;
- if (replaceableInterface != null)
- {
- MethodInfo mii = mi.MakeGenericMethod(replaceableInterface);
- if (mii.Invoke(scene, new object[0]) != null)
- {
- m_log.DebugFormat("[REGIONMODULE]: Not loading {0} because another module has registered {1}", module.Name, replaceableInterface.ToString());
- continue;
- }
- deferredSharedModules[replaceableInterface] = module;
- m_log.DebugFormat("[REGIONMODULE]: Deferred load of {0}", module.Name);
- continue;
- }
- m_log.DebugFormat("[REGIONMODULE]: Adding scene {0} to shared module {1}",
- scene.RegionInfo.RegionName, module.Name);
- module.AddRegion(scene);
- scene.AddRegionModule(module.Name, module);
- sharedlist.Add(module);
- }
- IConfig modulesConfig = m_openSim.ConfigSource.Source.Configs["Modules"];
- // Scan for, and load, nonshared modules
- List<INonSharedRegionModule> list = new List<INonSharedRegionModule>();
- foreach (TypeExtensionNode node in m_nonSharedModules)
- {
- object[] ctorArgs = new object[] {0};
- // Read the config
- string moduleString = modulesConfig.GetString("Setup_" + node.Id, string.Empty);
- // We may not want to load this at all
- if (moduleString == "disabled")
- continue;
- // Get the port number, if there is one
- if (!string.IsNullOrEmpty(moduleString))
- {
- // Get the port number from the string
- string[] moduleParts = moduleString.Split(new char[] {'/'}, 2);
- if (moduleParts.Length > 1)
- ctorArgs[0] = Convert.ToUInt32(moduleParts[0]);
- }
- // Actually load it
- INonSharedRegionModule module = null;
- Type[] ctorParamTypes = new Type[ctorArgs.Length];
- for (int i = 0; i < ctorParamTypes.Length; i++)
- ctorParamTypes[i] = ctorArgs[i].GetType();
- if (node.Type.GetConstructor(ctorParamTypes) != null)
- module = (INonSharedRegionModule)Activator.CreateInstance(node.Type, ctorArgs);
- else
- module = (INonSharedRegionModule)Activator.CreateInstance(node.Type);
- // Check for replaceable interfaces
- Type replaceableInterface = module.ReplaceableInterface;
- if (replaceableInterface != null)
- {
- MethodInfo mii = mi.MakeGenericMethod(replaceableInterface);
- if (mii.Invoke(scene, new object[0]) != null)
- {
- m_log.DebugFormat("[REGIONMODULE]: Not loading {0} because another module has registered {1}", module.Name, replaceableInterface.ToString());
- continue;
- }
- deferredNonSharedModules[replaceableInterface] = module;
- m_log.DebugFormat("[REGIONMODULE]: Deferred load of {0}", module.Name);
- continue;
- }
- m_log.DebugFormat("[REGIONMODULE]: Adding scene {0} to non-shared module {1}",
- scene.RegionInfo.RegionName, module.Name);
- // Initialise the module
- module.Initialise(m_openSim.ConfigSource.Source);
- list.Add(module);
- }
- // Now add the modules that we found to the scene. If a module
- // wishes to override a replaceable interface, it needs to
- // register it in Initialise, so that the deferred module
- // won't load.
- foreach (INonSharedRegionModule module in list)
- {
- module.AddRegion(scene);
- scene.AddRegionModule(module.Name, module);
- }
- // Now all modules without a replaceable base interface are loaded
- // Replaceable modules have either been skipped, or omitted.
- // Now scan the deferred modules here
- foreach (ISharedRegionModule module in deferredSharedModules.Values)
- {
- // Determine if the interface has been replaced
- Type replaceableInterface = module.ReplaceableInterface;
- MethodInfo mii = mi.MakeGenericMethod(replaceableInterface);
- if (mii.Invoke(scene, new object[0]) != null)
- {
- m_log.DebugFormat("[REGIONMODULE]: Not loading {0} because another module has registered {1}", module.Name, replaceableInterface.ToString());
- continue;
- }
- m_log.DebugFormat("[REGIONMODULE]: Adding scene {0} to shared module {1} (deferred)",
- scene.RegionInfo.RegionName, module.Name);
- // Not replaced, load the module
- module.AddRegion(scene);
- scene.AddRegionModule(module.Name, module);
- sharedlist.Add(module);
- }
- // Same thing for nonshared modules, load them unless overridden
- List<INonSharedRegionModule> deferredlist = new List<INonSharedRegionModule>();
- foreach (INonSharedRegionModule module in deferredNonSharedModules.Values)
- {
- // Check interface override
- Type replaceableInterface = module.ReplaceableInterface;
- if (replaceableInterface != null)
- {
- MethodInfo mii = mi.MakeGenericMethod(replaceableInterface);
- if (mii.Invoke(scene, new object[0]) != null)
- {
- m_log.DebugFormat("[REGIONMODULE]: Not loading {0} because another module has registered {1}", module.Name, replaceableInterface.ToString());
- continue;
- }
- }
- m_log.DebugFormat("[REGIONMODULE]: Adding scene {0} to non-shared module {1} (deferred)",
- scene.RegionInfo.RegionName, module.Name);
- module.Initialise(m_openSim.ConfigSource.Source);
- list.Add(module);
- deferredlist.Add(module);
- }
- // Finally, load valid deferred modules
- foreach (INonSharedRegionModule module in deferredlist)
- {
- module.AddRegion(scene);
- scene.AddRegionModule(module.Name, module);
- }
- // This is needed for all module types. Modules will register
- // Interfaces with scene in AddScene, and will also need a means
- // to access interfaces registered by other modules. Without
- // this extra method, a module attempting to use another modules's
- // interface would be successful only depending on load order,
- // which can't be depended upon, or modules would need to resort
- // to ugly kludges to attempt to request interfaces when needed
- // and unneccessary caching logic repeated in all modules.
- // The extra function stub is just that much cleaner
- //
- foreach (ISharedRegionModule module in sharedlist)
- {
- module.RegionLoaded(scene);
- }
- foreach (INonSharedRegionModule module in list)
- {
- module.RegionLoaded(scene);
- }
- scene.AllModulesLoaded();
- }
- public void RemoveRegionFromModules (Scene scene)
- {
- foreach (IRegionModuleBase module in scene.RegionModules.Values)
- {
- m_log.DebugFormat("[REGIONMODULE]: Removing scene {0} from module {1}",
- scene.RegionInfo.RegionName, module.Name);
- module.RemoveRegion(scene);
- if (module is INonSharedRegionModule)
- {
- // as we were the only user, this instance has to die
- module.Close();
- }
- }
- scene.RegionModules.Clear();
- }
- #endregion
- }
- }
|