/* * 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 OpenMetaverse; using OpenSim.Framework; using OpenSim.Region.Framework.Interfaces; using OpenSim.Region.Framework.Scenes; using OpenSim.Region.CoreModules.World.Wind; namespace OpenSim.Region.CoreModules { [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "WindModule")] public class WindModule : IWindModule { private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); private uint m_frame = 0; private int m_dataVersion = 0; private int m_frameUpdateRate = 150; private Scene m_scene = null; private bool m_ready = false; private bool m_inUpdate = false; private bool m_enabled = false; private IConfig m_windConfig; private IWindModelPlugin m_activeWindPlugin = null; private string m_dWindPluginName = "SimpleRandomWind"; private Dictionary m_availableWindPlugins = new Dictionary(); // Simplified windSpeeds based on the fact that the client protocal tracks at a resolution of 16m private Vector2[] windSpeeds = new Vector2[16 * 16]; #region INonSharedRegionModule Methods public void Initialise(IConfigSource config) { m_windConfig = config.Configs["Wind"]; // string desiredWindPlugin = m_dWindPluginName; if (m_windConfig != null) { m_enabled = m_windConfig.GetBoolean("enabled", true); m_frameUpdateRate = m_windConfig.GetInt("wind_update_rate", 150); // Determine which wind model plugin is desired if (m_windConfig.Contains("wind_plugin")) { m_dWindPluginName = m_windConfig.GetString("wind_plugin", m_dWindPluginName); } } if (m_enabled) { m_log.InfoFormat("[WIND] Enabled with an update rate of {0} frames.", m_frameUpdateRate); } } public void AddRegion(Scene scene) { if (!m_enabled) return; m_scene = scene; m_frame = 0; // Register all the Wind Model Plug-ins foreach (IWindModelPlugin windPlugin in AddinManager.GetExtensionObjects("/OpenSim/WindModule", false)) { m_log.InfoFormat("[WIND] Found Plugin: {0}", windPlugin.Name); m_availableWindPlugins.Add(windPlugin.Name, windPlugin); } // Check for desired plugin if (m_availableWindPlugins.ContainsKey(m_dWindPluginName)) { m_activeWindPlugin = m_availableWindPlugins[m_dWindPluginName]; m_log.InfoFormat("[WIND] {0} plugin found, initializing.", m_dWindPluginName); if (m_windConfig != null) { m_activeWindPlugin.Initialise(); m_activeWindPlugin.WindConfig(m_scene, m_windConfig); } } // if the plug-in wasn't found, default to no wind. if (m_activeWindPlugin == null) { m_log.ErrorFormat("[WIND] Could not find specified wind plug-in: {0}", m_dWindPluginName); m_log.ErrorFormat("[WIND] Defaulting to no wind."); } // This one puts an entry in the main help screen // m_scene.AddCommand("Regions", this, "wind", "wind", "Usage: wind [value] - Get or Update Wind paramaters", null); // This one enables the ability to type just the base command without any parameters // m_scene.AddCommand("Regions", this, "wind", "", "", HandleConsoleCommand); // Get a list of the parameters for each plugin foreach (IWindModelPlugin windPlugin in m_availableWindPlugins.Values) { // m_scene.AddCommand("Regions", this, String.Format("wind base wind_plugin {0}", windPlugin.Name), String.Format("{0} - {1}", windPlugin.Name, windPlugin.Description), "", HandleConsoleBaseCommand); m_scene.AddCommand( "Regions", this, "wind base wind_update_rate", "wind base wind_update_rate []", "Get or set the wind update rate.", "", HandleConsoleBaseCommand); foreach (KeyValuePair kvp in windPlugin.WindParams()) { string windCommand = String.Format("wind {0} {1}", windPlugin.Name, kvp.Key); m_scene.AddCommand("Regions", this, windCommand, string.Format("{0} []", windCommand), kvp.Value, "", HandleConsoleParamCommand); } } // Register event handlers for when Avatars enter the region, and frame ticks m_scene.EventManager.OnFrame += WindUpdate; // Register the wind module m_scene.RegisterModuleInterface(this); // Generate initial wind values GenWind(); // hopefully this will not be the same for all regions on same instance m_dataVersion = 1; // Mark Module Ready for duty m_ready = true; } public void RemoveRegion(Scene scene) { if (!m_enabled) return; m_ready = false; // REVIEW: If a region module is closed, is there a possibility that it'll re-open/initialize ?? m_activeWindPlugin = null; foreach (IWindModelPlugin windPlugin in m_availableWindPlugins.Values) { windPlugin.Dispose(); } m_availableWindPlugins.Clear(); // Remove our hooks m_scene.EventManager.OnFrame -= WindUpdate; // m_scene.EventManager.OnMakeRootAgent -= OnAgentEnteredRegion; } public void Close() { } public string Name { get { return "WindModule"; } } public Type ReplaceableInterface { get { return null; } } public void RegionLoaded(Scene scene) { } #endregion #region Console Commands private void ValidateConsole() { if (m_scene.ConsoleScene() == null) { // FIXME: If console region is root then this will be printed by every module. Currently, there is no // way to prevent this, short of making the entire module shared (which is complete overkill). // One possibility is to return a bool to signal whether the module has completely handled the command MainConsole.Instance.Output("Please change to a specific region in order to set Sun parameters."); return; } if (m_scene.ConsoleScene() != m_scene) { MainConsole.Instance.Output("Console Scene is not my scene."); return; } } /// /// Base console command handler, only used if a person specifies the base command with now options /// private void HandleConsoleCommand(string module, string[] cmdparams) { ValidateConsole(); MainConsole.Instance.Output( "The wind command can be used to change the currently active wind model plugin and update the parameters for wind plugins."); } /// /// Called to change the active wind model plugin /// private void HandleConsoleBaseCommand(string module, string[] cmdparams) { ValidateConsole(); if ((cmdparams.Length != 4) || !cmdparams[1].Equals("base")) { MainConsole.Instance.Output( "Invalid parameters to change parameters for Wind module base, usage: wind base "); return; } switch (cmdparams[2]) { case "wind_update_rate": int newRate = 1; if (int.TryParse(cmdparams[3], out newRate)) { m_frameUpdateRate = newRate; } else { MainConsole.Instance.Output( "Invalid value {0} specified for {1}", cmdparams[3], cmdparams[2]); return; } break; case "wind_plugin": string desiredPlugin = cmdparams[3]; if (desiredPlugin.Equals(m_activeWindPlugin.Name)) { MainConsole.Instance.Output("Wind model plugin {0} is already active", cmdparams[3]); return; } if (m_availableWindPlugins.ContainsKey(desiredPlugin)) { m_activeWindPlugin = m_availableWindPlugins[cmdparams[3]]; MainConsole.Instance.Output("{0} wind model plugin now active", m_activeWindPlugin.Name); } else { MainConsole.Instance.Output("Could not find wind model plugin {0}", desiredPlugin); } break; } } /// /// Called to change plugin parameters. /// private void HandleConsoleParamCommand(string module, string[] cmdparams) { ValidateConsole(); // wind [value] if ((cmdparams.Length != 4) && (cmdparams.Length != 3)) { MainConsole.Instance.Output("Usage: wind [value]"); return; } string plugin = cmdparams[1]; string param = cmdparams[2]; float value = 0f; if (cmdparams.Length == 4) { if (!float.TryParse(cmdparams[3], out value)) { MainConsole.Instance.Output("Invalid value {0}", cmdparams[3]); } try { WindParamSet(plugin, param, value); MainConsole.Instance.Output("{0} set to {1}", param, value); } catch (Exception e) { MainConsole.Instance.Output("{0}", e.Message); } } else { try { value = WindParamGet(plugin, param); MainConsole.Instance.Output("{0} : {1}", param, value); } catch (Exception e) { MainConsole.Instance.Output("{0}", e.Message); } } } #endregion #region IWindModule Methods /// /// Retrieve the wind speed at the given region coordinate. This /// implimentation ignores Z. /// /// 0...255 /// 0...255 public Vector3 WindSpeed(int x, int y, int z) { if (m_activeWindPlugin != null) { return m_activeWindPlugin.WindSpeed(x, y, z); } else { return new Vector3(0.0f, 0.0f, 0.0f); } } public void WindParamSet(string plugin, string param, float value) { if (m_availableWindPlugins.ContainsKey(plugin)) { IWindModelPlugin windPlugin = m_availableWindPlugins[plugin]; windPlugin.WindParamSet(param, value); } else { throw new Exception(String.Format("Could not find plugin {0}", plugin)); } } public float WindParamGet(string plugin, string param) { if (m_availableWindPlugins.ContainsKey(plugin)) { IWindModelPlugin windPlugin = m_availableWindPlugins[plugin]; return windPlugin.WindParamGet(param); } else { throw new Exception(String.Format("Could not find plugin {0}", plugin)); } } public string WindActiveModelPluginName { get { if (m_activeWindPlugin != null) { return m_activeWindPlugin.Name; } else { return String.Empty; } } } #endregion /// /// Called on each frame update. Updates the wind model and clients as necessary. /// public void WindUpdate() { if ((!m_ready || m_inUpdate || (m_frame++ % m_frameUpdateRate) != 0)) return; m_inUpdate = true; Util.FireAndForget(delegate { try { GenWind(); m_scene.ForEachClient(delegate(IClientAPI client) { client.SendWindData(m_dataVersion, windSpeeds); }); } finally { m_inUpdate = false; } }, null, "WindModuleUpdate"); } /// /// Calculate new wind /// returns false if no change /// private bool GenWind() { if (m_activeWindPlugin != null && m_activeWindPlugin.WindUpdate(m_frame)) { windSpeeds = m_activeWindPlugin.WindLLClientArray(); m_dataVersion++; return true; } return false; } } }