diff --git a/.gitignore b/.gitignore index c8990d2f67..e04c219d90 100644 --- a/.gitignore +++ b/.gitignore @@ -44,6 +44,8 @@ bin/Physics* bin/Terrain* bin/Regions/* bin/UserAssets +bin/assetcache +bin/maptiles bin/estate_settings.xml bin/config-include/CenomeCache.ini bin/config-include/FlotsamCache.ini diff --git a/OpenSim/Framework/IScene.cs b/OpenSim/Framework/IScene.cs index a9432c2df3..87ec99ef60 100644 --- a/OpenSim/Framework/IScene.cs +++ b/OpenSim/Framework/IScene.cs @@ -66,6 +66,19 @@ namespace OpenSim.Framework IConfigSource Config { get; } + /// + /// Are logins enabled on this simulator? + /// + bool LoginsEnabled { get; set; } + + /// + /// Is this region ready for use? + /// + /// + /// This does not mean that logins are enabled, merely that they can be. + /// + bool Ready { get; set; } + float TimeDilation { get; } bool AllowScriptCrossings { get; } diff --git a/OpenSim/Framework/Statistics/AssetStatsCollector.cs b/OpenSim/Framework/Monitoring/AssetStatsCollector.cs similarity index 99% rename from OpenSim/Framework/Statistics/AssetStatsCollector.cs rename to OpenSim/Framework/Monitoring/AssetStatsCollector.cs index 7082ef3c3c..2a4d45bf0c 100644 --- a/OpenSim/Framework/Statistics/AssetStatsCollector.cs +++ b/OpenSim/Framework/Monitoring/AssetStatsCollector.cs @@ -28,7 +28,7 @@ using System; using System.Timers; -namespace OpenSim.Framework.Statistics +namespace OpenSim.Framework.Monitoring { /// /// Asset service statistics collection diff --git a/OpenSim/Framework/Statistics/BaseStatsCollector.cs b/OpenSim/Framework/Monitoring/BaseStatsCollector.cs similarity index 98% rename from OpenSim/Framework/Statistics/BaseStatsCollector.cs rename to OpenSim/Framework/Monitoring/BaseStatsCollector.cs index 3f918f3bd9..9ee087694b 100644 --- a/OpenSim/Framework/Statistics/BaseStatsCollector.cs +++ b/OpenSim/Framework/Monitoring/BaseStatsCollector.cs @@ -31,8 +31,7 @@ using System.Text; using OpenMetaverse; using OpenMetaverse.StructuredData; - -namespace OpenSim.Framework.Statistics +namespace OpenSim.Framework.Monitoring { /// /// Statistics which all collectors are interested in reporting diff --git a/OpenSim/Framework/Statistics/Interfaces/IPullStatsProvider.cs b/OpenSim/Framework/Monitoring/Interfaces/IPullStatsProvider.cs similarity index 97% rename from OpenSim/Framework/Statistics/Interfaces/IPullStatsProvider.cs rename to OpenSim/Framework/Monitoring/Interfaces/IPullStatsProvider.cs index 430e580e1a..86a66207f6 100644 --- a/OpenSim/Framework/Statistics/Interfaces/IPullStatsProvider.cs +++ b/OpenSim/Framework/Monitoring/Interfaces/IPullStatsProvider.cs @@ -25,7 +25,7 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -namespace OpenSim.Framework.Statistics.Interfaces +namespace OpenSim.Framework.Monitoring.Interfaces { /// /// Implemented by objects which allow statistical information to be pulled from them. diff --git a/OpenSim/Framework/Statistics/Interfaces/IStatsCollector.cs b/OpenSim/Framework/Monitoring/Interfaces/IStatsCollector.cs similarity index 98% rename from OpenSim/Framework/Statistics/Interfaces/IStatsCollector.cs rename to OpenSim/Framework/Monitoring/Interfaces/IStatsCollector.cs index 477bbb364e..99f75e34b1 100644 --- a/OpenSim/Framework/Statistics/Interfaces/IStatsCollector.cs +++ b/OpenSim/Framework/Monitoring/Interfaces/IStatsCollector.cs @@ -25,7 +25,7 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -namespace OpenSim.Framework.Statistics +namespace OpenSim.Framework.Monitoring { /// /// Implemented by classes which collect up non-viewer statistical information diff --git a/OpenSim/Framework/Monitoring/MemoryWatchdog.cs b/OpenSim/Framework/Monitoring/MemoryWatchdog.cs new file mode 100644 index 0000000000..a23cf1fea8 --- /dev/null +++ b/OpenSim/Framework/Monitoring/MemoryWatchdog.cs @@ -0,0 +1,129 @@ +/* + * 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.Linq; +using System.Reflection; +using System.Threading; +using log4net; + +namespace OpenSim.Framework.Monitoring +{ + /// + /// Experimental watchdog for memory usage. + /// + public static class MemoryWatchdog + { +// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + /// + /// Is this watchdog active? + /// + public static bool Enabled + { + get { return m_enabled; } + set + { +// m_log.DebugFormat("[MEMORY WATCHDOG]: Setting MemoryWatchdog.Enabled to {0}", value); + + if (value && !m_enabled) + UpdateLastRecord(GC.GetTotalMemory(false), Util.EnvironmentTickCount()); + + m_enabled = value; + } + } + private static bool m_enabled; + + /// + /// Average memory churn in bytes per millisecond. + /// + public static double AverageMemoryChurn + { + get { if (m_samples.Count > 0) return m_samples.Average(); else return 0; } + } + + /// + /// Maximum number of statistical samples. + /// + /// + /// At the moment this corresponds to 1 minute since the sampling rate is every 2.5 seconds as triggered from + /// the main Watchdog. + /// + private static int m_maxSamples = 24; + + /// + /// Time when the watchdog was last updated. + /// + private static int m_lastUpdateTick; + + /// + /// Memory used at time of last watchdog update. + /// + private static long m_lastUpdateMemory; + + /// + /// Memory churn rate per millisecond. + /// +// private static double m_churnRatePerMillisecond; + + /// + /// Historical samples for calculating moving average. + /// + private static Queue m_samples = new Queue(m_maxSamples); + + public static void Update() + { + int now = Util.EnvironmentTickCount(); + long memoryNow = GC.GetTotalMemory(false); + long memoryDiff = memoryNow - m_lastUpdateMemory; + + if (memoryDiff >= 0) + { + if (m_samples.Count >= m_maxSamples) + m_samples.Dequeue(); + + double elapsed = Util.EnvironmentTickCountSubtract(now, m_lastUpdateTick); + + // This should never happen since it's not useful for updates to occur with no time elapsed, but + // protect ourselves from a divide-by-zero just in case. + if (elapsed == 0) + return; + + m_samples.Enqueue(memoryDiff / (double)elapsed); + } + + UpdateLastRecord(memoryNow, now); + } + + private static void UpdateLastRecord(long memoryNow, int timeNow) + { + m_lastUpdateMemory = memoryNow; + m_lastUpdateTick = timeNow; + } + } +} \ No newline at end of file diff --git a/OpenSim/Framework/Statistics/SimExtraStatsCollector.cs b/OpenSim/Framework/Monitoring/SimExtraStatsCollector.cs similarity index 99% rename from OpenSim/Framework/Statistics/SimExtraStatsCollector.cs rename to OpenSim/Framework/Monitoring/SimExtraStatsCollector.cs index a506e3ba4d..cdd7cc711e 100644 --- a/OpenSim/Framework/Statistics/SimExtraStatsCollector.cs +++ b/OpenSim/Framework/Monitoring/SimExtraStatsCollector.cs @@ -28,12 +28,11 @@ using System; using System.Collections.Generic; using System.Text; - using OpenMetaverse; -using OpenSim.Framework.Statistics.Interfaces; using OpenMetaverse.StructuredData; +using OpenSim.Framework.Monitoring.Interfaces; -namespace OpenSim.Framework.Statistics +namespace OpenSim.Framework.Monitoring { /// /// Collects sim statistics which aren't already being collected for the linden viewer's statistics pane diff --git a/OpenSim/Framework/Statistics/StatsManager.cs b/OpenSim/Framework/Monitoring/StatsManager.cs similarity index 98% rename from OpenSim/Framework/Statistics/StatsManager.cs rename to OpenSim/Framework/Monitoring/StatsManager.cs index 436ce2fa04..d78fa6a1f0 100644 --- a/OpenSim/Framework/Statistics/StatsManager.cs +++ b/OpenSim/Framework/Monitoring/StatsManager.cs @@ -25,7 +25,7 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -namespace OpenSim.Framework.Statistics +namespace OpenSim.Framework.Monitoring { /// /// Singleton used to provide access to statistics reporters diff --git a/OpenSim/Framework/Statistics/UserStatsCollector.cs b/OpenSim/Framework/Monitoring/UserStatsCollector.cs similarity index 98% rename from OpenSim/Framework/Statistics/UserStatsCollector.cs rename to OpenSim/Framework/Monitoring/UserStatsCollector.cs index fd2a9bf198..e89c8e6344 100644 --- a/OpenSim/Framework/Statistics/UserStatsCollector.cs +++ b/OpenSim/Framework/Monitoring/UserStatsCollector.cs @@ -27,7 +27,7 @@ using System.Timers; -namespace OpenSim.Framework.Statistics +namespace OpenSim.Framework.Monitoring { /// /// Collects user service statistics diff --git a/OpenSim/Framework/Watchdog.cs b/OpenSim/Framework/Monitoring/Watchdog.cs similarity index 99% rename from OpenSim/Framework/Watchdog.cs rename to OpenSim/Framework/Monitoring/Watchdog.cs index 449d014590..e4db964dba 100644 --- a/OpenSim/Framework/Watchdog.cs +++ b/OpenSim/Framework/Monitoring/Watchdog.cs @@ -31,7 +31,7 @@ using System.Linq; using System.Threading; using log4net; -namespace OpenSim.Framework +namespace OpenSim.Framework.Monitoring { /// /// Manages launching threads and keeping watch over them for timeouts @@ -325,6 +325,9 @@ namespace OpenSim.Framework callback(callbackInfo); } + if (MemoryWatchdog.Enabled) + MemoryWatchdog.Update(); + m_watchdogTimer.Start(); } } diff --git a/OpenSim/Framework/RegionInfo.cs b/OpenSim/Framework/RegionInfo.cs index 1b2f6818c1..4bde7be2e1 100644 --- a/OpenSim/Framework/RegionInfo.cs +++ b/OpenSim/Framework/RegionInfo.cs @@ -482,9 +482,16 @@ namespace OpenSim.Framework MainConsole.Instance.Output("=====================================\n"); if (name == String.Empty) - name = MainConsole.Instance.CmdPrompt("New region name", name); - if (name == String.Empty) - throw new Exception("Cannot interactively create region with no name"); + { + while (name.Trim() == string.Empty) + { + name = MainConsole.Instance.CmdPrompt("New region name", name); + if (name.Trim() == string.Empty) + { + MainConsole.Instance.Output("Cannot interactively create region with no name"); + } + } + } source.AddConfig(name); @@ -515,15 +522,20 @@ namespace OpenSim.Framework // allKeys.Remove("RegionUUID"); string regionUUID = config.GetString("RegionUUID", string.Empty); - if (regionUUID == String.Empty) + if (!UUID.TryParse(regionUUID.Trim(), out RegionID)) { UUID newID = UUID.Random(); - - regionUUID = MainConsole.Instance.CmdPrompt("RegionUUID", newID.ToString()); + while (RegionID == UUID.Zero) + { + regionUUID = MainConsole.Instance.CmdPrompt("RegionUUID", newID.ToString()); + if (!UUID.TryParse(regionUUID.Trim(), out RegionID)) + { + MainConsole.Instance.Output("RegionUUID must be a valid UUID"); + } + } config.Set("RegionUUID", regionUUID); } - RegionID = new UUID(regionUUID); originRegionID = RegionID; // What IS this?! (Needed for RegionCombinerModule?) // Location diff --git a/OpenSim/Framework/Servers/BaseOpenSimServer.cs b/OpenSim/Framework/Servers/BaseOpenSimServer.cs index 9a2cd0e56c..cf19002baf 100644 --- a/OpenSim/Framework/Servers/BaseOpenSimServer.cs +++ b/OpenSim/Framework/Servers/BaseOpenSimServer.cs @@ -40,9 +40,9 @@ using log4net.Core; using log4net.Repository; using OpenSim.Framework; using OpenSim.Framework.Console; +using OpenSim.Framework.Monitoring; using OpenSim.Framework.Servers; using OpenSim.Framework.Servers.HttpServer; -using OpenSim.Framework.Statistics; using Timer=System.Timers.Timer; using OpenMetaverse; diff --git a/OpenSim/Framework/Servers/HttpServer/BaseHttpServer.cs b/OpenSim/Framework/Servers/HttpServer/BaseHttpServer.cs index 24f986a756..e45cb895f7 100644 --- a/OpenSim/Framework/Servers/HttpServer/BaseHttpServer.cs +++ b/OpenSim/Framework/Servers/HttpServer/BaseHttpServer.cs @@ -45,6 +45,7 @@ using OpenMetaverse.StructuredData; using CoolHTTPListener = HttpServer.HttpListener; using HttpListener=System.Net.HttpListener; using LogPrio=HttpServer.LogPrio; +using OpenSim.Framework.Monitoring; namespace OpenSim.Framework.Servers.HttpServer { diff --git a/OpenSim/Framework/Servers/HttpServer/PollServiceRequestManager.cs b/OpenSim/Framework/Servers/HttpServer/PollServiceRequestManager.cs index a3bd330fb8..a385110b53 100644 --- a/OpenSim/Framework/Servers/HttpServer/PollServiceRequestManager.cs +++ b/OpenSim/Framework/Servers/HttpServer/PollServiceRequestManager.cs @@ -32,6 +32,7 @@ using System.Reflection; using log4net; using HttpServer; using OpenSim.Framework; +using OpenSim.Framework.Monitoring; /* diff --git a/OpenSim/Framework/Servers/HttpServer/PollServiceWorkerThread.cs b/OpenSim/Framework/Servers/HttpServer/PollServiceWorkerThread.cs index 1e3fbf0db5..1c529b6e93 100644 --- a/OpenSim/Framework/Servers/HttpServer/PollServiceWorkerThread.cs +++ b/OpenSim/Framework/Servers/HttpServer/PollServiceWorkerThread.cs @@ -36,6 +36,7 @@ using HttpServer; using OpenMetaverse; using System.Reflection; using log4net; +using OpenSim.Framework.Monitoring; namespace OpenSim.Framework.Servers.HttpServer { diff --git a/OpenSim/Region/Application/OpenSim.cs b/OpenSim/Region/Application/OpenSim.cs index e6b57c2ecc..ba8aa9f98c 100644 --- a/OpenSim/Region/Application/OpenSim.cs +++ b/OpenSim/Region/Application/OpenSim.cs @@ -40,7 +40,7 @@ using OpenMetaverse; using OpenSim.Framework; using OpenSim.Framework.Console; using OpenSim.Framework.Servers; -using OpenSim.Framework.Statistics; +using OpenSim.Framework.Monitoring; using OpenSim.Region.Framework.Interfaces; using OpenSim.Region.Framework.Scenes; @@ -200,9 +200,9 @@ namespace OpenSim PrintFileToConsole("startuplogo.txt"); // For now, start at the 'root' level by default - if (m_sceneManager.Scenes.Count == 1) // If there is only one region, select it + if (SceneManager.Scenes.Count == 1) // If there is only one region, select it ChangeSelectedRegion("region", - new string[] {"change", "region", m_sceneManager.Scenes[0].RegionInfo.RegionName}); + new string[] {"change", "region", SceneManager.Scenes[0].RegionInfo.RegionName}); else ChangeSelectedRegion("region", new string[] {"change", "region", "root"}); @@ -461,7 +461,7 @@ namespace OpenSim if (cmdparams.Length > 4) alert = String.Format("\n{0}\n", String.Join(" ", cmdparams, 4, cmdparams.Length - 4)); - IList agents = m_sceneManager.GetCurrentSceneAvatars(); + IList agents = SceneManager.GetCurrentSceneAvatars(); foreach (ScenePresence presence in agents) { @@ -542,7 +542,7 @@ namespace OpenSim private void HandleForceUpdate(string module, string[] args) { MainConsole.Instance.Output("Updating all clients"); - m_sceneManager.ForceCurrentSceneClientUpdate(); + SceneManager.ForceCurrentSceneClientUpdate(); } /// @@ -554,7 +554,7 @@ namespace OpenSim { if (args.Length == 6) { - m_sceneManager.HandleEditCommandOnCurrentScene(args); + SceneManager.HandleEditCommandOnCurrentScene(args); } else { @@ -765,7 +765,7 @@ namespace OpenSim case "load": if (cmdparams.Length > 1) { - foreach (Scene s in new ArrayList(m_sceneManager.Scenes)) + foreach (Scene s in new ArrayList(SceneManager.Scenes)) { MainConsole.Instance.Output(String.Format("Loading module: {0}", cmdparams[1])); m_moduleLoader.LoadRegionModules(cmdparams[1], s); @@ -803,14 +803,14 @@ namespace OpenSim case "backup": MainConsole.Instance.Output("Triggering save of pending object updates to persistent store"); - m_sceneManager.BackupCurrentScene(); + SceneManager.BackupCurrentScene(); break; case "remove-region": string regRemoveName = CombineParams(cmdparams, 0); Scene removeScene; - if (m_sceneManager.TryGetScene(regRemoveName, out removeScene)) + if (SceneManager.TryGetScene(regRemoveName, out removeScene)) RemoveRegion(removeScene, false); else MainConsole.Instance.Output("No region with that name"); @@ -820,14 +820,14 @@ namespace OpenSim string regDeleteName = CombineParams(cmdparams, 0); Scene killScene; - if (m_sceneManager.TryGetScene(regDeleteName, out killScene)) + if (SceneManager.TryGetScene(regDeleteName, out killScene)) RemoveRegion(killScene, true); else MainConsole.Instance.Output("no region with that name"); break; case "restart": - m_sceneManager.RestartCurrentScene(); + SceneManager.RestartCurrentScene(); break; } } @@ -842,7 +842,7 @@ namespace OpenSim { string newRegionName = CombineParams(cmdparams, 2); - if (!m_sceneManager.TrySetCurrentScene(newRegionName)) + if (!SceneManager.TrySetCurrentScene(newRegionName)) MainConsole.Instance.Output(String.Format("Couldn't select region {0}", newRegionName)); } else @@ -850,7 +850,7 @@ namespace OpenSim MainConsole.Instance.Output("Usage: change region "); } - string regionName = (m_sceneManager.CurrentScene == null ? "root" : m_sceneManager.CurrentScene.RegionInfo.RegionName); + string regionName = (SceneManager.CurrentScene == null ? "root" : SceneManager.CurrentScene.RegionInfo.RegionName); MainConsole.Instance.Output(String.Format("Currently selected region is {0}", regionName)); // m_log.DebugFormat("Original prompt is {0}", m_consolePrompt); @@ -868,7 +868,7 @@ namespace OpenSim }); m_console.DefaultPrompt = prompt; - m_console.ConsoleScene = m_sceneManager.CurrentScene; + m_console.ConsoleScene = SceneManager.CurrentScene; } /// @@ -892,7 +892,7 @@ namespace OpenSim int newDebug; if (int.TryParse(args[2], out newDebug)) { - m_sceneManager.SetDebugPacketLevelOnCurrentScene(newDebug, name); + SceneManager.SetDebugPacketLevelOnCurrentScene(newDebug, name); // We provide user information elsewhere if any clients had their debug level set. // MainConsole.Instance.OutputFormat("Debug packet level set to {0}", newDebug); } @@ -907,7 +907,7 @@ namespace OpenSim case "scene": if (args.Length == 4) { - if (m_sceneManager.CurrentScene == null) + if (SceneManager.CurrentScene == null) { MainConsole.Instance.Output("Please use 'change region ' first"); } @@ -915,7 +915,7 @@ namespace OpenSim { string key = args[2]; string value = args[3]; - m_sceneManager.CurrentScene.SetSceneCoreDebug( + SceneManager.CurrentScene.SetSceneCoreDebug( new Dictionary() { { key, value } }); MainConsole.Instance.OutputFormat("Set debug scene {0} = {1}", key, value); @@ -954,10 +954,10 @@ namespace OpenSim IList agents; if (showParams.Length > 1 && showParams[1] == "full") { - agents = m_sceneManager.GetCurrentScenePresences(); + agents = SceneManager.GetCurrentScenePresences(); } else { - agents = m_sceneManager.GetCurrentSceneAvatars(); + agents = SceneManager.GetCurrentSceneAvatars(); } MainConsole.Instance.Output(String.Format("\nAgents connected: {0}\n", agents.Count)); @@ -1037,7 +1037,7 @@ namespace OpenSim MainConsole.Instance.Output("Shared Module: " + module.Name); } - m_sceneManager.ForEachScene( + SceneManager.ForEachScene( delegate(Scene scene) { m_log.Error("The currently loaded modules in " + scene.RegionInfo.RegionName + " are:"); foreach (IRegionModule module in scene.Modules.Values) @@ -1050,7 +1050,7 @@ namespace OpenSim } ); - m_sceneManager.ForEachScene( + SceneManager.ForEachScene( delegate(Scene scene) { MainConsole.Instance.Output("Loaded new region modules in" + scene.RegionInfo.RegionName + " are:"); foreach (IRegionModuleBase module in scene.RegionModules.Values) @@ -1066,7 +1066,7 @@ namespace OpenSim break; case "regions": - m_sceneManager.ForEachScene( + SceneManager.ForEachScene( delegate(Scene scene) { MainConsole.Instance.Output(String.Format( @@ -1080,7 +1080,7 @@ namespace OpenSim break; case "ratings": - m_sceneManager.ForEachScene( + SceneManager.ForEachScene( delegate(Scene scene) { string rating = ""; @@ -1115,7 +1115,7 @@ namespace OpenSim cdt.AddColumn("IP", 16); cdt.AddColumn("Viewer Name", 24); - m_sceneManager.ForEachScene( + SceneManager.ForEachScene( s => { foreach (AgentCircuitData aCircuit in s.AuthenticateHandler.GetAgentCircuits().Values) @@ -1140,13 +1140,13 @@ namespace OpenSim cdt.AddColumn("Endpoint", 23); cdt.AddColumn("Active?", 7); - m_sceneManager.ForEachScene( + SceneManager.ForEachScene( s => s.ForEachClient( c => cdt.AddRow( s.Name, c.Name, - c.RemoteEndPoint.ToString(), c.CircuitCode.ToString(), + c.RemoteEndPoint.ToString(), c.IsActive.ToString()))); MainConsole.Instance.Output(cdt.ToString()); @@ -1161,11 +1161,11 @@ namespace OpenSim { if (cmdparams.Length > 5) { - m_sceneManager.SaveNamedPrimsToXml2(cmdparams[3], cmdparams[4]); + SceneManager.SaveNamedPrimsToXml2(cmdparams[3], cmdparams[4]); } else { - m_sceneManager.SaveNamedPrimsToXml2("Primitive", DEFAULT_PRIM_BACKUP_FILENAME); + SceneManager.SaveNamedPrimsToXml2("Primitive", DEFAULT_PRIM_BACKUP_FILENAME); } } @@ -1180,11 +1180,11 @@ namespace OpenSim if (cmdparams.Length > 0) { - m_sceneManager.SaveCurrentSceneToXml(cmdparams[2]); + SceneManager.SaveCurrentSceneToXml(cmdparams[2]); } else { - m_sceneManager.SaveCurrentSceneToXml(DEFAULT_PRIM_BACKUP_FILENAME); + SceneManager.SaveCurrentSceneToXml(DEFAULT_PRIM_BACKUP_FILENAME); } } @@ -1221,13 +1221,13 @@ namespace OpenSim MainConsole.Instance.Output(String.Format("loadOffsets = <{0},{1},{2}>",loadOffset.X,loadOffset.Y,loadOffset.Z)); } } - m_sceneManager.LoadCurrentSceneFromXml(cmdparams[0], generateNewIDS, loadOffset); + SceneManager.LoadCurrentSceneFromXml(cmdparams[2], generateNewIDS, loadOffset); } else { try { - m_sceneManager.LoadCurrentSceneFromXml(DEFAULT_PRIM_BACKUP_FILENAME, false, loadOffset); + SceneManager.LoadCurrentSceneFromXml(DEFAULT_PRIM_BACKUP_FILENAME, false, loadOffset); } catch (FileNotFoundException) { @@ -1244,11 +1244,11 @@ namespace OpenSim { if (cmdparams.Length > 2) { - m_sceneManager.SaveCurrentSceneToXml2(cmdparams[2]); + SceneManager.SaveCurrentSceneToXml2(cmdparams[2]); } else { - m_sceneManager.SaveCurrentSceneToXml2(DEFAULT_PRIM_BACKUP_FILENAME); + SceneManager.SaveCurrentSceneToXml2(DEFAULT_PRIM_BACKUP_FILENAME); } } @@ -1263,7 +1263,7 @@ namespace OpenSim { try { - m_sceneManager.LoadCurrentSceneFromXml2(cmdparams[2]); + SceneManager.LoadCurrentSceneFromXml2(cmdparams[2]); } catch (FileNotFoundException) { @@ -1274,7 +1274,7 @@ namespace OpenSim { try { - m_sceneManager.LoadCurrentSceneFromXml2(DEFAULT_PRIM_BACKUP_FILENAME); + SceneManager.LoadCurrentSceneFromXml2(DEFAULT_PRIM_BACKUP_FILENAME); } catch (FileNotFoundException) { @@ -1291,7 +1291,7 @@ namespace OpenSim { try { - m_sceneManager.LoadArchiveToCurrentScene(cmdparams); + SceneManager.LoadArchiveToCurrentScene(cmdparams); } catch (Exception e) { @@ -1305,7 +1305,7 @@ namespace OpenSim /// protected void SaveOar(string module, string[] cmdparams) { - m_sceneManager.SaveCurrentSceneToArchive(cmdparams); + SceneManager.SaveCurrentSceneToArchive(cmdparams); } private static string CombineParams(string[] commandParams, int pos) diff --git a/OpenSim/Region/Application/OpenSimBase.cs b/OpenSim/Region/Application/OpenSimBase.cs index 76ac246caa..aed10f60a2 100644 --- a/OpenSim/Region/Application/OpenSimBase.cs +++ b/OpenSim/Region/Application/OpenSimBase.cs @@ -40,7 +40,7 @@ using OpenSim.Framework.Communications; using OpenSim.Framework.Console; using OpenSim.Framework.Servers; using OpenSim.Framework.Servers.HttpServer; -using OpenSim.Framework.Statistics; +using OpenSim.Framework.Monitoring; using OpenSim.Region.ClientStack; using OpenSim.Region.CoreModules.ServiceConnectorsOut.UserAccounts; using OpenSim.Region.Framework; @@ -300,7 +300,7 @@ namespace OpenSim private void HandleCommanderCommand(string module, string[] cmd) { - m_sceneManager.SendCommandToPluginModules(cmd); + SceneManager.SendCommandToPluginModules(cmd); } private void HandleCommanderHelp(string module, string[] cmd) @@ -318,7 +318,10 @@ namespace OpenSim // Called from base.StartUp() m_httpServerPort = m_networkServersInfo.HttpListenerPort; - m_sceneManager.OnRestartSim += handleRestartRegion; + SceneManager.OnRestartSim += handleRestartRegion; + + // Only start the memory watchdog once all regions are ready + SceneManager.OnRegionsReadyStatusChange += sm => MemoryWatchdog.Enabled = sm.AllRegionsReady; } /// @@ -480,7 +483,7 @@ namespace OpenSim scene.SnmpService.BootInfo("ScriptEngine started", scene); } - m_sceneManager.Add(scene); + SceneManager.Add(scene); if (m_autoCreateClientStack) { @@ -510,7 +513,6 @@ namespace OpenSim } scene.Start(); - scene.StartScripts(); return clientServer; @@ -644,14 +646,14 @@ namespace OpenSim { // only need to check this if we are not at the // root level - if ((m_sceneManager.CurrentScene != null) && - (m_sceneManager.CurrentScene.RegionInfo.RegionID == scene.RegionInfo.RegionID)) + if ((SceneManager.CurrentScene != null) && + (SceneManager.CurrentScene.RegionInfo.RegionID == scene.RegionInfo.RegionID)) { - m_sceneManager.TrySetCurrentScene(".."); + SceneManager.TrySetCurrentScene(".."); } scene.DeleteAllSceneObjects(); - m_sceneManager.CloseScene(scene); + SceneManager.CloseScene(scene); ShutdownClientServer(scene.RegionInfo); if (!cleanup) @@ -693,7 +695,7 @@ namespace OpenSim public void RemoveRegion(string name, bool cleanUp) { Scene target; - if (m_sceneManager.TryGetScene(name, out target)) + if (SceneManager.TryGetScene(name, out target)) RemoveRegion(target, cleanUp); } @@ -706,13 +708,13 @@ namespace OpenSim { // only need to check this if we are not at the // root level - if ((m_sceneManager.CurrentScene != null) && - (m_sceneManager.CurrentScene.RegionInfo.RegionID == scene.RegionInfo.RegionID)) + if ((SceneManager.CurrentScene != null) && + (SceneManager.CurrentScene.RegionInfo.RegionID == scene.RegionInfo.RegionID)) { - m_sceneManager.TrySetCurrentScene(".."); + SceneManager.TrySetCurrentScene(".."); } - m_sceneManager.CloseScene(scene); + SceneManager.CloseScene(scene); ShutdownClientServer(scene.RegionInfo); } @@ -724,7 +726,7 @@ namespace OpenSim public void CloseRegion(string name) { Scene target; - if (m_sceneManager.TryGetScene(name, out target)) + if (SceneManager.TryGetScene(name, out target)) CloseRegion(target); } @@ -781,6 +783,7 @@ namespace OpenSim scene.LoadWorldMap(); scene.PhysicsScene = GetPhysicsScene(scene.RegionInfo.RegionName); + scene.PhysicsScene.RequestAssetMethod = scene.PhysicsRequestAsset; scene.PhysicsScene.SetTerrain(scene.Heightmap.GetFloatsSerialised()); scene.PhysicsScene.SetWaterLevel((float) regionInfo.RegionSettings.WaterHeight); @@ -980,7 +983,7 @@ namespace OpenSim try { - m_sceneManager.Close(); + SceneManager.Close(); } catch (Exception e) { @@ -1005,7 +1008,7 @@ namespace OpenSim /// The first out parameter describing the number of all the avatars in the Region server public void GetAvatarNumber(out int usernum) { - usernum = m_sceneManager.GetCurrentSceneAvatars().Count; + usernum = SceneManager.GetCurrentSceneAvatars().Count; } /// @@ -1014,7 +1017,7 @@ namespace OpenSim /// The first out parameter describing the number of regions public void GetRegionNumber(out int regionnum) { - regionnum = m_sceneManager.Scenes.Count; + regionnum = SceneManager.Scenes.Count; } /// diff --git a/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs b/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs index ad9074cde4..f7bb817644 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs @@ -41,7 +41,7 @@ using OpenMetaverse.Messages.Linden; using OpenMetaverse.StructuredData; using OpenSim.Framework; using OpenSim.Framework.Client; -using OpenSim.Framework.Statistics; +using OpenSim.Framework.Monitoring; using OpenSim.Region.Framework.Interfaces; using OpenSim.Region.Framework.Scenes; using OpenSim.Services.Interfaces; @@ -355,8 +355,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP private int m_animationSequenceNumber = 1; private bool m_SendLogoutPacketWhenClosing = true; private AgentUpdateArgs lastarg; - private bool m_IsActive = true; - private bool m_IsLoggingOut = false; protected Dictionary m_packetHandlers = new Dictionary(); protected Dictionary m_genericPacketHandlers = new Dictionary(); //PauPaw:Local Generic Message handlers @@ -428,16 +426,19 @@ namespace OpenSim.Region.ClientStack.LindenUDP public uint CircuitCode { get { return m_circuitCode; } } public int MoneyBalance { get { return m_moneyBalance; } } public int NextAnimationSequenceNumber { get { return m_animationSequenceNumber++; } } - public bool IsActive - { - get { return m_IsActive; } - set { m_IsActive = value; } - } - public bool IsLoggingOut - { - get { return m_IsLoggingOut; } - set { m_IsLoggingOut = value; } - } + + /// + /// As well as it's function in IClientAPI, in LLClientView we are locking on this property in order to + /// prevent race conditions by different threads calling Close(). + /// + public bool IsActive { get; set; } + + /// + /// Used to synchronise threads when client is being closed. + /// + public Object CloseSyncLock { get; private set; } + + public bool IsLoggingOut { get; set; } public bool DisableFacelights { @@ -462,6 +463,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP { // DebugPacketLevel = 1; + CloseSyncLock = new Object(); + RegisterInterface(this); RegisterInterface(this); RegisterInterface(this); @@ -494,13 +497,15 @@ namespace OpenSim.Region.ClientStack.LindenUDP m_prioritizer = new Prioritizer(m_scene); RegisterLocalPacketHandlers(); + + IsActive = true; } #region Client Methods /// - /// Shut down the client view + /// Close down the client view /// public void Close() { @@ -513,7 +518,29 @@ namespace OpenSim.Region.ClientStack.LindenUDP public void Close(bool sendStop) { IsActive = false; + // We lock here to prevent race conditions between two threads calling close simultaneously (e.g. + // a simultaneous relog just as a client is being closed out due to no packet ack from the old connection. + lock (CloseSyncLock) + { + if (!IsActive) + return; + IsActive = false; + CloseWithoutChecks(sendStop); + } + } + + /// + /// Closes down the client view without first checking whether it is active. + /// + /// + /// This exists because LLUDPServer has to set IsActive = false in earlier synchronous code before calling + /// CloseWithoutIsActiveCheck asynchronously. + /// + /// Callers must lock ClosingSyncLock before calling. + /// + public void CloseWithoutChecks(bool sendStop) + { m_log.DebugFormat( "[CLIENT]: Close has been called for {0} attached to scene {1}", Name, m_scene.RegionInfo.RegionName); @@ -3634,7 +3661,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP public void SendCoarseLocationUpdate(List users, List CoarseLocations) { - if (!IsActive) return; // We don't need to update inactive clients. + // We don't need to update inactive clients. + if (!IsActive) + return; CoarseLocationUpdatePacket loc = (CoarseLocationUpdatePacket)PacketPool.Instance.GetPacket(PacketType.CoarseLocationUpdate); loc.Header.Reliable = false; @@ -5267,7 +5296,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP AddLocalPacketHandler(PacketType.ChatFromViewer, HandleChatFromViewer); AddLocalPacketHandler(PacketType.AvatarPropertiesUpdate, HandlerAvatarPropertiesUpdate); AddLocalPacketHandler(PacketType.ScriptDialogReply, HandlerScriptDialogReply); - AddLocalPacketHandler(PacketType.ImprovedInstantMessage, HandlerImprovedInstantMessage, false); + AddLocalPacketHandler(PacketType.ImprovedInstantMessage, HandlerImprovedInstantMessage); AddLocalPacketHandler(PacketType.AcceptFriendship, HandlerAcceptFriendship); AddLocalPacketHandler(PacketType.DeclineFriendship, HandlerDeclineFriendship); AddLocalPacketHandler(PacketType.TerminateFriendship, HandlerTerminateFriendship); diff --git a/OpenSim/Region/ClientStack/Linden/UDP/LLUDPClient.cs b/OpenSim/Region/ClientStack/Linden/UDP/LLUDPClient.cs index ae721757b0..c472176933 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/LLUDPClient.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/LLUDPClient.cs @@ -279,7 +279,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP public string GetStats() { return string.Format( - "{0,7} {1,7} {2,7} {3,9} {4,7} {5,7} {6,7} {7,7} {8,7} {9,8} {10,7} {11,7}", + "{0,7} {1,7} {2,7} {3,9} {4,7} {5,7} {6,7} {7,7} {8,7} {9,8} {10,7} {11,7} {12,7}", + Util.EnvironmentTickCountSubtract(TickLastPacketReceived), PacketsReceived, PacketsSent, PacketsResent, diff --git a/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs b/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs index b09f607b49..6c2e71b9ea 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs @@ -37,7 +37,7 @@ using log4net; using Nini.Config; using OpenMetaverse.Packets; using OpenSim.Framework; -using OpenSim.Framework.Statistics; +using OpenSim.Framework.Monitoring; using OpenSim.Region.Framework.Scenes; using OpenMetaverse; @@ -1181,22 +1181,21 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// regular client pings. /// /// - private void DeactivateClientDueToTimeout(IClientAPI client) + private void DeactivateClientDueToTimeout(LLClientView client) { - // We must set IsActive synchronously so that we can stop the packet loop reinvoking this method, even - // though it's set later on by LLClientView.Close() - client.IsActive = false; - - m_log.WarnFormat( - "[LLUDPSERVER]: Ack timeout, disconnecting {0} agent for {1} in {2}", - client.SceneAgent.IsChildAgent ? "child" : "root", client.Name, m_scene.RegionInfo.RegionName); - - StatsManager.SimExtraStats.AddAbnormalClientThreadTermination(); - - if (!client.SceneAgent.IsChildAgent) - client.Kick("Simulator logged you out due to connection timeout"); - - Util.FireAndForget(o => client.Close()); + lock (client.CloseSyncLock) + { + m_log.WarnFormat( + "[LLUDPSERVER]: Ack timeout, disconnecting {0} agent for {1} in {2}", + client.SceneAgent.IsChildAgent ? "child" : "root", client.Name, m_scene.RegionInfo.RegionName); + + StatsManager.SimExtraStats.AddAbnormalClientThreadTermination(); + + if (!client.SceneAgent.IsChildAgent) + client.Kick("Simulator logged you out due to connection timeout"); + + client.CloseWithoutChecks(true); + } } private void IncomingPacketHandler() diff --git a/OpenSim/Region/ClientStack/RegionApplicationBase.cs b/OpenSim/Region/ClientStack/RegionApplicationBase.cs index c4324e8592..4672f8aa8a 100644 --- a/OpenSim/Region/ClientStack/RegionApplicationBase.cs +++ b/OpenSim/Region/ClientStack/RegionApplicationBase.cs @@ -53,9 +53,8 @@ namespace OpenSim.Region.ClientStack protected ISimulationDataService m_simulationDataService; protected IEstateDataService m_estateDataService; protected ClientStackManager m_clientStackManager; - protected SceneManager m_sceneManager = new SceneManager(); - public SceneManager SceneManager { get { return m_sceneManager; } } + public SceneManager SceneManager { get; protected set; } public NetworkServersInfo NetServersInfo { get { return m_networkServersInfo; } } public ISimulationDataService SimulationDataService { get { return m_simulationDataService; } } public IEstateDataService EstateDataService { get { return m_estateDataService; } } @@ -77,6 +76,7 @@ namespace OpenSim.Region.ClientStack protected override void StartupSpecific() { + SceneManager = new SceneManager(); m_clientStackManager = CreateClientStackManager(); Initialize(); diff --git a/OpenSim/Region/CoreModules/Avatar/Attachments/AttachmentsModule.cs b/OpenSim/Region/CoreModules/Avatar/Attachments/AttachmentsModule.cs index 31e8a2e4cd..b58870434d 100644 --- a/OpenSim/Region/CoreModules/Avatar/Attachments/AttachmentsModule.cs +++ b/OpenSim/Region/CoreModules/Avatar/Attachments/AttachmentsModule.cs @@ -626,10 +626,12 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments m_scene.InventoryService.UpdateItem(item); - // this gets called when the agent logs off! + // If the name of the object has been changed whilst attached then we want to update the inventory + // item in the viewer. if (sp.ControllingClient != null) sp.ControllingClient.SendInventoryItemCreateUpdate(item, 0); } + grp.HasGroupChanged = false; // Prevent it being saved over and over } // else diff --git a/OpenSim/Region/CoreModules/Avatar/Attachments/Tests/AttachmentsModuleTests.cs b/OpenSim/Region/CoreModules/Avatar/Attachments/Tests/AttachmentsModuleTests.cs index cd1e1c19d7..273e290d0f 100644 --- a/OpenSim/Region/CoreModules/Avatar/Attachments/Tests/AttachmentsModuleTests.cs +++ b/OpenSim/Region/CoreModules/Avatar/Attachments/Tests/AttachmentsModuleTests.cs @@ -47,6 +47,7 @@ using OpenSim.Region.CoreModules.ServiceConnectorsOut.Simulation; using OpenSim.Region.CoreModules.World.Serialiser; using OpenSim.Region.Framework.Scenes; using OpenSim.Region.Framework.Interfaces; +using OpenSim.Region.ScriptEngine.Interfaces; using OpenSim.Region.ScriptEngine.XEngine; using OpenSim.Services.Interfaces; using OpenSim.Tests.Common; @@ -289,21 +290,37 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments.Tests { TestHelpers.InMethod(); - Scene scene = CreateTestScene(); + Scene scene = CreateScriptingEnabledTestScene(); UserAccount ua1 = UserAccountHelpers.CreateUserWithInventory(scene, 0x1); - ScenePresence sp = SceneHelpers.AddScenePresence(scene, ua1.PrincipalID); + ScenePresence sp = SceneHelpers.AddScenePresence(scene, ua1); SceneObjectGroup so = SceneHelpers.CreateSceneObject(1, sp.UUID, "att-name", 0x10); - TaskInventoryHelpers.AddScript(scene, so.RootPart); + TaskInventoryItem scriptItem + = TaskInventoryHelpers.AddScript( + scene, + so.RootPart, + "scriptItem", + "default { attach(key id) { if (id != NULL_KEY) { llSay(0, \"Hello World\"); } } }"); + InventoryItemBase userItem = UserInventoryHelpers.AddInventoryItem(scene, so, 0x100, 0x1000); + // FIXME: Right now, we have to do a tricksy chat listen to make sure we know when the script is running. + // In the future, we need to be able to do this programatically more predicably. + scene.EventManager.OnChatFromWorld += OnChatFromWorld; + scene.AttachmentsModule.RezSingleAttachmentFromInventory(sp, userItem.ID, (uint)AttachmentPoint.Chest); + m_chatEvent.WaitOne(60000); + // TODO: Need to have a test that checks the script is actually started but this involves a lot more // plumbing of the script engine and either pausing for events or more infrastructure to turn off various // script engine delays/asychronicity that isn't helpful in an automated regression testing context. SceneObjectGroup attSo = scene.GetSceneObjectGroup(so.Name); Assert.That(attSo.ContainsScripts(), Is.True); + + TaskInventoryItem reRezzedScriptItem = attSo.RootPart.Inventory.GetInventoryItem(scriptItem.Name); + IScriptModule xengine = scene.RequestModuleInterface(); + Assert.That(xengine.GetScriptState(reRezzedScriptItem.ItemID), Is.True); } [Test] @@ -379,29 +396,49 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments.Tests ScenePresence sp = SceneHelpers.AddScenePresence(scene, ua1); SceneObjectGroup so = SceneHelpers.CreateSceneObject(1, sp.UUID, "att-name", 0x10); - TaskInventoryHelpers.AddScript(scene, so.RootPart); + TaskInventoryItem scriptTaskItem + = TaskInventoryHelpers.AddScript( + scene, + so.RootPart, + "scriptItem", + "default { attach(key id) { if (id != NULL_KEY) { llSay(0, \"Hello World\"); } } }"); + InventoryItemBase userItem = UserInventoryHelpers.AddInventoryItem(scene, so, 0x100, 0x1000); // FIXME: Right now, we have to do a tricksy chat listen to make sure we know when the script is running. // In the future, we need to be able to do this programatically more predicably. scene.EventManager.OnChatFromWorld += OnChatFromWorld; - SceneObjectGroup soRezzed - = (SceneObjectGroup)scene.AttachmentsModule.RezSingleAttachmentFromInventory(sp, userItem.ID, (uint)AttachmentPoint.Chest); + SceneObjectGroup rezzedSo + = (SceneObjectGroup)(scene.AttachmentsModule.RezSingleAttachmentFromInventory(sp, userItem.ID, (uint)AttachmentPoint.Chest)); // Wait for chat to signal rezzed script has been started. m_chatEvent.WaitOne(60000); - scene.AttachmentsModule.DetachSingleAttachmentToInv(sp, soRezzed); + scene.AttachmentsModule.DetachSingleAttachmentToInv(sp, rezzedSo); InventoryItemBase userItemUpdated = scene.InventoryService.GetItem(userItem); AssetBase asset = scene.AssetService.Get(userItemUpdated.AssetID.ToString()); + // TODO: It would probably be better here to check script state via the saving and retrieval of state + // information at a higher level, rather than having to inspect the serialization. XmlDocument soXml = new XmlDocument(); soXml.LoadXml(Encoding.UTF8.GetString(asset.Data)); XmlNodeList scriptStateNodes = soXml.GetElementsByTagName("ScriptState"); Assert.That(scriptStateNodes.Count, Is.EqualTo(1)); + + // Re-rez the attachment to check script running state + SceneObjectGroup reRezzedSo = (SceneObjectGroup)(scene.AttachmentsModule.RezSingleAttachmentFromInventory(sp, userItem.ID, (uint)AttachmentPoint.Chest)); + + // Wait for chat to signal rezzed script has been started. + m_chatEvent.WaitOne(60000); + + TaskInventoryItem reRezzedScriptItem = reRezzedSo.RootPart.Inventory.GetInventoryItem(scriptTaskItem.Name); + IScriptModule xengine = scene.RequestModuleInterface(); + Assert.That(xengine.GetScriptState(reRezzedScriptItem.ItemID), Is.True); + +// Console.WriteLine(soXml.OuterXml); } /// diff --git a/OpenSim/Region/CoreModules/Avatar/Commands/UserCommandsModule.cs b/OpenSim/Region/CoreModules/Avatar/Commands/UserCommandsModule.cs index 4bcd2ac499..764adf9401 100644 --- a/OpenSim/Region/CoreModules/Avatar/Commands/UserCommandsModule.cs +++ b/OpenSim/Region/CoreModules/Avatar/Commands/UserCommandsModule.cs @@ -37,7 +37,7 @@ using Nini.Config; using OpenMetaverse; using OpenSim.Framework; using OpenSim.Framework.Console; -using OpenSim.Framework.Statistics; +using OpenSim.Framework.Monitoring; using OpenSim.Region.Framework.Interfaces; using OpenSim.Region.Framework.Scenes; diff --git a/OpenSim/Region/CoreModules/Avatar/Groups/GroupsModule.cs b/OpenSim/Region/CoreModules/Avatar/Groups/GroupsModule.cs index 31363e5705..b258e13dce 100644 --- a/OpenSim/Region/CoreModules/Avatar/Groups/GroupsModule.cs +++ b/OpenSim/Region/CoreModules/Avatar/Groups/GroupsModule.cs @@ -96,7 +96,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Groups scene.EventManager.OnNewClient += OnNewClient; scene.EventManager.OnClientClosed += OnClientClosed; - scene.EventManager.OnIncomingInstantMessage += OnGridInstantMessage; +// scene.EventManager.OnIncomingInstantMessage += OnGridInstantMessage; } public void PostInitialise() @@ -133,7 +133,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Groups private void OnNewClient(IClientAPI client) { // Subscribe to instant messages - client.OnInstantMessage += OnInstantMessage; +// client.OnInstantMessage += OnInstantMessage; client.OnAgentDataUpdateRequest += OnAgentDataUpdateRequest; client.OnUUIDGroupNameRequest += HandleUUIDGroupNameRequest; lock (m_ClientMap) @@ -171,15 +171,15 @@ namespace OpenSim.Region.CoreModules.Avatar.Groups ActiveGroupTitle); } - private void OnInstantMessage(IClientAPI client, GridInstantMessage im) - { - } +// private void OnInstantMessage(IClientAPI client, GridInstantMessage im) +// { +// } - private void OnGridInstantMessage(GridInstantMessage msg) - { - // Trigger the above event handler - OnInstantMessage(null, msg); - } +// private void OnGridInstantMessage(GridInstantMessage msg) +// { +// // Trigger the above event handler +// OnInstantMessage(null, msg); +// } private void HandleUUIDGroupNameRequest(UUID id,IClientAPI remote_client) { diff --git a/OpenSim/Region/CoreModules/Avatar/InstantMessage/MessageTransferModule.cs b/OpenSim/Region/CoreModules/Avatar/InstantMessage/MessageTransferModule.cs index 6064ddc426..1406aaec25 100644 --- a/OpenSim/Region/CoreModules/Avatar/InstantMessage/MessageTransferModule.cs +++ b/OpenSim/Region/CoreModules/Avatar/InstantMessage/MessageTransferModule.cs @@ -141,13 +141,15 @@ namespace OpenSim.Region.CoreModules.Avatar.InstantMessage foreach (Scene scene in m_Scenes) { // m_log.DebugFormat( -// "[INSTANT MESSAGE]: Looking for root agent {0} in {1}", +// "[INSTANT MESSAGE]: Looking for root agent {0} in {1}", // toAgentID.ToString(), scene.RegionInfo.RegionName); + ScenePresence sp = scene.GetScenePresence(toAgentID); if (sp != null && !sp.IsChildAgent) { // Local message -// m_log.DebugFormat("[INSTANT MESSAGE]: Delivering IM to root agent {0} {1}", user.Name, toAgentID); + m_log.DebugFormat("[INSTANT MESSAGE]: Delivering IM to root agent {0} {1}", sp.Name, toAgentID); + sp.ControllingClient.SendInstantMessage(im); // Message sent @@ -159,13 +161,15 @@ namespace OpenSim.Region.CoreModules.Avatar.InstantMessage // try child avatar second foreach (Scene scene in m_Scenes) { -// m_log.DebugFormat( -// "[INSTANT MESSAGE]: Looking for child of {0} in {1}", toAgentID, scene.RegionInfo.RegionName); + m_log.DebugFormat( + "[INSTANT MESSAGE]: Looking for child of {0} in {1}", toAgentID, scene.RegionInfo.RegionName); + ScenePresence sp = scene.GetScenePresence(toAgentID); if (sp != null) { // Local message -// m_log.DebugFormat("[INSTANT MESSAGE]: Delivering IM to child agent {0} {1}", user.Name, toAgentID); + m_log.DebugFormat("[INSTANT MESSAGE]: Delivering IM to child agent {0} {1}", sp.Name, toAgentID); + sp.ControllingClient.SendInstantMessage(im); // Message sent @@ -174,10 +178,9 @@ namespace OpenSim.Region.CoreModules.Avatar.InstantMessage } } -// m_log.DebugFormat("[INSTANT MESSAGE]: Delivering IM to {0} via XMLRPC", im.toAgentID); - SendGridInstantMessageViaXMLRPC(im, result); + m_log.DebugFormat("[INSTANT MESSAGE]: Delivering IM to {0} via XMLRPC", im.toAgentID); - return; + SendGridInstantMessageViaXMLRPC(im, result); } private void HandleUndeliveredMessage(GridInstantMessage im, MessageResultNotification result) diff --git a/OpenSim/Region/CoreModules/Avatar/Inventory/Transfer/InventoryTransferModule.cs b/OpenSim/Region/CoreModules/Avatar/Inventory/Transfer/InventoryTransferModule.cs index 80554fb3aa..81de29c108 100644 --- a/OpenSim/Region/CoreModules/Avatar/Inventory/Transfer/InventoryTransferModule.cs +++ b/OpenSim/Region/CoreModules/Avatar/Inventory/Transfer/InventoryTransferModule.cs @@ -313,8 +313,9 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Transfer m_TransferModule.SendInstantMessage(im, delegate(bool success) {}); } } - else if (im.dialog == (byte) InstantMessageDialog.InventoryDeclined || - im.dialog == (byte) InstantMessageDialog.TaskInventoryDeclined) + else if ( + im.dialog == (byte)InstantMessageDialog.InventoryDeclined + || im.dialog == (byte)InstantMessageDialog.TaskInventoryDeclined) { // Here, the recipient is local and we can assume that the // inventory is loaded. Courtesy of the above bulk update, diff --git a/OpenSim/Region/CoreModules/Framework/InventoryAccess/InventoryAccessModule.cs b/OpenSim/Region/CoreModules/Framework/InventoryAccess/InventoryAccessModule.cs index d30c2e2d4c..9a56f42567 100644 --- a/OpenSim/Region/CoreModules/Framework/InventoryAccess/InventoryAccessModule.cs +++ b/OpenSim/Region/CoreModules/Framework/InventoryAccess/InventoryAccessModule.cs @@ -204,8 +204,9 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess AssetBase asset = m_Scene.CreateAsset(name, description, assetType, data, remoteClient.AgentId); m_Scene.AssetService.Store(asset); - - m_Scene.CreateNewInventoryItem(remoteClient, remoteClient.AgentId.ToString(), string.Empty, folderID, asset.Name, 0, callbackID, asset, invType, nextOwnerMask, creationDate); + m_Scene.CreateNewInventoryItem( + remoteClient, remoteClient.AgentId.ToString(), string.Empty, folderID, + name, description, 0, callbackID, asset, invType, nextOwnerMask, creationDate); } else { diff --git a/OpenSim/Region/CoreModules/Framework/Statistics/Logging/BinaryLoggingModule.cs b/OpenSim/Region/CoreModules/Framework/Statistics/Logging/BinaryLoggingModule.cs index a75ff6234e..fb74cc6d72 100644 --- a/OpenSim/Region/CoreModules/Framework/Statistics/Logging/BinaryLoggingModule.cs +++ b/OpenSim/Region/CoreModules/Framework/Statistics/Logging/BinaryLoggingModule.cs @@ -39,7 +39,7 @@ using OpenSim.Region.Framework; using OpenSim.Region.Framework.Interfaces; using OpenSim.Region.Framework.Scenes; -namespace OpenSim.Region.CoreModules.Avatar.Attachments +namespace OpenSim.Region.CoreModules.Framework.Statistics.Logging { [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "BinaryLoggingModule")] public class BinaryLoggingModule : INonSharedRegionModule diff --git a/OpenSim/Region/CoreModules/Framework/Statistics/Logging/LogWriter.cs b/OpenSim/Region/CoreModules/Framework/Statistics/Logging/LogWriter.cs new file mode 100755 index 0000000000..65e4c90ba5 --- /dev/null +++ b/OpenSim/Region/CoreModules/Framework/Statistics/Logging/LogWriter.cs @@ -0,0 +1,161 @@ +/* + * 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.IO; +using System.Text; +using log4net; + +namespace OpenSim.Region.CoreModules.Framework.Statistics.Logging +{ + /// + /// Class for writing a high performance, high volume log file. + /// Sometimes, to debug, one has a high volume logging to do and the regular + /// log file output is not appropriate. + /// Create a new instance with the parameters needed and + /// call Write() to output a line. Call Close() when finished. + /// If created with no parameters, it will not log anything. + /// + public class LogWriter : IDisposable + { + public bool Enabled { get; private set; } + + private string m_logDirectory = "."; + private int m_logMaxFileTimeMin = 5; // 5 minutes + public String LogFileHeader { get; set; } + + private StreamWriter m_logFile = null; + private TimeSpan m_logFileLife; + private DateTime m_logFileEndTime; + private Object m_logFileWriteLock = new Object(); + + // set externally when debugging. If let 'null', this does not write any error messages. + public ILog ErrorLogger = null; + private string LogHeader = "[LOG WRITER]"; + + /// + /// Create a log writer that will not write anything. Good for when not enabled + /// but the write statements are still in the code. + /// + public LogWriter() + { + Enabled = false; + m_logFile = null; + } + + /// + /// Create a log writer instance. + /// + /// The directory to create the log file in. May be 'null' for default. + /// The characters that begin the log file name. May be 'null' for default. + /// Maximum age of a log file in minutes. If zero, will set default. + public LogWriter(string dir, string headr, int maxFileTime) + { + m_logDirectory = dir == null ? "." : dir; + + LogFileHeader = headr == null ? "log-" : headr; + + m_logMaxFileTimeMin = maxFileTime; + if (m_logMaxFileTimeMin < 1) + m_logMaxFileTimeMin = 5; + + m_logFileLife = new TimeSpan(0, m_logMaxFileTimeMin, 0); + m_logFileEndTime = DateTime.Now + m_logFileLife; + + Enabled = true; + } + + public void Dispose() + { + this.Close(); + } + + public void Close() + { + Enabled = false; + if (m_logFile != null) + { + m_logFile.Close(); + m_logFile.Dispose(); + m_logFile = null; + } + } + + public void Write(string line, params object[] args) + { + if (!Enabled) return; + Write(String.Format(line, args)); + } + + public void Write(string line) + { + if (!Enabled) return; + try + { + lock (m_logFileWriteLock) + { + DateTime now = DateTime.Now; + if (m_logFile == null || now > m_logFileEndTime) + { + if (m_logFile != null) + { + m_logFile.Close(); + m_logFile.Dispose(); + m_logFile = null; + } + + // First log file or time has expired, start writing to a new log file + m_logFileEndTime = now + m_logFileLife; + string path = (m_logDirectory.Length > 0 ? m_logDirectory + + System.IO.Path.DirectorySeparatorChar.ToString() : "") + + String.Format("{0}{1}.log", LogFileHeader, now.ToString("yyyyMMddHHmmss")); + m_logFile = new StreamWriter(File.Open(path, FileMode.Append, FileAccess.Write)); + } + if (m_logFile != null) + { + StringBuilder buff = new StringBuilder(line.Length + 25); + buff.Append(now.ToString("yyyyMMddHHmmssfff")); + // buff.Append(now.ToString("yyyyMMddHHmmss")); + buff.Append(","); + buff.Append(line); + buff.Append("\r\n"); + m_logFile.Write(buff.ToString()); + } + } + } + catch (Exception e) + { + if (ErrorLogger != null) + { + ErrorLogger.ErrorFormat("{0}: FAILURE WRITING TO LOGFILE: {1}", LogHeader, e); + } + Enabled = false; + } + return; + } + } +} \ No newline at end of file diff --git a/OpenSim/Region/CoreModules/InterGrid/OpenGridProtocolModule.cs b/OpenSim/Region/CoreModules/InterGrid/OpenGridProtocolModule.cs index a6e2548406..4a76b00467 100644 --- a/OpenSim/Region/CoreModules/InterGrid/OpenGridProtocolModule.cs +++ b/OpenSim/Region/CoreModules/InterGrid/OpenGridProtocolModule.cs @@ -40,6 +40,7 @@ using OpenMetaverse; using OpenMetaverse.StructuredData; using OpenSim.Framework; using OpenSim.Framework.Capabilities; +using OpenSim.Framework.Monitoring; using OpenSim.Framework.Servers; using OpenSim.Region.Framework.Interfaces; using OpenSim.Region.Framework.Scenes; diff --git a/OpenSim/Region/CoreModules/ServiceConnectorsOut/Inventory/RemoteXInventoryServiceConnector.cs b/OpenSim/Region/CoreModules/ServiceConnectorsOut/Inventory/RemoteXInventoryServiceConnector.cs index 990dffb61a..11e0150083 100644 --- a/OpenSim/Region/CoreModules/ServiceConnectorsOut/Inventory/RemoteXInventoryServiceConnector.cs +++ b/OpenSim/Region/CoreModules/ServiceConnectorsOut/Inventory/RemoteXInventoryServiceConnector.cs @@ -31,7 +31,7 @@ using System.Collections.Generic; using System.Reflection; using Nini.Config; using OpenSim.Framework; -using OpenSim.Framework.Statistics; +using OpenSim.Framework.Monitoring; using OpenSim.Services.Connectors; using OpenSim.Region.Framework.Interfaces; using OpenSim.Region.Framework.Scenes; diff --git a/OpenSim/Region/CoreModules/ServiceConnectorsOut/MapImage/MapImageServiceModule.cs b/OpenSim/Region/CoreModules/ServiceConnectorsOut/MapImage/MapImageServiceModule.cs index 6cd077a47a..e4c6c1ad76 100644 --- a/OpenSim/Region/CoreModules/ServiceConnectorsOut/MapImage/MapImageServiceModule.cs +++ b/OpenSim/Region/CoreModules/ServiceConnectorsOut/MapImage/MapImageServiceModule.cs @@ -131,11 +131,6 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.MapImage m_enabled = true; } - /// - /// - /// - - /// /// /// @@ -149,10 +144,9 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.MapImage lock (m_scenes) m_scenes[scene.RegionInfo.RegionID] = scene; - scene.EventManager.OnLoginsEnabled += OnLoginsEnabled; + scene.EventManager.OnRegionReadyStatusChange += s => { if (s.Ready) UploadMapTile(s); }; } - /// /// /// @@ -166,21 +160,7 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.MapImage } #endregion ISharedRegionModule - - void OnLoginsEnabled(string regionName) - { - Scene scene = null; - foreach (Scene s in m_scenes.Values) - if (s.RegionInfo.RegionName == regionName) - { - scene = s; - break; - } - if (scene != null) - UploadMapTile(scene); - } - - + /// /// /// diff --git a/OpenSim/Region/CoreModules/World/Access/AccessModule.cs b/OpenSim/Region/CoreModules/World/Access/AccessModule.cs index 553a32d3bd..e7b14547f4 100644 --- a/OpenSim/Region/CoreModules/World/Access/AccessModule.cs +++ b/OpenSim/Region/CoreModules/World/Access/AccessModule.cs @@ -129,18 +129,18 @@ namespace OpenSim.Region.CoreModules.World switch (cmd[1]) { case "enable": - scene.LoginsDisabled = false; + scene.LoginsEnabled = true; MainConsole.Instance.Output(String.Format("Logins are enabled for region {0}", scene.RegionInfo.RegionName)); break; case "disable": - scene.LoginsDisabled = true; + scene.LoginsEnabled = false; MainConsole.Instance.Output(String.Format("Logins are disabled for region {0}", scene.RegionInfo.RegionName)); break; case "status": - if (scene.LoginsDisabled) - MainConsole.Instance.Output(String.Format("Login in {0} are disabled", scene.RegionInfo.RegionName)); - else + if (scene.LoginsEnabled) MainConsole.Instance.Output(String.Format("Login in {0} are enabled", scene.RegionInfo.RegionName)); + else + MainConsole.Instance.Output(String.Format("Login in {0} are disabled", scene.RegionInfo.RegionName)); break; default: MainConsole.Instance.Output("Syntax: login enable|disable|status"); diff --git a/OpenSim/Region/CoreModules/World/Objects/Commands/ObjectCommandsModule.cs b/OpenSim/Region/CoreModules/World/Objects/Commands/ObjectCommandsModule.cs index e5cd3e2133..09f6758ba4 100644 --- a/OpenSim/Region/CoreModules/World/Objects/Commands/ObjectCommandsModule.cs +++ b/OpenSim/Region/CoreModules/World/Objects/Commands/ObjectCommandsModule.cs @@ -37,7 +37,7 @@ using Nini.Config; using OpenMetaverse; using OpenSim.Framework; using OpenSim.Framework.Console; -using OpenSim.Framework.Statistics; +using OpenSim.Framework.Monitoring; using OpenSim.Region.Framework.Interfaces; using OpenSim.Region.Framework.Scenes; diff --git a/OpenSim/Region/CoreModules/World/Region/RegionCommandsModule.cs b/OpenSim/Region/CoreModules/World/Region/RegionCommandsModule.cs index 2838e0c356..7d3547322f 100644 --- a/OpenSim/Region/CoreModules/World/Region/RegionCommandsModule.cs +++ b/OpenSim/Region/CoreModules/World/Region/RegionCommandsModule.cs @@ -37,7 +37,7 @@ using Nini.Config; using OpenMetaverse; using OpenSim.Framework; using OpenSim.Framework.Console; -using OpenSim.Framework.Statistics; +using OpenSim.Framework.Monitoring; using OpenSim.Region.Framework.Interfaces; using OpenSim.Region.Framework.Scenes; diff --git a/OpenSim/Region/CoreModules/World/Sound/SoundModule.cs b/OpenSim/Region/CoreModules/World/Sound/SoundModule.cs index d768a1ac16..14c1a3947e 100644 --- a/OpenSim/Region/CoreModules/World/Sound/SoundModule.cs +++ b/OpenSim/Region/CoreModules/World/Sound/SoundModule.cs @@ -119,17 +119,20 @@ namespace OpenSim.Region.CoreModules.World.Sound m_scene.ForEachRootScenePresence(delegate(ScenePresence sp) { double dis = Util.GetDistanceTo(sp.AbsolutePosition, position); + if (dis > 100.0) // Max audio distance return; + float thisSpGain; + // Scale by distance if (radius == 0) - gain = (float)((double)gain * ((100.0 - dis) / 100.0)); + thisSpGain = (float)((double)gain * ((100.0 - dis) / 100.0)); else - gain = (float)((double)gain * ((radius - dis) / radius)); + thisSpGain = (float)((double)gain * ((radius - dis) / radius)); sp.ControllingClient.SendTriggeredSound( - soundId, ownerID, objectID, parentID, handle, position, (float)gain); + soundId, ownerID, objectID, parentID, handle, position, thisSpGain); }); } } diff --git a/OpenSim/Region/CoreModules/World/WorldMap/WorldMapModule.cs b/OpenSim/Region/CoreModules/World/WorldMap/WorldMapModule.cs index 309856f542..26b406e35a 100644 --- a/OpenSim/Region/CoreModules/World/WorldMap/WorldMapModule.cs +++ b/OpenSim/Region/CoreModules/World/WorldMap/WorldMapModule.cs @@ -42,6 +42,7 @@ using OpenMetaverse.Imaging; using OpenMetaverse.StructuredData; using OpenSim.Framework; using OpenSim.Framework.Capabilities; +using OpenSim.Framework.Monitoring; using OpenSim.Framework.Servers; using OpenSim.Framework.Servers.HttpServer; using OpenSim.Region.Framework.Interfaces; diff --git a/OpenSim/Region/Framework/Interfaces/IRegionReadyModule.cs b/OpenSim/Region/Framework/Interfaces/IRegionReadyModule.cs index aa4a757420..136ca9261b 100644 --- a/OpenSim/Region/Framework/Interfaces/IRegionReadyModule.cs +++ b/OpenSim/Region/Framework/Interfaces/IRegionReadyModule.cs @@ -25,14 +25,23 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - using System; +using OpenSim.Framework; namespace OpenSim.Region.Framework.Interfaces { public interface IRegionReadyModule { void OarLoadingAlert(string msg); + + /// + /// Trigger region ready status manually. + /// + /// + /// This should be called by the scene if the IRegionReadyModule has set Scene.LoginLock == true + /// + /// + void TriggerRegionReady(IScene scene); } } diff --git a/OpenSim/Region/Framework/Interfaces/IScenePresence.cs b/OpenSim/Region/Framework/Interfaces/IScenePresence.cs index 3f68ee02ec..0fe681fb63 100644 --- a/OpenSim/Region/Framework/Interfaces/IScenePresence.cs +++ b/OpenSim/Region/Framework/Interfaces/IScenePresence.cs @@ -40,8 +40,6 @@ namespace OpenSim.Region.Framework.Interfaces /// public interface IScenePresence : ISceneAgent { - PresenceType PresenceType { get; } - /// /// Copy of the script states while the agent is in transit. This state may /// need to be placed back in case of transfer fail. diff --git a/OpenSim/Region/Framework/Interfaces/IScriptModuleComms.cs b/OpenSim/Region/Framework/Interfaces/IScriptModuleComms.cs index bfe1e8dc33..ed71a951cb 100644 --- a/OpenSim/Region/Framework/Interfaces/IScriptModuleComms.cs +++ b/OpenSim/Region/Framework/Interfaces/IScriptModuleComms.cs @@ -67,6 +67,10 @@ namespace OpenSim.Region.Framework.Interfaces /// void DispatchReply(UUID scriptId, int code, string text, string key); + /// For constants + void RegisterConstant(string cname, object value); + object LookupModConstant(string cname); + // For use ONLY by the script API void RaiseEvent(UUID script, string id, string module, string command, string key); } diff --git a/OpenSim/Region/Framework/Scenes/Animation/ScenePresenceAnimator.cs b/OpenSim/Region/Framework/Scenes/Animation/ScenePresenceAnimator.cs index 9ddac1950a..50a176bcfc 100644 --- a/OpenSim/Region/Framework/Scenes/Animation/ScenePresenceAnimator.cs +++ b/OpenSim/Region/Framework/Scenes/Animation/ScenePresenceAnimator.cs @@ -551,11 +551,5 @@ namespace OpenSim.Region.Framework.Scenes.Animation SendAnimPack(animIDs, sequenceNums, objectIDs); } - - public void Close() - { - m_animations = null; - m_scenePresence = null; - } } -} +} \ No newline at end of file diff --git a/OpenSim/Region/Framework/Scenes/EventManager.cs b/OpenSim/Region/Framework/Scenes/EventManager.cs index d783e57620..7cb3811483 100644 --- a/OpenSim/Region/Framework/Scenes/EventManager.cs +++ b/OpenSim/Region/Framework/Scenes/EventManager.cs @@ -500,15 +500,24 @@ namespace OpenSim.Region.Framework.Scenes public delegate void RegionHeartbeatEnd(Scene scene); public event RegionHeartbeatEnd OnRegionHeartbeatEnd; - public delegate void LoginsEnabled(string regionName); - /// - /// This should only fire in all circumstances if the RegionReady module is active. + /// Fired when logins to a region are enabled or disabled. /// /// - /// TODO: Fire this even when the RegionReady module is not active. + /// /// - public event LoginsEnabled OnLoginsEnabled; + /// Fired + public event RegionLoginsStatusChange OnRegionLoginsStatusChange; + public delegate void RegionLoginsStatusChange(IScene scene); + + /// + /// Fired when a region is considered ready for use. + /// + /// + /// A region is considered ready when startup operations such as loading of scripts already on the region + /// have been completed. + /// + public event Action OnRegionReadyStatusChange; public delegate void PrimsLoaded(Scene s); public event PrimsLoaded OnPrimsLoaded; @@ -2502,21 +2511,42 @@ namespace OpenSim.Region.Framework.Scenes } } - public void TriggerLoginsEnabled (string regionName) + public void TriggerRegionLoginsStatusChange(IScene scene) { - LoginsEnabled handler = OnLoginsEnabled; + RegionLoginsStatusChange handler = OnRegionLoginsStatusChange; - if ( handler != null) + if (handler != null) { - foreach (LoginsEnabled d in handler.GetInvocationList()) + foreach (RegionLoginsStatusChange d in handler.GetInvocationList()) { try { - d(regionName); + d(scene); } catch (Exception e) { - m_log.ErrorFormat("[EVENT MANAGER]: Delegate for LoginsEnabled failed - continuing {0} - {1}", + m_log.ErrorFormat("[EVENT MANAGER]: Delegate for OnRegionLoginsStatusChange failed - continuing {0} - {1}", + e.Message, e.StackTrace); + } + } + } + } + + public void TriggerRegionReadyStatusChange(IScene scene) + { + Action handler = OnRegionReadyStatusChange; + + if (handler != null) + { + foreach (Action d in handler.GetInvocationList()) + { + try + { + d(scene); + } + catch (Exception e) + { + m_log.ErrorFormat("[EVENT MANAGER]: Delegate for OnRegionReadyStatusChange failed - continuing {0} - {1}", e.Message, e.StackTrace); } } diff --git a/OpenSim/Region/Framework/Scenes/RegionStatsHandler.cs b/OpenSim/Region/Framework/Scenes/RegionStatsHandler.cs index 1365831779..c11174dc4e 100644 --- a/OpenSim/Region/Framework/Scenes/RegionStatsHandler.cs +++ b/OpenSim/Region/Framework/Scenes/RegionStatsHandler.cs @@ -39,7 +39,7 @@ using OpenSim.Framework.Communications; using OpenSim.Framework.Console; using OpenSim.Framework.Servers; using OpenSim.Framework.Servers.HttpServer; -using OpenSim.Framework.Statistics; +using OpenSim.Framework.Monitoring; using OpenSim.Region.Framework; using OpenSim.Region.Framework.Interfaces; using OpenSim.Region.Framework.Scenes; diff --git a/OpenSim/Region/Framework/Scenes/Scene.Inventory.cs b/OpenSim/Region/Framework/Scenes/Scene.Inventory.cs index 98b8fccb4a..672d95a724 100644 --- a/OpenSim/Region/Framework/Scenes/Scene.Inventory.cs +++ b/OpenSim/Region/Framework/Scenes/Scene.Inventory.cs @@ -811,16 +811,20 @@ namespace OpenSim.Region.Framework.Scenes && oldAgentID == LibraryService.LibraryRootFolder.Owner)) { CreateNewInventoryItem( - remoteClient, item.CreatorId, item.CreatorData, newFolderID, newName, item.Flags, callbackID, asset, (sbyte)item.InvType, - item.BasePermissions, item.CurrentPermissions, item.EveryOnePermissions, item.NextPermissions, item.GroupPermissions, Util.UnixTimeSinceEpoch()); + remoteClient, item.CreatorId, item.CreatorData, newFolderID, + newName, item.Description, item.Flags, callbackID, asset, (sbyte)item.InvType, + item.BasePermissions, item.CurrentPermissions, item.EveryOnePermissions, + item.NextPermissions, item.GroupPermissions, Util.UnixTimeSinceEpoch()); } else { // If item is transfer or permissions are off or calling agent is allowed to copy item owner's inventory item. - if (((item.CurrentPermissions & (uint)PermissionMask.Transfer) != 0) && (m_permissions.BypassPermissions() || m_permissions.CanCopyUserInventory(remoteClient.AgentId, oldItemID))) + if (((item.CurrentPermissions & (uint)PermissionMask.Transfer) != 0) + && (m_permissions.BypassPermissions() + || m_permissions.CanCopyUserInventory(remoteClient.AgentId, oldItemID))) { CreateNewInventoryItem( - remoteClient, item.CreatorId, item.CreatorData, newFolderID, newName, item.Flags, callbackID, + remoteClient, item.CreatorId, item.CreatorData, newFolderID, newName, item.Description, item.Flags, callbackID, asset, (sbyte) item.InvType, item.NextPermissions, item.NextPermissions, item.EveryOnePermissions & item.NextPermissions, item.NextPermissions, item.GroupPermissions, Util.UnixTimeSinceEpoch()); @@ -885,32 +889,50 @@ namespace OpenSim.Region.Framework.Scenes /// /// Create a new inventory item. /// - /// - /// - /// - /// - /// - /// - public void CreateNewInventoryItem(IClientAPI remoteClient, string creatorID, string creatorData, UUID folderID, string name, uint flags, uint callbackID, - AssetBase asset, sbyte invType, uint nextOwnerMask, int creationDate) + /// Client creating this inventory item. + /// + /// + /// UUID of folder in which this item should be placed. + /// Item name. + /// Item description. + /// Item flags + /// Generated by the client. + /// Asset to which this item refers. + /// Type of inventory item. + /// Next owner pemrissions mask. + /// Unix timestamp at which this item was created. + public void CreateNewInventoryItem( + IClientAPI remoteClient, string creatorID, string creatorData, UUID folderID, + string name, string description, uint flags, uint callbackID, + AssetBase asset, sbyte invType, uint nextOwnerMask, int creationDate) { CreateNewInventoryItem( - remoteClient, creatorID, creatorData, folderID, name, flags, callbackID, asset, invType, + remoteClient, creatorID, creatorData, folderID, name, description, flags, callbackID, asset, invType, (uint)PermissionMask.All, (uint)PermissionMask.All, 0, nextOwnerMask, 0, creationDate); } /// /// Create a new Inventory Item /// - /// - /// - /// - /// - /// - /// - /// + /// Client creating this inventory item. + /// + /// + /// UUID of folder in which this item should be placed. + /// Item name. + /// Item description. + /// Item flags + /// Generated by the client. + /// Asset to which this item refers. + /// Type of inventory item. + /// Base permissions mask. + /// Current permissions mask. + /// Everyone permissions mask. + /// Next owner pemrissions mask. + /// Group permissions mask. + /// Unix timestamp at which this item was created. private void CreateNewInventoryItem( - IClientAPI remoteClient, string creatorID, string creatorData, UUID folderID, string name, uint flags, uint callbackID, AssetBase asset, sbyte invType, + IClientAPI remoteClient, string creatorID, string creatorData, UUID folderID, + string name, string description, uint flags, uint callbackID, AssetBase asset, sbyte invType, uint baseMask, uint currentMask, uint everyoneMask, uint nextOwnerMask, uint groupMask, int creationDate) { InventoryItemBase item = new InventoryItemBase(); @@ -919,8 +941,8 @@ namespace OpenSim.Region.Framework.Scenes item.CreatorData = creatorData; item.ID = UUID.Random(); item.AssetID = asset.FullID; - item.Description = asset.Description; item.Name = name; + item.Description = description; item.Flags = flags; item.AssetType = asset.Type; item.InvType = invType; @@ -1002,7 +1024,8 @@ namespace OpenSim.Region.Framework.Scenes asset.Description = description; CreateNewInventoryItem( - remoteClient, remoteClient.AgentId.ToString(), string.Empty, folderID, name, 0, callbackID, asset, invType, + remoteClient, remoteClient.AgentId.ToString(), string.Empty, folderID, + name, description, 0, callbackID, asset, invType, (uint)PermissionMask.All, (uint)PermissionMask.All, (uint)PermissionMask.All, (uint)PermissionMask.All, (uint)PermissionMask.All, Util.UnixTimeSinceEpoch()); } diff --git a/OpenSim/Region/Framework/Scenes/Scene.cs b/OpenSim/Region/Framework/Scenes/Scene.cs index ad9e91d7b9..3a28d42092 100644 --- a/OpenSim/Region/Framework/Scenes/Scene.cs +++ b/OpenSim/Region/Framework/Scenes/Scene.cs @@ -40,6 +40,7 @@ using OpenMetaverse; using OpenMetaverse.Packets; using OpenMetaverse.Imaging; using OpenSim.Framework; +using OpenSim.Framework.Monitoring; using OpenSim.Services.Interfaces; using OpenSim.Framework.Communications; using OpenSim.Framework.Console; @@ -129,9 +130,10 @@ namespace OpenSim.Region.Framework.Scenes public bool m_strictAccessControl = true; public bool m_seeIntoBannedRegion = false; public int MaxUndoCount = 5; + // Using this for RegionReady module to prevent LoginsDisabled from changing under our feet; public bool LoginLock = false; - public bool LoginsDisabled = true; + public bool StartDisabled = false; public bool LoadingPrims; public IXfer XferManager; @@ -727,6 +729,8 @@ namespace OpenSim.Region.Framework.Scenes { IConfig startupConfig = m_config.Configs["Startup"]; + StartDisabled = startupConfig.GetBoolean("StartDisabled", false); + m_defaultDrawDistance = startupConfig.GetFloat("DefaultDrawDistance",m_defaultDrawDistance); m_useBackup = startupConfig.GetBoolean("UseSceneBackup", m_useBackup); if (!m_useBackup) @@ -1229,15 +1233,17 @@ namespace OpenSim.Region.Framework.Scenes avatar.ControllingClient.SendShutdownConnectionNotice(); }); + // Stop updating the scene objects and agents. + m_shuttingDown = true; + // Wait here, or the kick messages won't actually get to the agents before the scene terminates. + // We also need to wait to avoid a race condition with the scene update loop which might not yet + // have checked ShuttingDown. Thread.Sleep(500); // Stop all client threads. ForEachScenePresence(delegate(ScenePresence avatar) { avatar.ControllingClient.Close(); }); - // Stop updating the scene objects and agents. - m_shuttingDown = true; - m_log.Debug("[SCENE]: Persisting changed objects"); EventManager.TriggerSceneShuttingDown(this); @@ -1252,6 +1258,15 @@ namespace OpenSim.Region.Framework.Scenes m_sceneGraph.Close(); + if (PhysicsScene != null) + { + PhysicsScene phys = PhysicsScene; + // remove the physics engine from both Scene and SceneGraph + PhysicsScene = null; + phys.Dispose(); + phys = null; + } + if (!GridService.DeregisterRegion(RegionInfo.RegionID)) m_log.WarnFormat("[SCENE]: Deregister from grid failed for region {0}", Name); @@ -1530,7 +1545,7 @@ namespace OpenSim.Region.Framework.Scenes // landMS = Util.EnvironmentTickCountSubtract(ldMS); //} - if (LoginsDisabled && Frame == 20) + if (!LoginsEnabled && Frame == 20) { // m_log.DebugFormat("{0} {1} {2}", LoginsDisabled, m_sceneGraph.GetActiveScriptsCount(), LoginLock); @@ -1538,31 +1553,34 @@ namespace OpenSim.Region.Framework.Scenes // this is a rare case where we know we have just went through a long cycle of heap // allocations, and there is no more work to be done until someone logs in GC.Collect(); - - IConfig startupConfig = m_config.Configs["Startup"]; - if (startupConfig == null || !startupConfig.GetBoolean("StartDisabled", false)) + + if (!LoginLock) + { + if (!StartDisabled) + { + m_log.InfoFormat("[REGION]: Enabling logins for {0}", RegionInfo.RegionName); + LoginsEnabled = true; + } + + m_sceneGridService.InformNeighborsThatRegionisUp( + RequestModuleInterface(), RegionInfo); + + // Region ready should always be set + Ready = true; + } + else { // This handles a case of a region having no scripts for the RegionReady module if (m_sceneGraph.GetActiveScriptsCount() == 0) { - // need to be able to tell these have changed in RegionReady - LoginLock = false; - EventManager.TriggerLoginsEnabled(RegionInfo.RegionName); + // In this case, we leave it to the IRegionReadyModule to enable logins + + // LoginLock can currently only be set by a region module implementation. + // If somehow this hasn't been done then the quickest way to bugfix is to see the + // NullReferenceException + IRegionReadyModule rrm = RequestModuleInterface(); + rrm.TriggerRegionReady(this); } - - // For RegionReady lockouts - if (!LoginLock) - { - m_log.InfoFormat("[REGION]: Enabling logins for {0}", RegionInfo.RegionName); - LoginsDisabled = false; - } - - m_sceneGridService.InformNeighborsThatRegionisUp(RequestModuleInterface(), RegionInfo); - } - else - { - StartDisabled = true; - LoginsDisabled = true; } } } @@ -3477,25 +3495,31 @@ namespace OpenSim.Region.Framework.Scenes if (AgentTransactionsModule != null) AgentTransactionsModule.RemoveAgentAssetTransactions(agentID); - avatar.Close(); - m_authenticateHandler.RemoveCircuit(avatar.ControllingClient.CircuitCode); m_log.Debug("[Scene] The avatar has left the building"); } catch (Exception e) { m_log.Error( - string.Format("[SCENE]: Exception removing {0} from {1}, ", avatar.Name, RegionInfo.RegionName), e); + string.Format("[SCENE]: Exception removing {0} from {1}. Cleaning up. Exception ", avatar.Name, Name), e); } finally { - // Always clean these structures up so that any failure above doesn't cause them to remain in the - // scene with possibly bad effects (e.g. continually timing out on unacked packets and triggering - // the same cleanup exception continually. - // TODO: This should probably extend to the whole method, but we don't want to also catch the NRE - // since this would hide the underlying failure and other associated problems. - m_sceneGraph.RemoveScenePresence(agentID); - m_clientManager.Remove(agentID); + try + { + // Always clean these structures up so that any failure above doesn't cause them to remain in the + // scene with possibly bad effects (e.g. continually timing out on unacked packets and triggering + // the same cleanup exception continually. + m_sceneGraph.RemoveScenePresence(agentID); + m_clientManager.Remove(agentID); + + avatar.Close(); + } + catch (Exception e) + { + m_log.Error( + string.Format("[SCENE]: Exception in final clean up of {0} in {1}. Exception ", avatar.Name, Name), e); + } } //m_log.InfoFormat("[SCENE] Memory pre GC {0}", System.GC.GetTotalMemory(false)); @@ -3609,7 +3633,7 @@ namespace OpenSim.Region.Framework.Scenes agent.startpos ); - if (LoginsDisabled) + if (!LoginsEnabled) { reason = "Logins Disabled"; return false; @@ -3666,8 +3690,8 @@ namespace OpenSim.Region.Framework.Scenes // We have a zombie from a crashed session. // Or the same user is trying to be root twice here, won't work. // Kill it. - m_log.DebugFormat( - "[SCENE]: Zombie scene presence detected for {0} {1} in {2}", + m_log.WarnFormat( + "[SCENE]: Existing root scene presence detected for {0} {1} in {2} when connecting. Removing existing presence.", sp.Name, sp.UUID, RegionInfo.RegionName); sp.ControllingClient.Close(); @@ -4657,6 +4681,23 @@ namespace OpenSim.Region.Framework.Scenes return m_sceneGraph.GetScenePresence(localID); } + /// + /// Gets all the scene presences in this scene. + /// + /// + /// This method will return both root and child scene presences. + /// + /// Consider using ForEachScenePresence() or ForEachRootScenePresence() if possible since these will not + /// involving creating a new List object. + /// + /// + /// A list of the scene presences. Adding or removing from the list will not affect the presences in the scene. + /// + public List GetScenePresences() + { + return new List(m_sceneGraph.GetScenePresences()); + } + /// /// Performs action on all avatars in the scene (root scene presences) /// Avatars may be an NPC or a 'real' client. @@ -5775,5 +5816,21 @@ Environment.Exit(1); { GC.Collect(); } + + // Wrappers to get physics modules retrieve assets. Has to be done this way + // because we can't assign the asset service to physics directly - at the + // time physics are instantiated it's not registered but it will be by + // the time the first prim exists. + public void PhysicsRequestAsset(UUID assetID, AssetReceivedDelegate callback) + { + AssetService.Get(assetID.ToString(), callback, PhysicsAssetReceived); + } + + private void PhysicsAssetReceived(string id, Object sender, AssetBase asset) + { + AssetReceivedDelegate callback = (AssetReceivedDelegate)sender; + + callback(asset); + } } } diff --git a/OpenSim/Region/Framework/Scenes/SceneBase.cs b/OpenSim/Region/Framework/Scenes/SceneBase.cs index 8db439720c..7c8bd8840c 100644 --- a/OpenSim/Region/Framework/Scenes/SceneBase.cs +++ b/OpenSim/Region/Framework/Scenes/SceneBase.cs @@ -106,6 +106,42 @@ namespace OpenSim.Region.Framework.Scenes protected readonly ClientManager m_clientManager = new ClientManager(); + public bool LoginsEnabled + { + get + { + return m_loginsEnabled; + } + + set + { + if (m_loginsEnabled != value) + { + m_loginsEnabled = value; + EventManager.TriggerRegionLoginsStatusChange(this); + } + } + } + private bool m_loginsEnabled; + + public bool Ready + { + get + { + return m_ready; + } + + set + { + if (m_ready != value) + { + m_ready = value; + EventManager.TriggerRegionReadyStatusChange(this); + } + } + } + private bool m_ready; + public float TimeDilation { get { return 1.0f; } diff --git a/OpenSim/Region/Framework/Scenes/SceneGraph.cs b/OpenSim/Region/Framework/Scenes/SceneGraph.cs index c3d66eb466..e0260e22d1 100644 --- a/OpenSim/Region/Framework/Scenes/SceneGraph.cs +++ b/OpenSim/Region/Framework/Scenes/SceneGraph.cs @@ -848,7 +848,7 @@ namespace OpenSim.Region.Framework.Scenes /// pass a delegate to ForEachScenePresence. /// /// - private List GetScenePresences() + protected internal List GetScenePresences() { return m_scenePresenceArray; } diff --git a/OpenSim/Region/Framework/Scenes/SceneManager.cs b/OpenSim/Region/Framework/Scenes/SceneManager.cs index e3fed493c4..f1b09cac37 100644 --- a/OpenSim/Region/Framework/Scenes/SceneManager.cs +++ b/OpenSim/Region/Framework/Scenes/SceneManager.cs @@ -47,6 +47,48 @@ namespace OpenSim.Region.Framework.Scenes public event RestartSim OnRestartSim; + /// + /// Fired when either all regions are ready for use or at least one region has become unready for use where + /// previously all regions were ready. + /// + public event Action OnRegionsReadyStatusChange; + + /// + /// Are all regions ready for use? + /// + public bool AllRegionsReady + { + get + { + return m_allRegionsReady; + } + + private set + { + if (m_allRegionsReady != value) + { + m_allRegionsReady = value; + Action handler = OnRegionsReadyStatusChange; + if (handler != null) + { + foreach (Action d in handler.GetInvocationList()) + { + try + { + d(this); + } + catch (Exception e) + { + m_log.ErrorFormat("[SCENE MANAGER]: Delegate for OnRegionsReadyStatusChange failed - continuing {0} - {1}", + e.Message, e.StackTrace); + } + } + } + } + } + } + private bool m_allRegionsReady; + private static SceneManager m_instance = null; public static SceneManager Instance { @@ -128,9 +170,11 @@ namespace OpenSim.Region.Framework.Scenes public void Add(Scene scene) { - scene.OnRestart += HandleRestart; + lock (m_localScenes) + m_localScenes.Add(scene.RegionInfo.RegionID, scene.RegionInfo.RegionName, scene); - m_localScenes.Add(scene.RegionInfo.RegionID, scene.RegionInfo.RegionName, scene); + scene.OnRestart += HandleRestart; + scene.EventManager.OnRegionReadyStatusChange += HandleRegionReadyStatusChange; } public void HandleRestart(RegionInfo rdata) @@ -138,12 +182,19 @@ namespace OpenSim.Region.Framework.Scenes m_log.Error("[SCENEMANAGER]: Got Restart message for region:" + rdata.RegionName + " Sending up to main"); int RegionSceneElement = -1; - m_localScenes.Remove(rdata.RegionID); + lock (m_localScenes) + m_localScenes.Remove(rdata.RegionID); // Send signal to main that we're restarting this sim. OnRestartSim(rdata); } + private void HandleRegionReadyStatusChange(IScene scene) + { + lock (m_localScenes) + AllRegionsReady = m_localScenes.FindAll(s => !s.Ready).Count == 0; + } + public void SendSimOnlineNotification(ulong regionHandle) { RegionInfo Result = null; @@ -483,7 +534,8 @@ namespace OpenSim.Region.Framework.Scenes public void CloseScene(Scene scene) { - m_localScenes.Remove(scene.RegionInfo.RegionID); + lock (m_localScenes) + m_localScenes.Remove(scene.RegionInfo.RegionID); scene.Close(); } diff --git a/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs b/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs index 6ab0027d7e..08c7a58519 100644 --- a/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs +++ b/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs @@ -1991,7 +1991,7 @@ namespace OpenSim.Region.Framework.Scenes try { if (!m_scene.ShuttingDown || // if shutting down then there will be nothing to handle the return so leave till next restart - m_scene.LoginsDisabled || // We're starting up or doing maintenance, don't mess with things + !m_scene.LoginsEnabled || // We're starting up or doing maintenance, don't mess with things m_scene.LoadingPrims) // Land may not be valid yet { diff --git a/OpenSim/Region/Framework/Scenes/ScenePresence.cs b/OpenSim/Region/Framework/Scenes/ScenePresence.cs index cdabd45d66..4d3ab517cb 100644 --- a/OpenSim/Region/Framework/Scenes/ScenePresence.cs +++ b/OpenSim/Region/Framework/Scenes/ScenePresence.cs @@ -110,15 +110,10 @@ namespace OpenSim.Region.Framework.Scenes public UUID currentParcelUUID = UUID.Zero; - protected ScenePresenceAnimator m_animator; /// /// The animator for this avatar /// - public ScenePresenceAnimator Animator - { - get { return m_animator; } - private set { m_animator = value; } - } + public ScenePresenceAnimator Animator { get; private set; } /// /// Attachments recorded on this avatar. @@ -2763,8 +2758,7 @@ namespace OpenSim.Region.Framework.Scenes //m_log.DebugFormat("[SCENE PRESENCE] SendAvatarDataToAgent from {0} ({1}) to {2} ({3})", Name, UUID, avatar.Name, avatar.UUID); avatar.ControllingClient.SendAvatarDataImmediate(this); - if (Animator != null) - Animator.SendAnimPackToClient(avatar.ControllingClient); + Animator.SendAnimPackToClient(avatar.ControllingClient); } /// @@ -3440,6 +3434,16 @@ namespace OpenSim.Region.Framework.Scenes if (IsChildAgent) return; + //if ((Math.Abs(Velocity.X) > 0.1e-9f) || (Math.Abs(Velocity.Y) > 0.1e-9f)) + // The Physics Scene will send updates every 500 ms grep: PhysicsActor.SubscribeEvents( + // as of this comment the interval is set in AddToPhysicalScene + +// if (m_updateCount > 0) +// { + Animator.UpdateMovementAnimations(); +// m_updateCount--; +// } + CollisionEventUpdate collisionData = (CollisionEventUpdate)e; Dictionary coldata = collisionData.m_objCollisionList; @@ -3453,7 +3457,7 @@ namespace OpenSim.Region.Framework.Scenes // m_lastColCount = coldata.Count; // } - if (coldata.Count != 0 && Animator != null) + if (coldata.Count != 0) { switch (Animator.CurrentMovementAnimation) { @@ -3565,7 +3569,7 @@ namespace OpenSim.Region.Framework.Scenes ControllingClient.SendHealth(Health); } - public void Close() + protected internal void Close() { // Clear known regions KnownRegions = new Dictionary(); @@ -3581,9 +3585,6 @@ namespace OpenSim.Region.Framework.Scenes // m_reprioritizationTimer.Dispose(); RemoveFromPhysicalScene(); - if(Animator != null) - Animator.Close(); - Animator = null; } public void AddAttachment(SceneObjectGroup gobj) diff --git a/OpenSim/Region/Framework/Scenes/SimStatsReporter.cs b/OpenSim/Region/Framework/Scenes/SimStatsReporter.cs index d6ff5a2890..20919a1982 100644 --- a/OpenSim/Region/Framework/Scenes/SimStatsReporter.cs +++ b/OpenSim/Region/Framework/Scenes/SimStatsReporter.cs @@ -30,7 +30,7 @@ using System.Collections.Generic; using System.Timers; using OpenMetaverse.Packets; using OpenSim.Framework; -using OpenSim.Framework.Statistics; +using OpenSim.Framework.Monitoring; using OpenSim.Region.Framework.Interfaces; namespace OpenSim.Region.Framework.Scenes diff --git a/OpenSim/Region/Framework/Scenes/Tests/ScenePresenceAgentTests.cs b/OpenSim/Region/Framework/Scenes/Tests/ScenePresenceAgentTests.cs index 02c45ef11c..5758869e7a 100644 --- a/OpenSim/Region/Framework/Scenes/Tests/ScenePresenceAgentTests.cs +++ b/OpenSim/Region/Framework/Scenes/Tests/ScenePresenceAgentTests.cs @@ -53,48 +53,83 @@ namespace OpenSim.Region.Framework.Scenes.Tests /// Scene presence tests /// [TestFixture] - public class ScenePresenceAgentTests + public class ScenePresenceAgentTests : OpenSimTestCase { - public Scene scene, scene2, scene3; - public UUID agent1, agent2, agent3; - public static Random random; - public ulong region1,region2,region3; - public AgentCircuitData acd1; - public SceneObjectGroup sog1, sog2, sog3; - public TestClient testclient; +// public Scene scene, scene2, scene3; +// public UUID agent1, agent2, agent3; +// public static Random random; +// public ulong region1, region2, region3; +// public AgentCircuitData acd1; +// public TestClient testclient; - [TestFixtureSetUp] - public void Init() +// [TestFixtureSetUp] +// public void Init() +// { +//// TestHelpers.InMethod(); +//// +//// SceneHelpers sh = new SceneHelpers(); +//// +//// scene = sh.SetupScene("Neighbour x", UUID.Random(), 1000, 1000); +//// scene2 = sh.SetupScene("Neighbour x+1", UUID.Random(), 1001, 1000); +//// scene3 = sh.SetupScene("Neighbour x-1", UUID.Random(), 999, 1000); +//// +//// ISharedRegionModule interregionComms = new LocalSimulationConnectorModule(); +//// interregionComms.Initialise(new IniConfigSource()); +//// interregionComms.PostInitialise(); +//// SceneHelpers.SetupSceneModules(scene, new IniConfigSource(), interregionComms); +//// SceneHelpers.SetupSceneModules(scene2, new IniConfigSource(), interregionComms); +//// SceneHelpers.SetupSceneModules(scene3, new IniConfigSource(), interregionComms); +// +//// agent1 = UUID.Random(); +//// agent2 = UUID.Random(); +//// agent3 = UUID.Random(); +// +//// region1 = scene.RegionInfo.RegionHandle; +//// region2 = scene2.RegionInfo.RegionHandle; +//// region3 = scene3.RegionInfo.RegionHandle; +// } + + [Test] + public void TestCreateRootScenePresence() { TestHelpers.InMethod(); +// TestHelpers.EnableLogging(); - SceneHelpers sh = new SceneHelpers(); + UUID spUuid = TestHelpers.ParseTail(0x1); - scene = sh.SetupScene("Neighbour x", UUID.Random(), 1000, 1000); - scene2 = sh.SetupScene("Neighbour x+1", UUID.Random(), 1001, 1000); - scene3 = sh.SetupScene("Neighbour x-1", UUID.Random(), 999, 1000); + TestScene scene = new SceneHelpers().SetupScene(); + SceneHelpers.AddScenePresence(scene, spUuid); - ISharedRegionModule interregionComms = new LocalSimulationConnectorModule(); - interregionComms.Initialise(new IniConfigSource()); - interregionComms.PostInitialise(); - SceneHelpers.SetupSceneModules(scene, new IniConfigSource(), interregionComms); - SceneHelpers.SetupSceneModules(scene2, new IniConfigSource(), interregionComms); - SceneHelpers.SetupSceneModules(scene3, new IniConfigSource(), interregionComms); + Assert.That(scene.AuthenticateHandler.GetAgentCircuitData(spUuid), Is.Not.Null); + Assert.That(scene.AuthenticateHandler.GetAgentCircuits().Count, Is.EqualTo(1)); - agent1 = UUID.Random(); - agent2 = UUID.Random(); - agent3 = UUID.Random(); - random = new Random(); - sog1 = SceneHelpers.CreateSceneObject(1, agent1); - scene.AddSceneObject(sog1); - sog2 = SceneHelpers.CreateSceneObject(1, agent1); - scene.AddSceneObject(sog2); - sog3 = SceneHelpers.CreateSceneObject(1, agent1); - scene.AddSceneObject(sog3); + ScenePresence sp = scene.GetScenePresence(spUuid); + Assert.That(sp, Is.Not.Null); + Assert.That(sp.IsChildAgent, Is.False); + Assert.That(sp.UUID, Is.EqualTo(spUuid)); - region1 = scene.RegionInfo.RegionHandle; - region2 = scene2.RegionInfo.RegionHandle; - region3 = scene3.RegionInfo.RegionHandle; + Assert.That(scene.GetScenePresences().Count, Is.EqualTo(1)); + } + + [Test] + public void TestCreateDuplicateRootScenePresence() + { + TestHelpers.InMethod(); +// TestHelpers.EnableLogging(); + + UUID spUuid = TestHelpers.ParseTail(0x1); + + TestScene scene = new SceneHelpers().SetupScene(); + SceneHelpers.AddScenePresence(scene, spUuid); + SceneHelpers.AddScenePresence(scene, spUuid); + + Assert.That(scene.AuthenticateHandler.GetAgentCircuitData(spUuid), Is.Not.Null); + Assert.That(scene.AuthenticateHandler.GetAgentCircuits().Count, Is.EqualTo(1)); + + ScenePresence sp = scene.GetScenePresence(spUuid); + Assert.That(sp, Is.Not.Null); + Assert.That(sp.IsChildAgent, Is.False); + Assert.That(sp.UUID, Is.EqualTo(spUuid)); } [Test] @@ -106,9 +141,6 @@ namespace OpenSim.Region.Framework.Scenes.Tests TestScene scene = new SceneHelpers().SetupScene(); ScenePresence sp = SceneHelpers.AddScenePresence(scene, TestHelpers.ParseTail(0x1)); - Assert.That(scene.AuthenticateHandler.GetAgentCircuitData(sp.UUID), Is.Not.Null); - Assert.That(scene.AuthenticateHandler.GetAgentCircuits().Count, Is.EqualTo(1)); - scene.IncomingCloseAgent(sp.UUID); Assert.That(scene.GetScenePresence(sp.UUID), Is.Null); @@ -266,99 +298,99 @@ namespace OpenSim.Region.Framework.Scenes.Tests // but things are synchronous among them. So there should be // 3 threads in here. //[Test] - public void T021_TestCrossToNewRegion() - { - TestHelpers.InMethod(); - - scene.RegisterRegionWithGrid(); - scene2.RegisterRegionWithGrid(); - - // Adding child agent to region 1001 - string reason; - scene2.NewUserConnection(acd1,0, out reason); - scene2.AddNewClient(testclient, PresenceType.User); - - ScenePresence presence = scene.GetScenePresence(agent1); - presence.MakeRootAgent(new Vector3(0,unchecked(Constants.RegionSize-1),0), true); - - ScenePresence presence2 = scene2.GetScenePresence(agent1); - - // Adding neighbour region caps info to presence2 - - string cap = presence.ControllingClient.RequestClientInfo().CapsPath; - presence2.AddNeighbourRegion(region1, cap); - - Assert.That(presence.IsChildAgent, Is.False, "Did not start root in origin region."); - Assert.That(presence2.IsChildAgent, Is.True, "Is not a child on destination region."); - - // Cross to x+1 - presence.AbsolutePosition = new Vector3(Constants.RegionSize+1,3,100); - presence.Update(); - - EventWaitHandle wh = new EventWaitHandle (false, EventResetMode.AutoReset, "Crossing"); - - // Mimicking communication between client and server, by waiting OK from client - // sent by TestClient.CrossRegion call. Originally, this is network comm. - if (!wh.WaitOne(5000,false)) - { - presence.Update(); - if (!wh.WaitOne(8000,false)) - throw new ArgumentException("1 - Timeout waiting for signal/variable."); - } - - // This is a TestClient specific method that fires OnCompleteMovementToRegion event, which - // would normally be fired after receiving the reply packet from comm. done on the last line. - testclient.CompleteMovement(); - - // Crossings are asynchronous - int timer = 10; - - // Make sure cross hasn't already finished - if (!presence.IsInTransit && !presence.IsChildAgent) - { - // If not and not in transit yet, give it some more time - Thread.Sleep(5000); - } - - // Enough time, should at least be in transit by now. - while (presence.IsInTransit && timer > 0) - { - Thread.Sleep(1000); - timer-=1; - } - - Assert.That(timer,Is.GreaterThan(0),"Timed out waiting to cross 2->1."); - Assert.That(presence.IsChildAgent, Is.True, "Did not complete region cross as expected."); - Assert.That(presence2.IsChildAgent, Is.False, "Did not receive root status after receiving agent."); - - // Cross Back - presence2.AbsolutePosition = new Vector3(-10, 3, 100); - presence2.Update(); - - if (!wh.WaitOne(5000,false)) - { - presence2.Update(); - if (!wh.WaitOne(8000,false)) - throw new ArgumentException("2 - Timeout waiting for signal/variable."); - } - testclient.CompleteMovement(); - - if (!presence2.IsInTransit && !presence2.IsChildAgent) - { - // If not and not in transit yet, give it some more time - Thread.Sleep(5000); - } - - // Enough time, should at least be in transit by now. - while (presence2.IsInTransit && timer > 0) - { - Thread.Sleep(1000); - timer-=1; - } - - Assert.That(timer,Is.GreaterThan(0),"Timed out waiting to cross 1->2."); - Assert.That(presence2.IsChildAgent, Is.True, "Did not return from region as expected."); - Assert.That(presence.IsChildAgent, Is.False, "Presence was not made root in old region again."); - } +// public void T021_TestCrossToNewRegion() +// { +// TestHelpers.InMethod(); +// +// scene.RegisterRegionWithGrid(); +// scene2.RegisterRegionWithGrid(); +// +// // Adding child agent to region 1001 +// string reason; +// scene2.NewUserConnection(acd1,0, out reason); +// scene2.AddNewClient(testclient, PresenceType.User); +// +// ScenePresence presence = scene.GetScenePresence(agent1); +// presence.MakeRootAgent(new Vector3(0,unchecked(Constants.RegionSize-1),0), true); +// +// ScenePresence presence2 = scene2.GetScenePresence(agent1); +// +// // Adding neighbour region caps info to presence2 +// +// string cap = presence.ControllingClient.RequestClientInfo().CapsPath; +// presence2.AddNeighbourRegion(region1, cap); +// +// Assert.That(presence.IsChildAgent, Is.False, "Did not start root in origin region."); +// Assert.That(presence2.IsChildAgent, Is.True, "Is not a child on destination region."); +// +// // Cross to x+1 +// presence.AbsolutePosition = new Vector3(Constants.RegionSize+1,3,100); +// presence.Update(); +// +// EventWaitHandle wh = new EventWaitHandle (false, EventResetMode.AutoReset, "Crossing"); +// +// // Mimicking communication between client and server, by waiting OK from client +// // sent by TestClient.CrossRegion call. Originally, this is network comm. +// if (!wh.WaitOne(5000,false)) +// { +// presence.Update(); +// if (!wh.WaitOne(8000,false)) +// throw new ArgumentException("1 - Timeout waiting for signal/variable."); +// } +// +// // This is a TestClient specific method that fires OnCompleteMovementToRegion event, which +// // would normally be fired after receiving the reply packet from comm. done on the last line. +// testclient.CompleteMovement(); +// +// // Crossings are asynchronous +// int timer = 10; +// +// // Make sure cross hasn't already finished +// if (!presence.IsInTransit && !presence.IsChildAgent) +// { +// // If not and not in transit yet, give it some more time +// Thread.Sleep(5000); +// } +// +// // Enough time, should at least be in transit by now. +// while (presence.IsInTransit && timer > 0) +// { +// Thread.Sleep(1000); +// timer-=1; +// } +// +// Assert.That(timer,Is.GreaterThan(0),"Timed out waiting to cross 2->1."); +// Assert.That(presence.IsChildAgent, Is.True, "Did not complete region cross as expected."); +// Assert.That(presence2.IsChildAgent, Is.False, "Did not receive root status after receiving agent."); +// +// // Cross Back +// presence2.AbsolutePosition = new Vector3(-10, 3, 100); +// presence2.Update(); +// +// if (!wh.WaitOne(5000,false)) +// { +// presence2.Update(); +// if (!wh.WaitOne(8000,false)) +// throw new ArgumentException("2 - Timeout waiting for signal/variable."); +// } +// testclient.CompleteMovement(); +// +// if (!presence2.IsInTransit && !presence2.IsChildAgent) +// { +// // If not and not in transit yet, give it some more time +// Thread.Sleep(5000); +// } +// +// // Enough time, should at least be in transit by now. +// while (presence2.IsInTransit && timer > 0) +// { +// Thread.Sleep(1000); +// timer-=1; +// } +// +// Assert.That(timer,Is.GreaterThan(0),"Timed out waiting to cross 1->2."); +// Assert.That(presence2.IsChildAgent, Is.True, "Did not return from region as expected."); +// Assert.That(presence.IsChildAgent, Is.False, "Presence was not made root in old region again."); +// } } } \ No newline at end of file diff --git a/OpenSim/Region/Framework/Scenes/Tests/ScenePresenceTeleportTests.cs b/OpenSim/Region/Framework/Scenes/Tests/ScenePresenceTeleportTests.cs index a407f01d63..37b5184715 100644 --- a/OpenSim/Region/Framework/Scenes/Tests/ScenePresenceTeleportTests.cs +++ b/OpenSim/Region/Framework/Scenes/Tests/ScenePresenceTeleportTests.cs @@ -301,7 +301,7 @@ namespace OpenSim.Region.Framework.Scenes.Tests sp.AbsolutePosition = preTeleportPosition; // Make sceneB refuse CreateAgent - sceneB.LoginsDisabled = true; + sceneB.LoginsEnabled = false; sceneA.RequestTeleportLocation( sp.ControllingClient, diff --git a/OpenSim/Region/OptionalModules/Agent/InternetRelayClientView/Server/IRCClientView.cs b/OpenSim/Region/OptionalModules/Agent/InternetRelayClientView/Server/IRCClientView.cs index 86f33eb463..3b83e58933 100644 --- a/OpenSim/Region/OptionalModules/Agent/InternetRelayClientView/Server/IRCClientView.cs +++ b/OpenSim/Region/OptionalModules/Agent/InternetRelayClientView/Server/IRCClientView.cs @@ -38,6 +38,7 @@ using OpenMetaverse; using OpenMetaverse.Packets; using OpenSim.Framework; using OpenSim.Framework.Client; +using OpenSim.Framework.Monitoring; using OpenSim.Region.Framework.Scenes; namespace OpenSim.Region.OptionalModules.Agent.InternetRelayClientView.Server diff --git a/OpenSim/Region/OptionalModules/Agent/InternetRelayClientView/Server/IRCServer.cs b/OpenSim/Region/OptionalModules/Agent/InternetRelayClientView/Server/IRCServer.cs index a7c5020eb6..9d27386a5a 100644 --- a/OpenSim/Region/OptionalModules/Agent/InternetRelayClientView/Server/IRCServer.cs +++ b/OpenSim/Region/OptionalModules/Agent/InternetRelayClientView/Server/IRCServer.cs @@ -34,6 +34,7 @@ using System.Text; using System.Threading; using log4net; using OpenSim.Framework; +using OpenSim.Framework.Monitoring; using OpenSim.Region.Framework.Scenes; namespace OpenSim.Region.OptionalModules.Agent.InternetRelayClientView.Server diff --git a/OpenSim/Region/OptionalModules/Agent/UDP/Linden/LindenUDPInfoModule.cs b/OpenSim/Region/OptionalModules/Agent/UDP/Linden/LindenUDPInfoModule.cs index a7ebeccdc9..5fe594860d 100644 --- a/OpenSim/Region/OptionalModules/Agent/UDP/Linden/LindenUDPInfoModule.cs +++ b/OpenSim/Region/OptionalModules/Agent/UDP/Linden/LindenUDPInfoModule.cs @@ -35,7 +35,7 @@ using Nini.Config; using OpenMetaverse; using OpenSim.Framework; using OpenSim.Framework.Console; -using OpenSim.Framework.Statistics; +using OpenSim.Framework.Monitoring; using OpenSim.Region.ClientStack.LindenUDP; using OpenSim.Region.Framework.Interfaces; using OpenSim.Region.Framework.Scenes; @@ -81,18 +81,6 @@ namespace OpenSim.Region.CoreModules.UDP.Linden lock (m_scenes) m_scenes[scene.RegionInfo.RegionID] = scene; - scene.AddCommand( - "Comms", this, "image queues clear", - "image queues clear ", - "Clear the image queues (textures downloaded via UDP) for a particular client.", - (mod, cmd) => MainConsole.Instance.Output(HandleImageQueuesClear(cmd))); - - scene.AddCommand( - "Comms", this, "image queues show", - "image queues show ", - "Show the image queues (textures downloaded via UDP) for a particular client.", - (mod, cmd) => MainConsole.Instance.Output(GetImageQueuesReport(cmd))); - scene.AddCommand( "Comms", this, "show pqueues", "show pqueues [full]", @@ -105,8 +93,15 @@ namespace OpenSim.Region.CoreModules.UDP.Linden "Comms", this, "show queues", "show queues [full]", "Show queue data for each client", - "Without the 'full' option, only root agents are shown." - + " With the 'full' option child agents are also shown.", + "Without the 'full' option, only root agents are shown.\n" + + "With the 'full' option child agents are also shown.\n\n" + + "Type - Rt is a root (avatar) client whilst cd is a child (neighbour interacting) client.\n" + + "Since Last In - Time in milliseconds since last packet received.\n" + + "Pkts In - Number of packets processed from the client.\n" + + "Pkts Out - Number of packets sent to the client.\n" + + "Pkts Resent - Number of packets resent to the client.\n" + + "Bytes Unacked - Number of bytes transferred to the client that are awaiting acknowledgement.\n" + + "Q Pkts * - Number of packets of various types (land, wind, etc.) to be sent to the client that are waiting for available bandwidth.\n", (mod, cmd) => MainConsole.Instance.Output(GetQueuesReport(cmd))); scene.AddCommand( @@ -114,6 +109,12 @@ namespace OpenSim.Region.CoreModules.UDP.Linden "show image queues ", "Show the image queues (textures downloaded via UDP) for a particular client.", (mod, cmd) => MainConsole.Instance.Output(GetImageQueuesReport(cmd))); + + scene.AddCommand( + "Comms", this, "clear image queues", + "clear image queues ", + "Clear the image queues (textures downloaded via UDP) for a particular client.", + (mod, cmd) => MainConsole.Instance.Output(HandleImageQueuesClear(cmd))); scene.AddCommand( "Comms", this, "show throttles", @@ -373,17 +374,22 @@ namespace OpenSim.Region.CoreModules.UDP.Linden int maxNameLength = 18; int maxRegionNameLength = 14; int maxTypeLength = 4; - int totalInfoFieldsLength = maxNameLength + columnPadding + maxRegionNameLength + columnPadding + maxTypeLength + columnPadding; + + int totalInfoFieldsLength + = maxNameLength + columnPadding + + maxRegionNameLength + columnPadding + + maxTypeLength + columnPadding; report.Append(GetColumnEntry("User", maxNameLength, columnPadding)); report.Append(GetColumnEntry("Region", maxRegionNameLength, columnPadding)); report.Append(GetColumnEntry("Type", maxTypeLength, columnPadding)); report.AppendFormat( - "{0,7} {1,7} {2,7} {3,9} {4,7} {5,7} {6,7} {7,7} {8,7} {9,8} {10,7} {11,7}\n", + "{0,7} {1,7} {2,7} {3,7} {4,9} {5,7} {6,7} {7,7} {8,7} {9,7} {10,8} {11,7} {12,7}\n", + "Since", + "Pkts", "Pkts", "Pkts", - "Pkts", "Bytes", "Q Pkts", "Q Pkts", @@ -396,7 +402,8 @@ namespace OpenSim.Region.CoreModules.UDP.Linden report.AppendFormat("{0,-" + totalInfoFieldsLength + "}", ""); report.AppendFormat( - "{0,7} {1,7} {2,7} {3,9} {4,7} {5,7} {6,7} {7,7} {8,7} {9,8} {10,7} {11,7}\n", + "{0,7} {1,7} {2,7} {3,7} {4,9} {5,7} {6,7} {7,7} {8,7} {9,7} {10,8} {11,7} {12,7}\n", + "Last In", "In", "Out", "Resent", @@ -417,22 +424,22 @@ namespace OpenSim.Region.CoreModules.UDP.Linden scene.ForEachClient( delegate(IClientAPI client) { + bool isChild = client.SceneAgent.IsChildAgent; + if (isChild && !showChildren) + return; + + string name = client.Name; + if (pname != "" && name != pname) + return; + + string regionName = scene.RegionInfo.RegionName; + + report.Append(GetColumnEntry(name, maxNameLength, columnPadding)); + report.Append(GetColumnEntry(regionName, maxRegionNameLength, columnPadding)); + report.Append(GetColumnEntry(isChild ? "Cd" : "Rt", maxTypeLength, columnPadding)); + if (client is IStatsCollector) { - bool isChild = client.SceneAgent.IsChildAgent; - if (isChild && !showChildren) - return; - - string name = client.Name; - if (pname != "" && name != pname) - return; - - string regionName = scene.RegionInfo.RegionName; - - report.Append(GetColumnEntry(name, maxNameLength, columnPadding)); - report.Append(GetColumnEntry(regionName, maxRegionNameLength, columnPadding)); - report.Append(GetColumnEntry(isChild ? "Cd" : "Rt", maxTypeLength, columnPadding)); - IStatsCollector stats = (IStatsCollector)client; report.AppendLine(stats.Report()); diff --git a/OpenSim/Region/OptionalModules/Avatar/Appearance/AppearanceInfoModule.cs b/OpenSim/Region/OptionalModules/Avatar/Appearance/AppearanceInfoModule.cs index 6bb6729c83..d718a2f052 100644 --- a/OpenSim/Region/OptionalModules/Avatar/Appearance/AppearanceInfoModule.cs +++ b/OpenSim/Region/OptionalModules/Avatar/Appearance/AppearanceInfoModule.cs @@ -36,7 +36,7 @@ using Nini.Config; using OpenMetaverse; using OpenSim.Framework; using OpenSim.Framework.Console; -using OpenSim.Framework.Statistics; +using OpenSim.Framework.Monitoring; using OpenSim.Region.ClientStack.LindenUDP; using OpenSim.Region.Framework.Interfaces; using OpenSim.Region.Framework.Scenes; diff --git a/OpenSim/Region/OptionalModules/Avatar/Attachments/AttachmentsCommandModule.cs b/OpenSim/Region/OptionalModules/Avatar/Attachments/AttachmentsCommandModule.cs index 1b9e3ace5c..d68aabc5ca 100644 --- a/OpenSim/Region/OptionalModules/Avatar/Attachments/AttachmentsCommandModule.cs +++ b/OpenSim/Region/OptionalModules/Avatar/Attachments/AttachmentsCommandModule.cs @@ -36,7 +36,7 @@ using Nini.Config; using OpenMetaverse; using OpenSim.Framework; using OpenSim.Framework.Console; -using OpenSim.Framework.Statistics; +using OpenSim.Framework.Monitoring; using OpenSim.Region.ClientStack.LindenUDP; using OpenSim.Region.Framework.Interfaces; using OpenSim.Region.Framework.Scenes; diff --git a/OpenSim/Region/OptionalModules/Avatar/Chat/IRCConnector.cs b/OpenSim/Region/OptionalModules/Avatar/Chat/IRCConnector.cs index cd401a6096..ca956fbe91 100644 --- a/OpenSim/Region/OptionalModules/Avatar/Chat/IRCConnector.cs +++ b/OpenSim/Region/OptionalModules/Avatar/Chat/IRCConnector.cs @@ -37,6 +37,7 @@ using OpenMetaverse; using log4net; using Nini.Config; using OpenSim.Framework; +using OpenSim.Framework.Monitoring; using OpenSim.Region.Framework.Interfaces; using OpenSim.Region.Framework.Scenes; diff --git a/OpenSim/Region/OptionalModules/Avatar/Friends/FriendsCommandsModule.cs b/OpenSim/Region/OptionalModules/Avatar/Friends/FriendsCommandsModule.cs index 2602050de4..4e84364dd3 100644 --- a/OpenSim/Region/OptionalModules/Avatar/Friends/FriendsCommandsModule.cs +++ b/OpenSim/Region/OptionalModules/Avatar/Friends/FriendsCommandsModule.cs @@ -37,7 +37,7 @@ using Nini.Config; using OpenMetaverse; using OpenSim.Framework; using OpenSim.Framework.Console; -using OpenSim.Framework.Statistics; +using OpenSim.Framework.Monitoring; using OpenSim.Region.ClientStack.LindenUDP; using OpenSim.Region.CoreModules.Avatar.Friends; using OpenSim.Region.Framework.Interfaces; diff --git a/OpenSim/Region/OptionalModules/PhysicsParameters/PhysicsParameters.cs b/OpenSim/Region/OptionalModules/PhysicsParameters/PhysicsParameters.cs index e45212444d..40f7fbc59a 100755 --- a/OpenSim/Region/OptionalModules/PhysicsParameters/PhysicsParameters.cs +++ b/OpenSim/Region/OptionalModules/PhysicsParameters/PhysicsParameters.cs @@ -47,7 +47,7 @@ namespace OpenSim.Region.OptionalModules.PhysicsParameters [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "PhysicsParameters")] public class PhysicsParameters : ISharedRegionModule { - private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); +// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); // private static string LogHeader = "[PHYSICS PARAMETERS]"; private List m_scenes = new List(); diff --git a/OpenSim/Region/OptionalModules/Scripting/RegionReadyModule/RegionReadyModule.cs b/OpenSim/Region/OptionalModules/Scripting/RegionReadyModule/RegionReadyModule.cs index 600cafb7c4..fff3a32010 100644 --- a/OpenSim/Region/OptionalModules/Scripting/RegionReadyModule/RegionReadyModule.cs +++ b/OpenSim/Region/OptionalModules/Scripting/RegionReadyModule/RegionReadyModule.cs @@ -31,16 +31,14 @@ using System.Reflection; using System.Net; using System.IO; using System.Text; - using log4net; using Nini.Config; using OpenMetaverse; using OpenMetaverse.StructuredData; -using OpenSim.Services.Interfaces; - using OpenSim.Framework; using OpenSim.Region.Framework.Interfaces; using OpenSim.Region.Framework.Scenes; +using OpenSim.Services.Interfaces; namespace OpenSim.Region.OptionalModules.Scripting.RegionReady { @@ -50,16 +48,15 @@ namespace OpenSim.Region.OptionalModules.Scripting.RegionReady LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); private IConfig m_config = null; - private bool m_ScriptRez; private bool m_firstEmptyCompileQueue; private bool m_oarFileLoading; private bool m_lastOarLoadedOk; private int m_channelNotify = -1000; private bool m_enabled = false; - private bool m_disable_logins = false; + private bool m_disable_logins; private string m_uri = string.Empty; - Scene m_scene = null; + Scene m_scene; #region INonSharedRegionModule interface @@ -93,53 +90,40 @@ namespace OpenSim.Region.OptionalModules.Scripting.RegionReady m_scene.RegisterModuleInterface(this); - m_ScriptRez = false; m_firstEmptyCompileQueue = true; m_oarFileLoading = false; m_lastOarLoadedOk = true; m_scene.EventManager.OnOarFileLoaded += OnOarFileLoaded; - m_scene.EventManager.OnRezScript += OnRezScript; - m_scene.EventManager.OnLoginsEnabled += OnLoginsEnabled; m_log.DebugFormat("[RegionReady]: Enabled for region {0}", scene.RegionInfo.RegionName); - if (m_disable_logins == true) + if (m_disable_logins) { - scene.LoginLock = true; - scene.LoginsDisabled = true; - m_log.InfoFormat("[RegionReady]: Region {0} - logins disabled during initialization.",m_scene.RegionInfo.RegionName); + m_scene.LoginLock = true; + m_scene.EventManager.OnEmptyScriptCompileQueue += OnEmptyScriptCompileQueue; - if(m_uri != string.Empty) + m_log.InfoFormat("[RegionReady]: Region {0} - LOGINS DISABLED DURING INITIALIZATION.", m_scene.Name); + + if (m_uri != string.Empty) { RRAlert("disabled"); } } } - void OnRezScript (uint localID, UUID itemID, string script, int startParam, bool postOnRez, string engine, int stateSource) - { - if (!m_ScriptRez) - { - m_ScriptRez = true; - m_scene.EventManager.OnEmptyScriptCompileQueue += OnEmptyScriptCompileQueue; - m_scene.EventManager.OnRezScript -= OnRezScript; - } - } - public void RemoveRegion(Scene scene) { if (!m_enabled) return; - m_scene.EventManager.OnEmptyScriptCompileQueue -= OnEmptyScriptCompileQueue; m_scene.EventManager.OnOarFileLoaded -= OnOarFileLoaded; - m_scene.EventManager.OnLoginsEnabled -= OnLoginsEnabled; - if(m_uri != string.Empty) - { + if (m_disable_logins) + m_scene.EventManager.OnEmptyScriptCompileQueue -= OnEmptyScriptCompileQueue; + + if (m_uri != string.Empty) RRAlert("shutdown"); - } m_scene = null; } @@ -159,7 +143,6 @@ namespace OpenSim.Region.OptionalModules.Scripting.RegionReady #endregion - void OnEmptyScriptCompileQueue(int numScriptsFailed, string message) { m_log.DebugFormat("[RegionReady]: Script compile queue empty!"); @@ -193,75 +176,80 @@ namespace OpenSim.Region.OptionalModules.Scripting.RegionReady m_scene.RegionInfo.RegionName, c.Message, m_channelNotify); m_scene.EventManager.TriggerOnChatBroadcast(this, c); - m_scene.EventManager.TriggerLoginsEnabled(m_scene.RegionInfo.RegionName); - m_scene.SceneGridService.InformNeighborsThatRegionisUp(m_scene.RequestModuleInterface(), m_scene.RegionInfo); + + TriggerRegionReady(m_scene); } } void OnOarFileLoaded(Guid requestId, string message) { m_oarFileLoading = true; + if (message==String.Empty) { m_lastOarLoadedOk = true; - } else { + } + else + { m_log.WarnFormat("[RegionReady]: Oar file load errors: {0}", message); m_lastOarLoadedOk = false; } } - // This will be triggerd by Scene if we have no scripts - // m_ScriptsRezzing will be false if there were none - // else it will be true and we should wait on the - // empty compile queue - void OnLoginsEnabled(string regionName) + /// + /// This will be triggered by Scene directly if it contains no scripts on startup. Otherwise it is triggered + /// when the script compile queue is empty after initial region startup. + /// + /// + public void TriggerRegionReady(IScene scene) { - if (m_disable_logins == true) + m_scene.EventManager.OnEmptyScriptCompileQueue -= OnEmptyScriptCompileQueue; + m_scene.LoginLock = false; + + if (!m_scene.StartDisabled) { - if (m_scene.StartDisabled == false) - { - m_scene.LoginsDisabled = false; - m_scene.LoginLock = false; + m_scene.LoginsEnabled = true; - m_scene.EventManager.OnEmptyScriptCompileQueue -= OnEmptyScriptCompileQueue; + // m_log.InfoFormat("[RegionReady]: Logins enabled for {0}, Oar {1}", + // m_scene.RegionInfo.RegionName, m_oarFileLoading.ToString()); - // m_log.InfoFormat("[RegionReady]: Logins enabled for {0}, Oar {1}", - // m_scene.RegionInfo.RegionName, m_oarFileLoading.ToString()); - - m_log.InfoFormat( - "[RegionReady]: INITIALIZATION COMPLETE FOR {0} - LOGINS ENABLED", m_scene.Name); - - if (m_uri != string.Empty) - { - RRAlert("enabled"); - } - } + m_log.InfoFormat( + "[RegionReady]: INITIALIZATION COMPLETE FOR {0} - LOGINS ENABLED", m_scene.Name); } + + m_scene.SceneGridService.InformNeighborsThatRegionisUp( + m_scene.RequestModuleInterface(), m_scene.RegionInfo); + + if (m_uri != string.Empty) + { + RRAlert("enabled"); + } + + m_scene.Ready = true; } public void OarLoadingAlert(string msg) { // Let's bypass this for now until some better feedback can be established // - return; - if (msg == "load") - { - m_scene.EventManager.OnEmptyScriptCompileQueue += OnEmptyScriptCompileQueue; - m_scene.EventManager.OnOarFileLoaded += OnOarFileLoaded; - m_scene.EventManager.OnLoginsEnabled += OnLoginsEnabled; - m_scene.EventManager.OnRezScript += OnRezScript; - m_oarFileLoading = true; - m_firstEmptyCompileQueue = true; - - m_scene.LoginsDisabled = true; - m_scene.LoginLock = true; - if ( m_uri != string.Empty ) - { - RRAlert("loading oar"); - RRAlert("disabled"); - } - } +// if (msg == "load") +// { +// m_scene.EventManager.OnEmptyScriptCompileQueue += OnEmptyScriptCompileQueue; +// m_scene.EventManager.OnOarFileLoaded += OnOarFileLoaded; +// m_scene.EventManager.OnLoginsEnabled += OnLoginsEnabled; +// m_scene.EventManager.OnRezScript += OnRezScript; +// m_oarFileLoading = true; +// m_firstEmptyCompileQueue = true; +// +// m_scene.LoginsDisabled = true; +// m_scene.LoginLock = true; +// if ( m_uri != string.Empty ) +// { +// RRAlert("loading oar"); +// RRAlert("disabled"); +// } +// } } public void RRAlert(string status) diff --git a/OpenSim/Region/OptionalModules/Scripting/ScriptModuleComms/ScriptModuleCommsModule.cs b/OpenSim/Region/OptionalModules/Scripting/ScriptModuleComms/ScriptModuleCommsModule.cs index 74a85e2cd5..705a84700d 100644 --- a/OpenSim/Region/OptionalModules/Scripting/ScriptModuleComms/ScriptModuleCommsModule.cs +++ b/OpenSim/Region/OptionalModules/Scripting/ScriptModuleComms/ScriptModuleCommsModule.cs @@ -46,6 +46,8 @@ namespace OpenSim.Region.OptionalModules.Scripting.ScriptModuleComms private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + private Dictionary m_constants = new Dictionary(); + #region ScriptInvocation protected class ScriptInvocationData { @@ -269,6 +271,37 @@ namespace OpenSim.Region.OptionalModules.Scripting.ScriptModuleComms Delegate fn = LookupScriptInvocation(fname); return fn.DynamicInvoke(olist.ToArray()); } + + /// + /// Operation to for a region module to register a constant to be used + /// by the script engine + /// + public void RegisterConstant(string cname, object value) + { + m_log.DebugFormat("[MODULE COMMANDS] register constant <{0}> with value {1}",cname,value.ToString()); + lock (m_constants) + { + m_constants.Add(cname,value); + } + } + + /// + /// Operation to check for a registered constant + /// + public object LookupModConstant(string cname) + { + // m_log.DebugFormat("[MODULE COMMANDS] lookup constant <{0}>",cname); + + lock (m_constants) + { + object value = null; + if (m_constants.TryGetValue(cname,out value)) + return value; + } + + return null; + } + #endregion } diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs b/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs index dc0c0083f8..09e1f0c022 100644 --- a/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs @@ -41,7 +41,7 @@ public class BSCharacter : PhysicsActor private BSScene _scene; private String _avName; - private bool _stopped; + // private bool _stopped; private Vector3 _size; private Vector3 _scale; private PrimitiveBaseShape _pbs; @@ -134,9 +134,9 @@ public class BSCharacter : PhysicsActor { base.RequestPhysicsterseUpdate(); } - + // No one calls this method so I don't know what it could possibly mean public override bool Stopped { - get { return _stopped; } + get { return false; } } public override Vector3 Size { get { return _size; } @@ -391,52 +391,47 @@ public class BSCharacter : PhysicsActor _mass = _density * _avatarVolume; } - // Set to 'true' if the individual changed items should be checked - // (someday RequestPhysicsTerseUpdate() will take a bitmap of changed properties) - const bool SHOULD_CHECK_FOR_INDIVIDUAL_CHANGES = false; - // The physics engine says that properties have updated. Update same and inform // the world that things have changed. public void UpdateProperties(EntityProperties entprop) { + /* bool changed = false; - if (SHOULD_CHECK_FOR_INDIVIDUAL_CHANGES) { - // we assign to the local variables so the normal set action does not happen - if (_position != entprop.Position) { - _position = entprop.Position; - changed = true; - } - if (_orientation != entprop.Rotation) { - _orientation = entprop.Rotation; - changed = true; - } - if (_velocity != entprop.Velocity) { - _velocity = entprop.Velocity; - changed = true; - } - if (_acceleration != entprop.Acceleration) { - _acceleration = entprop.Acceleration; - changed = true; - } - if (_rotationalVelocity != entprop.RotationalVelocity) { - _rotationalVelocity = entprop.RotationalVelocity; - changed = true; - } - if (changed) { - // m_log.DebugFormat("{0}: UpdateProperties: id={1}, c={2}, pos={3}, rot={4}", LogHeader, LocalID, changed, _position, _orientation); - // Avatar movement is not done by generating this event. There is code in the heartbeat - // loop that updates avatars. - // base.RequestPhysicsterseUpdate(); - } - } - else { + // we assign to the local variables so the normal set action does not happen + if (_position != entprop.Position) { _position = entprop.Position; + changed = true; + } + if (_orientation != entprop.Rotation) { _orientation = entprop.Rotation; + changed = true; + } + if (_velocity != entprop.Velocity) { _velocity = entprop.Velocity; + changed = true; + } + if (_acceleration != entprop.Acceleration) { _acceleration = entprop.Acceleration; + changed = true; + } + if (_rotationalVelocity != entprop.RotationalVelocity) { _rotationalVelocity = entprop.RotationalVelocity; + changed = true; + } + if (changed) { + // m_log.DebugFormat("{0}: UpdateProperties: id={1}, c={2}, pos={3}, rot={4}", LogHeader, LocalID, changed, _position, _orientation); + // Avatar movement is not done by generating this event. There is code in the heartbeat + // loop that updates avatars. // base.RequestPhysicsterseUpdate(); } + */ + _position = entprop.Position; + _orientation = entprop.Rotation; + _velocity = entprop.Velocity; + _acceleration = entprop.Acceleration; + _rotationalVelocity = entprop.RotationalVelocity; + // Avatars don't report theirr changes the usual way. Changes are checked for in the heartbeat loop. + // base.RequestPhysicsterseUpdate(); } // Called by the scene when a collision with this object is reported diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSConstraint.cs b/OpenSim/Region/Physics/BulletSPlugin/BSConstraint.cs new file mode 100755 index 0000000000..ea3093a96b --- /dev/null +++ b/OpenSim/Region/Physics/BulletSPlugin/BSConstraint.cs @@ -0,0 +1,125 @@ +/* + * 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 copyrightD + * 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.Text; +using OpenMetaverse; + +namespace OpenSim.Region.Physics.BulletSPlugin +{ + +public class BSConstraint : IDisposable +{ + private BulletSim m_world; + private BulletBody m_body1; + private BulletBody m_body2; + private BulletConstraint m_constraint; + private bool m_enabled = false; + + public BSConstraint(BulletSim world, BulletBody obj1, BulletBody obj2, + Vector3 frame1, Quaternion frame1rot, + Vector3 frame2, Quaternion frame2rot + ) + { + m_world = world; + m_body1 = obj1; + m_body2 = obj2; + m_constraint = new BulletConstraint(BulletSimAPI.CreateConstraint2(m_world.Ptr, m_body1.Ptr, m_body2.Ptr, + frame1, frame1rot, + frame2, frame2rot, + true /*useLinearReferenceFrameA*/, true /*disableCollisionsBetweenLinkedBodies*/)); + m_enabled = true; + } + + public void Dispose() + { + if (m_enabled) + { + // BulletSimAPI.RemoveConstraint(m_world.ID, m_body1.ID, m_body2.ID); + BulletSimAPI.DestroyConstraint2(m_world.Ptr, m_constraint.Ptr); + m_enabled = false; + } + } + + public BulletBody Body1 { get { return m_body1; } } + public BulletBody Body2 { get { return m_body2; } } + + public bool SetLinearLimits(Vector3 low, Vector3 high) + { + bool ret = false; + if (m_enabled) + ret = BulletSimAPI.SetLinearLimits2(m_constraint.Ptr, low, high); + return ret; + } + + public bool SetAngularLimits(Vector3 low, Vector3 high) + { + bool ret = false; + if (m_enabled) + ret = BulletSimAPI.SetAngularLimits2(m_constraint.Ptr, low, high); + return ret; + } + + public bool SetCFMAndERP(float cfm, float erp) + { + bool ret = true; + BulletSimAPI.SetConstraintParam2(m_constraint.Ptr, ConstraintParams.BT_CONSTRAINT_STOP_CFM, cfm, ConstraintParamAxis.AXIS_ALL); + BulletSimAPI.SetConstraintParam2(m_constraint.Ptr, ConstraintParams.BT_CONSTRAINT_STOP_ERP, erp, ConstraintParamAxis.AXIS_ALL); + BulletSimAPI.SetConstraintParam2(m_constraint.Ptr, ConstraintParams.BT_CONSTRAINT_CFM, cfm, ConstraintParamAxis.AXIS_ALL); + return ret; + } + + public bool UseFrameOffset(bool useOffset) + { + bool ret = false; + float onOff = useOffset ? ConfigurationParameters.numericTrue : ConfigurationParameters.numericFalse; + if (m_enabled) + ret = BulletSimAPI.UseFrameOffset2(m_constraint.Ptr, onOff); + return ret; + } + + public bool TranslationalLimitMotor(bool enable, float targetVelocity, float maxMotorForce) + { + bool ret = false; + float onOff = enable ? ConfigurationParameters.numericTrue : ConfigurationParameters.numericFalse; + if (m_enabled) + ret = BulletSimAPI.TranslationalLimitMotor2(m_constraint.Ptr, onOff, targetVelocity, maxMotorForce); + return ret; + } + + public bool CalculateTransforms() + { + bool ret = false; + if (m_enabled) + { + BulletSimAPI.CalculateTransforms2(m_constraint.Ptr); + ret = true; + } + return ret; + } +} +} diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSConstraintCollection.cs b/OpenSim/Region/Physics/BulletSPlugin/BSConstraintCollection.cs new file mode 100755 index 0000000000..c88e6455b9 --- /dev/null +++ b/OpenSim/Region/Physics/BulletSPlugin/BSConstraintCollection.cs @@ -0,0 +1,184 @@ +/* + * 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 copyrightD + * 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.Text; +using log4net; +using OpenMetaverse; + +namespace OpenSim.Region.Physics.BulletSPlugin +{ + +public class BSConstraintCollection : IDisposable +{ + // private static readonly ILog m_log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + // private static readonly string LogHeader = "[CONSTRAINT COLLECTION]"; + + delegate bool ConstraintAction(BSConstraint constrain); + + private List m_constraints; + private BulletSim m_world; + + public BSConstraintCollection(BulletSim world) + { + m_world = world; + m_constraints = new List(); + } + + public void Dispose() + { + this.Clear(); + } + + public void Clear() + { + foreach (BSConstraint cons in m_constraints) + { + cons.Dispose(); + } + m_constraints.Clear(); + } + + public BSConstraint CreateConstraint(BulletSim world, BulletBody obj1, BulletBody obj2, + Vector3 frame1, Quaternion frame1rot, + Vector3 frame2, Quaternion frame2rot) + { + BSConstraint constrain = new BSConstraint(world, obj1, obj2, frame1, frame1rot, frame2, frame2rot); + + this.AddConstraint(constrain); + return constrain; + } + + public bool AddConstraint(BSConstraint cons) + { + // There is only one constraint between any bodies. Remove any old just to make sure. + RemoveAndDestroyConstraint(cons.Body1, cons.Body2); + + m_constraints.Add(cons); + + return true; + } + + // Get the constraint between two bodies. There can be only one. + // Return 'true' if a constraint was found. + public bool TryGetConstraint(BulletBody body1, BulletBody body2, out BSConstraint returnConstraint) + { + bool found = false; + BSConstraint foundConstraint = null; + + uint lookingID1 = body1.ID; + uint lookingID2 = body2.ID; + ForEachConstraint(delegate(BSConstraint constrain) + { + if ((constrain.Body1.ID == lookingID1 && constrain.Body2.ID == lookingID2) + || (constrain.Body1.ID == lookingID2 && constrain.Body2.ID == lookingID1)) + { + foundConstraint = constrain; + found = true; + } + return found; + }); + returnConstraint = foundConstraint; + return found; + } + + // Remove any constraint between the passed bodies. + // Presumed there is only one such constraint possible. + // Return 'true' if a constraint was found and destroyed. + public bool RemoveAndDestroyConstraint(BulletBody body1, BulletBody body2) + { + // return BulletSimAPI.RemoveConstraint(m_world.ID, obj1.ID, obj2.ID); + + bool ret = false; + BSConstraint constrain; + + if (this.TryGetConstraint(body1, body2, out constrain)) + { + // remove the constraint from our collection + m_constraints.Remove(constrain); + // tell the engine that all its structures need to be freed + constrain.Dispose(); + // we destroyed something + ret = true; + } + + return ret; + } + + // Remove all constraints that reference the passed body. + // Return 'true' if any constraints were destroyed. + public bool RemoveAndDestroyConstraint(BulletBody body1) + { + // return BulletSimAPI.RemoveConstraintByID(m_world.ID, obj.ID); + + List toRemove = new List(); + uint lookingID = body1.ID; + ForEachConstraint(delegate(BSConstraint constrain) + { + if (constrain.Body1.ID == lookingID || constrain.Body2.ID == lookingID) + { + toRemove.Add(constrain); + } + return false; + }); + lock (m_constraints) + { + foreach (BSConstraint constrain in toRemove) + { + m_constraints.Remove(constrain); + constrain.Dispose(); + } + } + return (toRemove.Count > 0); + } + + public bool RecalculateAllConstraints() + { + foreach (BSConstraint constrain in m_constraints) + { + constrain.CalculateTransforms(); + } + return true; + } + + // Lock the constraint list and loop through it. + // The constraint action returns 'true' if it wants the loop aborted. + private void ForEachConstraint(ConstraintAction action) + { + lock (m_constraints) + { + foreach (BSConstraint constrain in m_constraints) + { + if (action(constrain)) + break; + } + } + } + + +} +} diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs b/OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs index eb20eb3daa..c197e61ee8 100644 --- a/OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs @@ -57,7 +57,6 @@ namespace OpenSim.Region.Physics.BulletSPlugin private int frcount = 0; // Used to limit dynamics debug output to // every 100th frame - // private BSScene m_parentScene = null; private BSPrim m_prim; // the prim this dynamic controller belongs to // Vehicle properties @@ -131,8 +130,9 @@ namespace OpenSim.Region.Physics.BulletSPlugin m_type = Vehicle.TYPE_NONE; } - internal void ProcessFloatVehicleParam(Vehicle pParam, float pValue) + internal void ProcessFloatVehicleParam(Vehicle pParam, float pValue, float timestep) { + DetailLog("{0},ProcessFloatVehicleParam,param={1},val={2}", m_prim.LocalID, pParam, pValue); switch (pParam) { case Vehicle.ANGULAR_DEFLECTION_EFFICIENCY: @@ -229,8 +229,9 @@ namespace OpenSim.Region.Physics.BulletSPlugin } }//end ProcessFloatVehicleParam - internal void ProcessVectorVehicleParam(Vehicle pParam, Vector3 pValue) + internal void ProcessVectorVehicleParam(Vehicle pParam, Vector3 pValue, float timestep) { + DetailLog("{0},ProcessVectorVehicleParam,param={1},val={2}", m_prim.LocalID, pParam, pValue); switch (pParam) { case Vehicle.ANGULAR_FRICTION_TIMESCALE: @@ -265,6 +266,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin internal void ProcessRotationVehicleParam(Vehicle pParam, Quaternion pValue) { + DetailLog("{0},ProcessRotationalVehicleParam,param={1},val={2}", m_prim.LocalID, pParam, pValue); switch (pParam) { case Vehicle.REFERENCE_FRAME: @@ -278,6 +280,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin internal void ProcessVehicleFlags(int pParam, bool remove) { + DetailLog("{0},ProcessVehicleFlags,param={1},remove={2}", m_prim.LocalID, pParam, remove); if (remove) { if (pParam == -1) @@ -434,6 +437,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin internal void ProcessTypeChange(Vehicle pType) { + DetailLog("{0},ProcessTypeChange,type={1}", m_prim.LocalID, pType); // Set Defaults For Type m_type = pType; switch (pType) @@ -594,11 +598,10 @@ namespace OpenSim.Region.Physics.BulletSPlugin m_flags |= (VehicleFlag.LIMIT_ROLL_ONLY); m_Hoverflags |= (VehicleFlag.HOVER_GLOBAL_HEIGHT); break; - } }//end SetDefaultsForType - internal void Step(float pTimestep, BSScene pParentScene) + internal void Step(float pTimestep) { if (m_type == Vehicle.TYPE_NONE) return; @@ -606,21 +609,34 @@ namespace OpenSim.Region.Physics.BulletSPlugin if (frcount > 100) frcount = 0; - MoveLinear(pTimestep, pParentScene); + MoveLinear(pTimestep); MoveAngular(pTimestep); LimitRotation(pTimestep); + + DetailLog("{0},Dynamics,done,pos={1},force={2},velocity={3},angvel={4}", + m_prim.LocalID, m_prim.Position, m_prim.Force, m_prim.Velocity, m_prim.RotationalVelocity); }// end Step - private void MoveLinear(float pTimestep, BSScene _pParentScene) + private void MoveLinear(float pTimestep) { - if (!m_linearMotorDirection.ApproxEquals(Vector3.Zero, 0.01f)) // requested m_linearMotorDirection is significant + // requested m_linearMotorDirection is significant + // if (!m_linearMotorDirection.ApproxEquals(Vector3.Zero, 0.01f)) + if (m_linearMotorDirection.LengthSquared() > 0.0001f) { + Vector3 origDir = m_linearMotorDirection; + Vector3 origVel = m_lastLinearVelocityVector; + // add drive to body - Vector3 addAmount = m_linearMotorDirection/(m_linearMotorTimescale/pTimestep); - m_lastLinearVelocityVector += (addAmount*10); // lastLinearVelocityVector is the current body velocity vector? + // Vector3 addAmount = m_linearMotorDirection/(m_linearMotorTimescale/pTimestep); + Vector3 addAmount = m_linearMotorDirection/(m_linearMotorTimescale); + // lastLinearVelocityVector is the current body velocity vector? + // RA: Not sure what the *10 is for. A correction for pTimestep? + // m_lastLinearVelocityVector += (addAmount*10); + m_lastLinearVelocityVector += addAmount; // This will work temporarily, but we really need to compare speed on an axis // KF: Limit body velocity to applied velocity? + // Limit the velocity vector to less than the last set linear motor direction if (Math.Abs(m_lastLinearVelocityVector.X) > Math.Abs(m_linearMotorDirectionLASTSET.X)) m_lastLinearVelocityVector.X = m_linearMotorDirectionLASTSET.X; if (Math.Abs(m_lastLinearVelocityVector.Y) > Math.Abs(m_linearMotorDirectionLASTSET.Y)) @@ -630,76 +646,93 @@ namespace OpenSim.Region.Physics.BulletSPlugin // decay applied velocity Vector3 decayfraction = ((Vector3.One/(m_linearMotorDecayTimescale/pTimestep))); - //Console.WriteLine("decay: " + decayfraction); m_linearMotorDirection -= m_linearMotorDirection * decayfraction * 0.5f; - //Console.WriteLine("actual: " + m_linearMotorDirection); + + /* + Vector3 addAmount = (m_linearMotorDirection - m_lastLinearVelocityVector)/m_linearMotorTimescale; + m_lastLinearVelocityVector += addAmount; + + float decayfraction = (1.0f - 1.0f / m_linearMotorDecayTimescale); + m_linearMotorDirection *= decayfraction; + + */ + + DetailLog("{0},MoveLinear,nonZero,origdir={1},origvel={2},add={3},decay={4},dir={5},vel={6}", + m_prim.LocalID, origDir, origVel, addAmount, decayfraction, m_linearMotorDirection, m_lastLinearVelocityVector); } else - { // requested is not significant - // if what remains of applied is small, zero it. - if (m_lastLinearVelocityVector.ApproxEquals(Vector3.Zero, 0.01f)) - m_lastLinearVelocityVector = Vector3.Zero; + { + // if what remains of applied is small, zero it. + // if (m_lastLinearVelocityVector.ApproxEquals(Vector3.Zero, 0.01f)) + // m_lastLinearVelocityVector = Vector3.Zero; + m_linearMotorDirection = Vector3.Zero; + m_lastLinearVelocityVector = Vector3.Zero; } // convert requested object velocity to world-referenced vector - m_dir = m_lastLinearVelocityVector; - Quaternion rot = m_prim.Orientation; - Quaternion rotq = new Quaternion(rot.X, rot.Y, rot.Z, rot.W); // rotq = rotation of object - m_dir *= rotq; // apply obj rotation to velocity vector + Quaternion rotq = m_prim.Orientation; + m_dir = m_lastLinearVelocityVector * rotq; - // add Gravity andBuoyancy + // Add the various forces into m_dir which will be our new direction vector (velocity) + + // add Gravity and Buoyancy // KF: So far I have found no good method to combine a script-requested // .Z velocity and gravity. Therefore only 0g will used script-requested // .Z velocity. >0g (m_VehicleBuoyancy < 1) will used modified gravity only. Vector3 grav = Vector3.Zero; - // There is some gravity, make a gravity force vector - // that is applied after object velocity. - float objMass = m_prim.Mass; + // There is some gravity, make a gravity force vector that is applied after object velocity. // m_VehicleBuoyancy: -1=2g; 0=1g; 1=0g; - grav.Z = _pParentScene.DefaultGravity.Z * objMass * (1f - m_VehicleBuoyancy); + grav.Z = m_prim.Scene.DefaultGravity.Z * m_prim.Mass * (1f - m_VehicleBuoyancy); // Preserve the current Z velocity Vector3 vel_now = m_prim.Velocity; m_dir.Z = vel_now.Z; // Preserve the accumulated falling velocity Vector3 pos = m_prim.Position; + Vector3 posChange = pos; // Vector3 accel = new Vector3(-(m_dir.X - m_lastLinearVelocityVector.X / 0.1f), -(m_dir.Y - m_lastLinearVelocityVector.Y / 0.1f), m_dir.Z - m_lastLinearVelocityVector.Z / 0.1f); - Vector3 posChange = new Vector3(); - posChange.X = pos.X - m_lastPositionVector.X; - posChange.Y = pos.Y - m_lastPositionVector.Y; - posChange.Z = pos.Z - m_lastPositionVector.Z; double Zchange = Math.Abs(posChange.Z); if (m_BlockingEndPoint != Vector3.Zero) { + bool changed = false; if (pos.X >= (m_BlockingEndPoint.X - (float)1)) { pos.X -= posChange.X + 1; - m_prim.Position = pos; + changed = true; } if (pos.Y >= (m_BlockingEndPoint.Y - (float)1)) { pos.Y -= posChange.Y + 1; - m_prim.Position = pos; + changed = true; } if (pos.Z >= (m_BlockingEndPoint.Z - (float)1)) { pos.Z -= posChange.Z + 1; - m_prim.Position = pos; + changed = true; } if (pos.X <= 0) { pos.X += posChange.X + 1; - m_prim.Position = pos; + changed = true; } if (pos.Y <= 0) { pos.Y += posChange.Y + 1; + changed = true; + } + if (changed) + { m_prim.Position = pos; + DetailLog("{0},MoveLinear,blockingEndPoint,block={1},origPos={2},pos={3}", + m_prim.LocalID, m_BlockingEndPoint, posChange, pos); } } - if (pos.Z < _pParentScene.GetTerrainHeightAtXY(pos.X, pos.Y)) + + // If below the terrain, move us above the ground a little. + if (pos.Z < m_prim.Scene.GetTerrainHeightAtXYZ(pos)) { - pos.Z = _pParentScene.GetTerrainHeightAtXY(pos.X, pos.Y) + 2; + pos.Z = m_prim.Scene.GetTerrainHeightAtXYZ(pos) + 2; m_prim.Position = pos; + DetailLog("{0},MoveLinear,terrainHeight,pos={1}", m_prim.LocalID, pos); } // Check if hovering @@ -708,11 +741,11 @@ namespace OpenSim.Region.Physics.BulletSPlugin // We should hover, get the target height if ((m_Hoverflags & VehicleFlag.HOVER_WATER_ONLY) != 0) { - m_VhoverTargetHeight = _pParentScene.GetWaterLevel() + m_VhoverHeight; + m_VhoverTargetHeight = m_prim.Scene.GetWaterLevel() + m_VhoverHeight; } if ((m_Hoverflags & VehicleFlag.HOVER_TERRAIN_ONLY) != 0) { - m_VhoverTargetHeight = _pParentScene.GetTerrainHeightAtXY(pos.X, pos.Y) + m_VhoverHeight; + m_VhoverTargetHeight = m_prim.Scene.GetTerrainHeightAtXY(pos.X, pos.Y) + m_VhoverHeight; } if ((m_Hoverflags & VehicleFlag.HOVER_GLOBAL_HEIGHT) != 0) { @@ -746,6 +779,8 @@ namespace OpenSim.Region.Physics.BulletSPlugin } } + DetailLog("{0},MoveLinear,hover,pos={1},dir={2},height={3},target={4}", m_prim.LocalID, pos, m_dir, m_VhoverHeight, m_VhoverTargetHeight); + // m_VhoverEfficiency = 0f; // 0=boucy, 1=Crit.damped // m_VhoverTimescale = 0f; // time to acheive height // pTimestep is time since last frame,in secs @@ -774,12 +809,13 @@ namespace OpenSim.Region.Physics.BulletSPlugin { grav.Z = (float)(grav.Z * 1.125); } - float terraintemp = _pParentScene.GetTerrainHeightAtXY(pos.X, pos.Y); + float terraintemp = m_prim.Scene.GetTerrainHeightAtXYZ(pos); float postemp = (pos.Z - terraintemp); if (postemp > 2.5f) { grav.Z = (float)(grav.Z * 1.037125); } + DetailLog("{0},MoveLinear,limitMotorUp,grav={1}", m_prim.LocalID, grav); //End Experimental Values } if ((m_flags & (VehicleFlag.NO_X)) != 0) @@ -800,32 +836,39 @@ namespace OpenSim.Region.Physics.BulletSPlugin // Apply velocity m_prim.Velocity = m_dir; // apply gravity force - m_prim.Force = grav; + // Why is this set here? The physics engine already does gravity. + // m_prim.AddForce(grav, false); + // m_prim.Force = grav; - - // apply friction + // Apply friction Vector3 decayamount = Vector3.One / (m_linearFrictionTimescale / pTimestep); m_lastLinearVelocityVector -= m_lastLinearVelocityVector * decayamount; + + DetailLog("{0},MoveLinear,done,pos={1},vel={2},force={3},decay={4}", + m_prim.LocalID, m_lastPositionVector, m_dir, grav, decayamount); + } // end MoveLinear() private void MoveAngular(float pTimestep) { - /* - private Vector3 m_angularMotorDirection = Vector3.Zero; // angular velocity requested by LSL motor - private int m_angularMotorApply = 0; // application frame counter - private float m_angularMotorVelocity = 0; // current angular motor velocity (ramps up and down) - private float m_angularMotorTimescale = 0; // motor angular velocity ramp up rate - private float m_angularMotorDecayTimescale = 0; // motor angular velocity decay rate - private Vector3 m_angularFrictionTimescale = Vector3.Zero; // body angular velocity decay rate - private Vector3 m_lastAngularVelocity = Vector3.Zero; // what was last applied to body - */ + // m_angularMotorDirection // angular velocity requested by LSL motor + // m_angularMotorApply // application frame counter + // m_angularMotorVelocity // current angular motor velocity (ramps up and down) + // m_angularMotorTimescale // motor angular velocity ramp up rate + // m_angularMotorDecayTimescale // motor angular velocity decay rate + // m_angularFrictionTimescale // body angular velocity decay rate + // m_lastAngularVelocity // what was last applied to body // Get what the body is doing, this includes 'external' influences Vector3 angularVelocity = m_prim.RotationalVelocity; - // Vector3 angularVelocity = Vector3.Zero; if (m_angularMotorApply > 0) { + // Rather than snapping the angular motor velocity from the old value to + // a newly set velocity, this routine steps the value from the previous + // value (m_angularMotorVelocity) to the requested value (m_angularMotorDirection). + // There are m_angularMotorApply steps. + Vector3 origAngularVelocity = m_angularMotorVelocity; // ramp up to new value // current velocity += error / (time to get there / step interval) // requested speed - last motor speed @@ -833,23 +876,21 @@ namespace OpenSim.Region.Physics.BulletSPlugin m_angularMotorVelocity.Y += (m_angularMotorDirection.Y - m_angularMotorVelocity.Y) / (m_angularMotorTimescale / pTimestep); m_angularMotorVelocity.Z += (m_angularMotorDirection.Z - m_angularMotorVelocity.Z) / (m_angularMotorTimescale / pTimestep); + DetailLog("{0},MoveAngular,angularMotorApply,apply={1},origvel={2},dir={3},vel={4}", + m_prim.LocalID,m_angularMotorApply,origAngularVelocity, m_angularMotorDirection, m_angularMotorVelocity); + m_angularMotorApply--; // This is done so that if script request rate is less than phys frame rate the expected // velocity may still be acheived. } else { - // no motor recently applied, keep the body velocity - /* m_angularMotorVelocity.X = angularVelocity.X; - m_angularMotorVelocity.Y = angularVelocity.Y; - m_angularMotorVelocity.Z = angularVelocity.Z; */ - + // No motor recently applied, keep the body velocity // and decay the velocity m_angularMotorVelocity -= m_angularMotorVelocity / (m_angularMotorDecayTimescale / pTimestep); } // end motor section // Vertical attractor section Vector3 vertattr = Vector3.Zero; - if (m_verticalAttractionTimescale < 300) { float VAservo = 0.2f / (m_verticalAttractionTimescale * pTimestep); @@ -871,7 +912,6 @@ namespace OpenSim.Region.Physics.BulletSPlugin // Error is 0 (no error) to +/- 2 (max error) // scale it by VAservo verterr = verterr * VAservo; -//if (frcount == 0) Console.WriteLine("VAerr=" + verterr); // As the body rotates around the X axis, then verterr.Y increases; Rotated around Y then .X increases, so // Change Body angular velocity X based on Y, and Y based on X. Z is not changed. @@ -884,11 +924,15 @@ namespace OpenSim.Region.Physics.BulletSPlugin vertattr.X += bounce * angularVelocity.X; vertattr.Y += bounce * angularVelocity.Y; + DetailLog("{0},MoveAngular,verticalAttraction,verterr={1},bounce={2},vertattr={3}", + m_prim.LocalID, verterr, bounce, vertattr); + } // else vertical attractor is off - // m_lastVertAttractor = vertattr; + // m_lastVertAttractor = vertattr; // Bank section tba + // Deflection section tba // Sum velocities @@ -898,11 +942,13 @@ namespace OpenSim.Region.Physics.BulletSPlugin { m_lastAngularVelocity.X = 0; m_lastAngularVelocity.Y = 0; + DetailLog("{0},MoveAngular,noDeflectionUp,lastAngular={1}", m_prim.LocalID, m_lastAngularVelocity); } if (m_lastAngularVelocity.ApproxEquals(Vector3.Zero, 0.01f)) { m_lastAngularVelocity = Vector3.Zero; // Reduce small value to zero. + DetailLog("{0},MoveAngular,zeroSmallValues,lastAngular={1}", m_prim.LocalID, m_lastAngularVelocity); } // apply friction @@ -912,10 +958,12 @@ namespace OpenSim.Region.Physics.BulletSPlugin // Apply to the body m_prim.RotationalVelocity = m_lastAngularVelocity; + DetailLog("{0},MoveAngular,done,decay={1},lastAngular={2}", m_prim.LocalID, decayamount, m_lastAngularVelocity); } //end MoveAngular + internal void LimitRotation(float timestep) { - Quaternion rotq = m_prim.Orientation; // rotq = rotation of object + Quaternion rotq = m_prim.Orientation; Quaternion m_rot = rotq; bool changed = false; if (m_RollreferenceFrame != Quaternion.Identity) @@ -923,18 +971,22 @@ namespace OpenSim.Region.Physics.BulletSPlugin if (rotq.X >= m_RollreferenceFrame.X) { m_rot.X = rotq.X - (m_RollreferenceFrame.X / 2); + changed = true; } if (rotq.Y >= m_RollreferenceFrame.Y) { m_rot.Y = rotq.Y - (m_RollreferenceFrame.Y / 2); + changed = true; } if (rotq.X <= -m_RollreferenceFrame.X) { m_rot.X = rotq.X + (m_RollreferenceFrame.X / 2); + changed = true; } if (rotq.Y <= -m_RollreferenceFrame.Y) { m_rot.Y = rotq.Y + (m_RollreferenceFrame.Y / 2); + changed = true; } changed = true; } @@ -944,8 +996,23 @@ namespace OpenSim.Region.Physics.BulletSPlugin m_rot.Y = 0; changed = true; } + if ((m_flags & VehicleFlag.LOCK_ROTATION) != 0) + { + m_rot.X = 0; + m_rot.Y = 0; + changed = true; + } if (changed) m_prim.Orientation = m_rot; + + DetailLog("{0},LimitRotation,done,changed={1},orig={2},new={3}", m_prim.LocalID, changed, rotq, m_rot); + } + + // Invoke the detailed logger and output something if it's enabled. + private void DetailLog(string msg, params Object[] args) + { + if (m_prim.Scene.VehicleLoggingEnabled) + m_prim.Scene.PhysicsLogging.Write(msg, args); } } } diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSLinkset.cs b/OpenSim/Region/Physics/BulletSPlugin/BSLinkset.cs new file mode 100755 index 0000000000..6f8430c8cc --- /dev/null +++ b/OpenSim/Region/Physics/BulletSPlugin/BSLinkset.cs @@ -0,0 +1,352 @@ +/* + * 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 copyrightD + * 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.Text; + +using OMV = OpenMetaverse; + +namespace OpenSim.Region.Physics.BulletSPlugin +{ +public class BSLinkset +{ + private static string LogHeader = "[BULLETSIM LINKSET]"; + + private BSPrim m_linksetRoot; + public BSPrim Root { get { return m_linksetRoot; } } + + private BSScene m_scene; + + private List m_children; + + // We lock the diddling of linkset classes to prevent any badness. + // This locks the modification of the instances of this class. Changes + // to the physical representation is done via the tainting mechenism. + private object m_linksetActivityLock = new Object(); + + // We keep the prim's mass in the linkset structure since it could be dependent on other prims + private float m_mass; + public float LinksetMass + { + get + { + m_mass = ComputeLinksetMass(); + return m_mass; + } + } + + public OMV.Vector3 CenterOfMass + { + get { return ComputeLinksetCenterOfMass(); } + } + + public OMV.Vector3 GeometricCenter + { + get { return ComputeLinksetGeometricCenter(); } + } + + public BSLinkset(BSScene scene, BSPrim parent) + { + // A simple linkset of one (no children) + m_scene = scene; + m_linksetRoot = parent; + m_children = new List(); + m_mass = parent.MassRaw; + } + + // Link to a linkset where the child knows the parent. + // Parent changing should not happen so do some sanity checking. + // We return the parent's linkset so the child can track it's membership. + public BSLinkset AddMeToLinkset(BSPrim child, BSPrim parent) + { + lock (m_linksetActivityLock) + { + parent.Linkset.AddChildToLinkset(child); + } + return parent.Linkset; + } + + public BSLinkset RemoveMeFromLinkset(BSPrim child) + { + lock (m_linksetActivityLock) + { + if (IsRoot(child)) + { + // if root of linkset, take the linkset apart + while (m_children.Count > 0) + { + // Note that we don't do a foreach because the remove routine + // takes it out of the list. + RemoveChildFromLinkset(m_children[0]); + } + m_children.Clear(); // just to make sure + } + else + { + // Just removing a child from an existing linkset + RemoveChildFromLinkset(child); + } + } + + // The child is down to a linkset of just itself + return new BSLinkset(m_scene, child); + } + + // An existing linkset had one of its members rebuilt or something. + // Go through the linkset and rebuild the pointers to the bodies of the linkset members. + public BSLinkset RefreshLinkset(BSPrim requestor) + { + BSLinkset ret = requestor.Linkset; + + lock (m_linksetActivityLock) + { + System.IntPtr aPtr = BulletSimAPI.GetBodyHandle2(m_scene.World.Ptr, m_linksetRoot.LocalID); + if (aPtr == System.IntPtr.Zero) + { + // That's odd. We can't find the root of the linkset. + // The linkset is somehow dead. The requestor is now a member of a linkset of one. + DetailLog("{0},RefreshLinkset.RemoveRoot,child={1}", m_linksetRoot.LocalID, m_linksetRoot.LocalID); + ret = RemoveMeFromLinkset(m_linksetRoot); + } + else + { + // Reconstruct the pointer to the body of the linkset root. + DetailLog("{0},RefreshLinkset.RebuildRoot,rootID={1},ptr={2}", m_linksetRoot.LocalID, m_linksetRoot.LocalID, aPtr); + m_linksetRoot.Body = new BulletBody(m_linksetRoot.LocalID, aPtr); + + List toRemove = new List(); + foreach (BSPrim bsp in m_children) + { + aPtr = BulletSimAPI.GetBodyHandle2(m_scene.World.Ptr, bsp.LocalID); + if (aPtr == System.IntPtr.Zero) + { + toRemove.Add(bsp); + } + else + { + // Reconstruct the pointer to the body of the linkset root. + DetailLog("{0},RefreshLinkset.RebuildChild,rootID={1},ptr={2}", bsp.LocalID, m_linksetRoot.LocalID, aPtr); + bsp.Body = new BulletBody(bsp.LocalID, aPtr); + } + } + foreach (BSPrim bsp in toRemove) + { + RemoveChildFromLinkset(bsp); + } + } + } + + return ret; + } + + + // Return 'true' if the passed object is the root object of this linkset + public bool IsRoot(BSPrim requestor) + { + return (requestor.LocalID == m_linksetRoot.LocalID); + } + + // Return 'true' if this linkset has any children (more than the root member) + public bool HasAnyChildren { get { return (m_children.Count > 0); } } + + // Return 'true' if this child is in this linkset + public bool HasChild(BSPrim child) + { + bool ret = false; + foreach (BSPrim bp in m_children) + { + if (child.LocalID == bp.LocalID) + { + ret = true; + break; + } + } + return ret; + } + + private float ComputeLinksetMass() + { + float mass = m_linksetRoot.MassRaw; + foreach (BSPrim bp in m_children) + { + mass += bp.MassRaw; + } + return mass; + } + + private OMV.Vector3 ComputeLinksetCenterOfMass() + { + OMV.Vector3 com = m_linksetRoot.Position * m_linksetRoot.MassRaw; + float totalMass = m_linksetRoot.MassRaw; + + foreach (BSPrim bp in m_children) + { + com += bp.Position * bp.MassRaw; + totalMass += bp.MassRaw; + } + com /= totalMass; + + return com; + } + + private OMV.Vector3 ComputeLinksetGeometricCenter() + { + OMV.Vector3 com = m_linksetRoot.Position; + + foreach (BSPrim bp in m_children) + { + com += bp.Position * bp.MassRaw; + } + com /= m_children.Count + 1; + + return com; + } + + // I am the root of a linkset and a new child is being added + public void AddChildToLinkset(BSPrim pchild) + { + BSPrim child = pchild; + if (!HasChild(child)) + { + m_children.Add(child); + + m_scene.TaintedObject(delegate() + { + DebugLog("{0}: AddChildToLinkset: adding child {1} to {2}", LogHeader, child.LocalID, m_linksetRoot.LocalID); + DetailLog("{0},AddChildToLinkset,child={1}", m_linksetRoot.LocalID, pchild.LocalID); + PhysicallyLinkAChildToRoot(pchild); // build the physical binding between me and the child + }); + } + return; + } + + // I am the root of a linkset and one of my children is being removed. + // Safe to call even if the child is not really in my linkset. + public void RemoveChildFromLinkset(BSPrim pchild) + { + BSPrim child = pchild; + + if (m_children.Remove(child)) + { + m_scene.TaintedObject(delegate() + { + DebugLog("{0}: RemoveChildFromLinkset: Removing constraint to {1}", LogHeader, child.LocalID); + DetailLog("{0},RemoveChildFromLinkset,child={1}", m_linksetRoot.LocalID, pchild.LocalID); + + if (m_children.Count == 0) + { + // if the linkset is empty, make sure all linkages have been removed + PhysicallyUnlinkAllChildrenFromRoot(); + } + else + { + PhysicallyUnlinkAChildFromRoot(pchild); + } + }); + } + else + { + // This will happen if we remove the root of the linkset first. Non-fatal occurance. + // m_scene.Logger.ErrorFormat("{0}: Asked to remove child from linkset that was not in linkset", LogHeader); + } + return; + } + + // Create a constraint between me (root of linkset) and the passed prim (the child). + // Called at taint time! + private void PhysicallyLinkAChildToRoot(BSPrim childPrim) + { + // Zero motion for children so they don't interpolate + childPrim.ZeroMotion(); + + // relative position normalized to the root prim + OMV.Quaternion invThisOrientation = OMV.Quaternion.Inverse(m_linksetRoot.Orientation); + OMV.Vector3 childRelativePosition = (childPrim.Position - m_linksetRoot.Position) * invThisOrientation; + + // relative rotation of the child to the parent + OMV.Quaternion childRelativeRotation = invThisOrientation * childPrim.Orientation; + + // create a constraint that allows no freedom of movement between the two objects + // http://bulletphysics.org/Bullet/phpBB3/viewtopic.php?t=4818 + // DebugLog("{0}: CreateLinkset: Adding a constraint between root prim {1} and child prim {2}", LogHeader, LocalID, childPrim.LocalID); + DetailLog("{0},LinkAChildToMe,taint,root={1},child={2}", m_linksetRoot.LocalID, m_linksetRoot.LocalID, childPrim.LocalID); + BSConstraint constrain = m_scene.Constraints.CreateConstraint( + m_scene.World, m_linksetRoot.Body, childPrim.Body, + // childRelativePosition, + // childRelativeRotation, + OMV.Vector3.Zero, + OMV.Quaternion.Identity, + OMV.Vector3.Zero, + OMV.Quaternion.Identity + ); + constrain.SetLinearLimits(OMV.Vector3.Zero, OMV.Vector3.Zero); + constrain.SetAngularLimits(OMV.Vector3.Zero, OMV.Vector3.Zero); + + // tweek the constraint to increase stability + constrain.UseFrameOffset(m_scene.BoolNumeric(m_scene.Params.linkConstraintUseFrameOffset)); + constrain.TranslationalLimitMotor(m_scene.BoolNumeric(m_scene.Params.linkConstraintEnableTransMotor), + m_scene.Params.linkConstraintTransMotorMaxVel, + m_scene.Params.linkConstraintTransMotorMaxForce); + constrain.SetCFMAndERP(m_scene.Params.linkConstraintCFM, m_scene.Params.linkConstraintERP); + + } + + // Remove linkage between myself and a particular child + // Called at taint time! + private void PhysicallyUnlinkAChildFromRoot(BSPrim childPrim) + { + DebugLog("{0}: PhysicallyUnlinkAChildFromRoot: RemoveConstraint between root prim {1} and child prim {2}", + LogHeader, m_linksetRoot.LocalID, childPrim.LocalID); + DetailLog("{0},PhysicallyUnlinkAChildFromRoot,taint,root={1},child={2}", m_linksetRoot.LocalID, m_linksetRoot.LocalID, childPrim.LocalID); + // BulletSimAPI.RemoveConstraint(_scene.WorldID, LocalID, childPrim.LocalID); + m_scene.Constraints.RemoveAndDestroyConstraint(m_linksetRoot.Body, childPrim.Body); + } + + // Remove linkage between myself and any possible children I might have + // Called at taint time! + private void PhysicallyUnlinkAllChildrenFromRoot() + { + // DebugLog("{0}: PhysicallyUnlinkAllChildren:", LogHeader); + DetailLog("{0},PhysicallyUnlinkAllChildren,taint", m_linksetRoot.LocalID); + m_scene.Constraints.RemoveAndDestroyConstraint(m_linksetRoot.Body); + // BulletSimAPI.RemoveConstraintByID(_scene.WorldID, LocalID); + } + + // Invoke the detailed logger and output something if it's enabled. + private void DebugLog(string msg, params Object[] args) + { + m_scene.Logger.DebugFormat(msg, args); + } + + // Invoke the detailed logger and output something if it's enabled. + private void DetailLog(string msg, params Object[] args) + { + m_scene.PhysicsLogging.Write(msg, args); + } + +} +} diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs b/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs index 130f1ca145..a4ab702f60 100644 --- a/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs @@ -42,6 +42,8 @@ public sealed class BSPrim : PhysicsActor private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); private static readonly string LogHeader = "[BULLETS PRIM]"; + private void DebugLog(string mm, params Object[] xx) { if (_scene.shouldDebugLog) m_log.DebugFormat(mm, xx); } + private IMesh _mesh; private PrimitiveBaseShape _pbs; private ShapeData.PhysicsShapeType _shapeType; @@ -50,6 +52,7 @@ public sealed class BSPrim : PhysicsActor private List _hulls; private BSScene _scene; + public BSScene Scene { get { return _scene; } } private String _avName; private uint _localID = 0; @@ -63,7 +66,7 @@ public sealed class BSPrim : PhysicsActor private bool _isSelected; private bool _isVolumeDetect; private OMV.Vector3 _position; - private float _mass; + private float _mass; // the mass of this object private float _density; private OMV.Vector3 _force; private OMV.Vector3 _velocity; @@ -86,14 +89,25 @@ public sealed class BSPrim : PhysicsActor private bool _kinematic; private float _buoyancy; - private List _childrenPrims; - private BSPrim _parentPrim; + // Membership in a linkset is controlled by this class. + private BSLinkset _linkset; + public BSLinkset Linkset + { + get { return _linkset; } + set { _linkset = value; } + } private int _subscribedEventsMs = 0; private int _nextCollisionOkTime = 0; long _collidingStep; long _collidingGroundStep; + private BulletBody m_body; + public BulletBody Body { + get { return m_body; } + set { m_body = value; } + } + private BSDynamics _vehicle; private OMV.Vector3 _PIDTarget; @@ -127,17 +141,18 @@ public sealed class BSPrim : PhysicsActor _friction = _scene.Params.defaultFriction; // TODO: compute based on object material _density = _scene.Params.defaultDensity; // TODO: compute based on object material _restitution = _scene.Params.defaultRestitution; - _parentPrim = null; // not a child or a parent + _linkset = new BSLinkset(_scene, this); // a linkset of one _vehicle = new BSDynamics(this); // add vehicleness - _childrenPrims = new List(); - if (_isPhysical) - _mass = CalculateMass(); - else - _mass = 0f; + _mass = CalculateMass(); // do the actual object creation at taint time _scene.TaintedObject(delegate() { RecreateGeomAndObject(); + + // Get the pointer to the physical body for this object. + // At the moment, we're still letting BulletSim manage the creation and destruction + // of the object. Someday we'll move that into the C# code. + m_body = new BulletBody(LocalID, BulletSimAPI.GetBodyHandle2(_scene.World.Ptr, LocalID)); }); } @@ -145,13 +160,19 @@ public sealed class BSPrim : PhysicsActor public void Destroy() { // m_log.DebugFormat("{0}: Destroy, id={1}", LogHeader, LocalID); + // DetailLog("{0},Destroy", LocalID); + // Undo any vehicle properties _vehicle.ProcessTypeChange(Vehicle.TYPE_NONE); _scene.RemoveVehiclePrim(this); // just to make sure + _scene.TaintedObject(delegate() { + // Undo any links between me and any other object + _linkset = _linkset.RemoveMeFromLinkset(this); + // everything in the C# world will get garbage collected. Tell the C++ world to free stuff. - BulletSimAPI.DestroyObject(_scene.WorldID, _localID); + BulletSimAPI.DestroyObject(_scene.WorldID, LocalID); }); } @@ -164,8 +185,9 @@ public sealed class BSPrim : PhysicsActor _size = value; _scene.TaintedObject(delegate() { - if (_isPhysical) _mass = CalculateMass(); // changing size changes the mass - BulletSimAPI.SetObjectScaleMass(_scene.WorldID, _localID, _scale, _mass, _isPhysical); + _mass = CalculateMass(); // changing size changes the mass + BulletSimAPI.SetObjectScaleMass(_scene.WorldID, _localID, _scale, (IsPhysical ? _mass : 0f), IsPhysical); + DetailLog("{0}: setSize: size={1}, mass={2}, physical={3}", LocalID, _size, _mass, IsPhysical); RecreateGeomAndObject(); }); } @@ -175,7 +197,7 @@ public sealed class BSPrim : PhysicsActor _pbs = value; _scene.TaintedObject(delegate() { - if (_isPhysical) _mass = CalculateMass(); // changing the shape changes the mass + _mass = CalculateMass(); // changing the shape changes the mass RecreateGeomAndObject(); }); } @@ -202,33 +224,10 @@ public sealed class BSPrim : PhysicsActor // link me to the specified parent public override void link(PhysicsActor obj) { BSPrim parent = obj as BSPrim; - // m_log.DebugFormat("{0}: link {1}/{2} to {3}", LogHeader, _avName, _localID, obj.LocalID); - // TODO: decide if this parent checking needs to happen at taint time - if (_parentPrim == null) - { - if (parent != null) - { - // I don't have a parent so I am joining a linkset - parent.AddChildToLinkset(this); - } - } - else - { - // I already have a parent, is parenting changing? - if (parent != _parentPrim) - { - if (parent == null) - { - // we are being removed from a linkset - _parentPrim.RemoveChildFromLinkset(this); - } - else - { - // asking to reparent a prim should not happen - m_log.ErrorFormat("{0}: Reparenting a prim. ", LogHeader); - } - } - } + DebugLog("{0}: link {1}/{2} to {3}", LogHeader, _avName, _localID, obj.LocalID); + DetailLog("{0},link,parent={1}", LocalID, obj.LocalID); + + _linkset = _linkset.AddMeToLinkset(this, parent); return; } @@ -236,101 +235,92 @@ public sealed class BSPrim : PhysicsActor public override void delink() { // TODO: decide if this parent checking needs to happen at taint time // Race condition here: if link() and delink() in same simulation tick, the delink will not happen - // m_log.DebugFormat("{0}: delink {1}/{2}", LogHeader, _avName, _localID); - if (_parentPrim != null) - { - _parentPrim.RemoveChildFromLinkset(this); - } + DebugLog("{0}: delink {1}/{2}. Parent={3}", LogHeader, _avName, _localID, + _linkset.Root._avName+"/"+_linkset.Root.LocalID.ToString()); + DetailLog("{0},delink,parent={1}", LocalID, _linkset.Root.LocalID.ToString()); + + _linkset.RemoveMeFromLinkset(this); return; } - // I am the root of a linkset and a new child is being added - public void AddChildToLinkset(BSPrim pchild) - { - BSPrim child = pchild; - _scene.TaintedObject(delegate() - { - if (!_childrenPrims.Contains(child)) - { - _childrenPrims.Add(child); - child.ParentPrim = this; // the child has gained a parent - RecreateGeomAndObject(); // rebuild my shape with the new child added - } - }); - return; - } - - // I am the root of a linkset and one of my children is being removed. - // Safe to call even if the child is not really in my linkset. - public void RemoveChildFromLinkset(BSPrim pchild) - { - BSPrim child = pchild; - _scene.TaintedObject(delegate() - { - if (_childrenPrims.Contains(child)) - { - BulletSimAPI.RemoveConstraint(_scene.WorldID, child.LocalID, this.LocalID); - _childrenPrims.Remove(child); - child.ParentPrim = null; // the child has lost its parent - RecreateGeomAndObject(); // rebuild my shape with the child removed - } - else - { - m_log.ErrorFormat("{0}: Asked to remove child from linkset that was not in linkset"); - } - }); - return; - } - - public BSPrim ParentPrim - { - set { _parentPrim = value; } - } - - // return true if we are the root of a linkset (there are children to manage) - public bool IsRootOfLinkset - { - get { return (_parentPrim == null && _childrenPrims.Count != 0); } - } - // Set motion values to zero. // Do it to the properties so the values get set in the physics engine. // Push the setting of the values to the viewer. - private void ZeroMotion() + // Called at taint time! + public void ZeroMotion() { - Velocity = OMV.Vector3.Zero; + _velocity = OMV.Vector3.Zero; _acceleration = OMV.Vector3.Zero; - RotationalVelocity = OMV.Vector3.Zero; - base.RequestPhysicsterseUpdate(); + _rotationalVelocity = OMV.Vector3.Zero; + + // Zero some other properties directly into the physics engine + BulletSimAPI.SetVelocity2(Body.Ptr, OMV.Vector3.Zero); + BulletSimAPI.SetAngularVelocity2(Body.Ptr, OMV.Vector3.Zero); + BulletSimAPI.SetInterpolation2(Body.Ptr, OMV.Vector3.Zero, OMV.Vector3.Zero); + BulletSimAPI.ClearForces2(Body.Ptr); } - public override void LockAngularMotion(OMV.Vector3 axis) { return; } + public override void LockAngularMotion(OMV.Vector3 axis) + { + DetailLog("{0},LockAngularMotion,call,axis={1}", LocalID, axis); + return; + } public override OMV.Vector3 Position { get { - // don't do the following GetObjectPosition because this function is called a zillion times + if (!_linkset.IsRoot(this)) + // child prims move around based on their parent. Need to get the latest location + _position = BulletSimAPI.GetObjectPosition(_scene.WorldID, _localID); + + // don't do the GetObjectPosition for root elements because this function is called a zillion times // _position = BulletSimAPI.GetObjectPosition(_scene.WorldID, _localID); return _position; } set { _position = value; + // TODO: what does it mean to set the position of a child prim?? Rebuild the constraint? _scene.TaintedObject(delegate() { + DetailLog("{0},SetPosition,taint,pos={1},orient={2}", LocalID, _position, _orientation); BulletSimAPI.SetObjectTranslation(_scene.WorldID, _localID, _position, _orientation); - // m_log.DebugFormat("{0}: setPosition: id={1}, position={2}", LogHeader, _localID, _position); }); } } - public override float Mass { - get { return _mass; } + + // Return the effective mass of the object. + // If there are multiple items in the linkset, add them together for the root + public override float Mass + { + get + { + return _linkset.LinksetMass; + } } + + // used when we only want this prim's mass and not the linkset thing + public float MassRaw { get { return _mass; } } + + // Is this used? + public override OMV.Vector3 CenterOfMass + { + get { return _linkset.CenterOfMass; } + } + + // Is this used? + public override OMV.Vector3 GeometricCenter + { + get { return _linkset.GeometricCenter; } + } + public override OMV.Vector3 Force { get { return _force; } set { _force = value; _scene.TaintedObject(delegate() { - BulletSimAPI.SetObjectForce(_scene.WorldID, _localID, _force); + DetailLog("{0},SetForce,taint,force={1}", LocalID, _force); + // BulletSimAPI.SetObjectForce(_scene.WorldID, _localID, _force); + BulletSimAPI.SetObjectForce2(Body.Ptr, _force); }); } } @@ -341,15 +331,22 @@ public sealed class BSPrim : PhysicsActor } set { Vehicle type = (Vehicle)value; - _vehicle.ProcessTypeChange(type); _scene.TaintedObject(delegate() { + DetailLog("{0},SetVehicleType,taint,type={1}", LocalID, type); + _vehicle.ProcessTypeChange(type); if (type == Vehicle.TYPE_NONE) { _scene.RemoveVehiclePrim(this); } else { + _scene.TaintedObject(delegate() + { + // Tell the physics engine to clear state + BulletSimAPI.ClearForces2(this.Body.Ptr); + }); + // make it so the scene will call us each tick to do vehicle things _scene.AddVehiclePrim(this); } @@ -359,47 +356,59 @@ public sealed class BSPrim : PhysicsActor } public override void VehicleFloatParam(int param, float value) { - _vehicle.ProcessFloatVehicleParam((Vehicle)param, value); + _scene.TaintedObject(delegate() + { + _vehicle.ProcessFloatVehicleParam((Vehicle)param, value, _scene.LastSimulatedTimestep); + }); } public override void VehicleVectorParam(int param, OMV.Vector3 value) { - _vehicle.ProcessVectorVehicleParam((Vehicle)param, value); + _scene.TaintedObject(delegate() + { + _vehicle.ProcessVectorVehicleParam((Vehicle)param, value, _scene.LastSimulatedTimestep); + }); } public override void VehicleRotationParam(int param, OMV.Quaternion rotation) { - _vehicle.ProcessRotationVehicleParam((Vehicle)param, rotation); + _scene.TaintedObject(delegate() + { + _vehicle.ProcessRotationVehicleParam((Vehicle)param, rotation); + }); } public override void VehicleFlags(int param, bool remove) { - _vehicle.ProcessVehicleFlags(param, remove); + _scene.TaintedObject(delegate() + { + _vehicle.ProcessVehicleFlags(param, remove); + }); } - // Called each simulation step to advance vehicle characteristics + + // Called each simulation step to advance vehicle characteristics. + // Called from Scene when doing simulation step so we're in taint processing time. public void StepVehicle(float timeStep) { - _vehicle.Step(timeStep, _scene); + if (IsPhysical) + _vehicle.Step(timeStep); } // Allows the detection of collisions with inherently non-physical prims. see llVolumeDetect for more public override void SetVolumeDetect(int param) { bool newValue = (param != 0); - if (_isVolumeDetect != newValue) + _isVolumeDetect = newValue; + _scene.TaintedObject(delegate() { - _isVolumeDetect = newValue; - _scene.TaintedObject(delegate() - { - SetObjectDynamic(); - }); - } + SetObjectDynamic(); + }); return; } - public override OMV.Vector3 GeometricCenter { get { return OMV.Vector3.Zero; } } - public override OMV.Vector3 CenterOfMass { get { return OMV.Vector3.Zero; } } public override OMV.Vector3 Velocity { get { return _velocity; } - set { _velocity = value; + set { + _velocity = value; _scene.TaintedObject(delegate() { + DetailLog("{0},SetVelocity,taint,vel={1}", LocalID, _velocity); BulletSimAPI.SetObjectVelocity(_scene.WorldID, LocalID, _velocity); }); } @@ -407,6 +416,7 @@ public sealed class BSPrim : PhysicsActor public override OMV.Vector3 Torque { get { return _torque; } set { _torque = value; + DetailLog("{0},SetTorque,call,torque={1}", LocalID, _torque); } } public override float CollisionScore { @@ -419,13 +429,21 @@ public sealed class BSPrim : PhysicsActor set { _acceleration = value; } } public override OMV.Quaternion Orientation { - get { return _orientation; } + get { + if (!_linkset.IsRoot(this)) + { + // Children move around because tied to parent. Get a fresh value. + _orientation = BulletSimAPI.GetObjectOrientation(_scene.WorldID, LocalID); + } + return _orientation; + } set { _orientation = value; - // m_log.DebugFormat("{0}: set orientation: id={1}, ori={2}", LogHeader, LocalID, _orientation); + // TODO: what does it mean if a child in a linkset changes its orientation? Rebuild the constraint? _scene.TaintedObject(delegate() { // _position = BulletSimAPI.GetObjectPosition(_scene.WorldID, _localID); + DetailLog("{0},SetOrientation,taint,pos={1},orient={2}", LocalID, _position, _orientation); BulletSimAPI.SetObjectTranslation(_scene.WorldID, _localID, _position, _orientation); }); } @@ -458,25 +476,24 @@ public sealed class BSPrim : PhysicsActor get { return !IsPhantom && !_isVolumeDetect; } } - // make gravity work if the object is physical and not selected - // no locking here because only called when it is safe + // Make gravity work if the object is physical and not selected + // No locking here because only called when it is safe + // Only called at taint time so it is save to call into Bullet. private void SetObjectDynamic() { - // m_log.DebugFormat("{0}: ID={1}, SetObjectDynamic: IsStatic={2}, IsSolid={3}", LogHeader, _localID, IsStatic, IsSolid); - // non-physical things work best with a mass of zero - if (IsStatic) - { - _mass = 0f; - } - else - { - _mass = CalculateMass(); - // If it's dynamic, make sure the hull has been created for it - // This shouldn't do much work if the object had previously been built - RecreateGeomAndObject(); + // RA: remove this for the moment. + // The problem is that dynamic objects are hulls so if we are becoming physical + // the shape has to be checked and possibly built. + // Maybe a VerifyCorrectPhysicalShape() routine? + // RecreateGeomAndObject(); - } - BulletSimAPI.SetObjectProperties(_scene.WorldID, LocalID, IsStatic, IsSolid, SubscribedEvents(), _mass); + float mass = _mass; + // Bullet wants static objects have a mass of zero + if (IsStatic) + mass = 0f; + + DetailLog("{0},SetObjectDynamic,taint,static={1},solid={2},mass={3}", LocalID, IsStatic, IsSolid, mass); + BulletSimAPI.SetObjectProperties(_scene.WorldID, LocalID, IsStatic, IsSolid, SubscribedEvents(), mass); } // prims don't fly @@ -516,11 +533,24 @@ public sealed class BSPrim : PhysicsActor set { _floatOnWater = value; } } public override OMV.Vector3 RotationalVelocity { - get { return _rotationalVelocity; } - set { _rotationalVelocity = value; + get { + /* + OMV.Vector3 pv = OMV.Vector3.Zero; + // if close to zero, report zero + // This is copied from ODE but I'm not sure why it returns zero but doesn't + // zero the property in the physics engine. + if (_rotationalVelocity.ApproxEquals(pv, 0.2f)) + return pv; + */ + + return _rotationalVelocity; + } + set { + _rotationalVelocity = value; // m_log.DebugFormat("{0}: RotationalVelocity={1}", LogHeader, _rotationalVelocity); _scene.TaintedObject(delegate() { + DetailLog("{0},SetRotationalVel,taint,rotvel={1}", LocalID, _rotationalVelocity); BulletSimAPI.SetObjectAngularVelocity(_scene.WorldID, LocalID, _rotationalVelocity); }); } @@ -533,11 +563,13 @@ public sealed class BSPrim : PhysicsActor } public override float Buoyancy { get { return _buoyancy; } - set { _buoyancy = value; - _scene.TaintedObject(delegate() - { - BulletSimAPI.SetObjectBuoyancy(_scene.WorldID, _localID, _buoyancy); - }); + set { + _buoyancy = value; + _scene.TaintedObject(delegate() + { + DetailLog("{0},SetBuoyancy,taint,buoy={1}", LocalID, _buoyancy); + BulletSimAPI.SetObjectBuoyancy(_scene.WorldID, _localID, _buoyancy); + }); } } @@ -573,27 +605,45 @@ public sealed class BSPrim : PhysicsActor public override float APIDStrength { set { return; } } public override float APIDDamping { set { return; } } + private List m_accumulatedForces = new List(); public override void AddForce(OMV.Vector3 force, bool pushforce) { if (force.IsFinite()) { - _force.X += force.X; - _force.Y += force.Y; - _force.Z += force.Z; + // _force += force; + lock (m_accumulatedForces) + m_accumulatedForces.Add(new OMV.Vector3(force)); } else { m_log.WarnFormat("{0}: Got a NaN force applied to a Character", LogHeader); + return; } _scene.TaintedObject(delegate() { - BulletSimAPI.SetObjectForce(_scene.WorldID, _localID, _force); + lock (m_accumulatedForces) + { + if (m_accumulatedForces.Count > 0) + { + OMV.Vector3 fSum = OMV.Vector3.Zero; + foreach (OMV.Vector3 v in m_accumulatedForces) + { + fSum += v; + } + m_accumulatedForces.Clear(); + + DetailLog("{0},SetObjectForce,taint,force={1}", LocalID, fSum); + BulletSimAPI.SetObjectForce(_scene.WorldID, _localID, fSum); + } + } }); } public override void AddAngularForce(OMV.Vector3 force, bool pushforce) { + DetailLog("{0},AddAngularForce,call,angForce={1},push={2}", LocalID, force, pushforce); // m_log.DebugFormat("{0}: AddAngularForce. f={1}, push={2}", LogHeader, force, pushforce); } public override void SetMomentum(OMV.Vector3 momentum) { + DetailLog("{0},SetMomentum,call,mom={1}", LocalID, momentum); } public override void SubscribeEvents(int ms) { _subscribedEventsMs = ms; @@ -885,6 +935,9 @@ public sealed class BSPrim : PhysicsActor returnMass = _density * volume; + /* + * This change means each object keeps its own mass and the Mass property + * will return the sum if we're part of a linkset. if (IsRootOfLinkset) { foreach (BSPrim prim in _childrenPrims) @@ -892,6 +945,7 @@ public sealed class BSPrim : PhysicsActor returnMass += prim.CalculateMass(); } } + */ if (returnMass <= 0) returnMass = 0.0001f; @@ -907,9 +961,11 @@ public sealed class BSPrim : PhysicsActor // The objects needs a hull if it's physical otherwise a mesh is enough // No locking here because this is done when we know physics is not simulating // if 'forceRebuild' is true, the geometry is rebuilt. Otherwise a previously built version is used - private void CreateGeom(bool forceRebuild) + // Returns 'true' if the geometry was rebuilt + private bool CreateGeom(bool forceRebuild) { // the mesher thought this was too simple to mesh. Use a native Bullet collision shape. + bool ret = false; if (!_scene.NeedsMeshing(_pbs)) { if (_pbs.ProfileShape == ProfileShape.HalfCircle && _pbs.PathCurve == (byte)Extrusion.Curve1) @@ -917,16 +973,28 @@ public sealed class BSPrim : PhysicsActor if (_size.X == _size.Y && _size.Y == _size.Z && _size.X == _size.Z) { // m_log.DebugFormat("{0}: CreateGeom: Defaulting to sphere of size {1}", LogHeader, _size); - _shapeType = ShapeData.PhysicsShapeType.SHAPE_SPHERE; - // Bullet native objects are scaled by the Bullet engine so pass the size in - _scale = _size; + if (forceRebuild || (_shapeType != ShapeData.PhysicsShapeType.SHAPE_SPHERE)) + { + DetailLog("{0},CreateGeom,sphere", LocalID); + _shapeType = ShapeData.PhysicsShapeType.SHAPE_SPHERE; + // Bullet native objects are scaled by the Bullet engine so pass the size in + _scale = _size; + // TODO: do we need to check for and destroy a mesh or hull that might have been left from before? + ret = true; + } } } else { - // m_log.DebugFormat("{0}: CreateGeom: Defaulting to box. lid={1}, size={2}", LogHeader, LocalID, _size); - _shapeType = ShapeData.PhysicsShapeType.SHAPE_BOX; - _scale = _size; + // m_log.DebugFormat("{0}: CreateGeom: Defaulting to box. lid={1}, type={2}, size={3}", LogHeader, LocalID, _shapeType, _size); + if (forceRebuild || (_shapeType != ShapeData.PhysicsShapeType.SHAPE_BOX)) + { + DetailLog("{0},CreateGeom,box", LocalID); + _shapeType = ShapeData.PhysicsShapeType.SHAPE_BOX; + _scale = _size; + // TODO: do we need to check for and destroy a mesh or hull that might have been left from before? + ret = true; + } } } else @@ -938,6 +1006,7 @@ public sealed class BSPrim : PhysicsActor // physical objects require a hull for interaction. // This will create the mesh if it doesn't already exist CreateGeomHull(); + ret = true; } } else @@ -946,9 +1015,11 @@ public sealed class BSPrim : PhysicsActor { // Static (non-physical) objects only need a mesh for bumping into CreateGeomMesh(); + ret = true; } } } + return ret; } // No locking here because this is done when we know physics is not simulating @@ -961,10 +1032,12 @@ public sealed class BSPrim : PhysicsActor // if this new shape is the same as last time, don't recreate the mesh if (_meshKey == newMeshKey) return; + DetailLog("{0},CreateGeomMesh,create,key={1}", LocalID, _meshKey); // Since we're recreating new, get rid of any previously generated shape if (_meshKey != 0) { // m_log.DebugFormat("{0}: CreateGeom: deleting old mesh. lID={1}, Key={2}", LogHeader, _localID, _meshKey); + DetailLog("{0},CreateGeomMesh,deleteOld,key={1}", LocalID, _meshKey); BulletSimAPI.DestroyMesh(_scene.WorldID, _meshKey); _mesh = null; _meshKey = 0; @@ -981,7 +1054,6 @@ public sealed class BSPrim : PhysicsActor int vi = 0; foreach (OMV.Vector3 vv in vertices) { - // m_log.DebugFormat("{0}: {1}: <{2:0.00}, {3:0.00}, {4:0.00}>", LogHeader, vi / 3, vv.X, vv.Y, vv.Z); verticesAsFloats[vi++] = vv.X; verticesAsFloats[vi++] = vv.Y; verticesAsFloats[vi++] = vv.Z; @@ -995,6 +1067,7 @@ public sealed class BSPrim : PhysicsActor _shapeType = ShapeData.PhysicsShapeType.SHAPE_MESH; // meshes are already scaled by the meshmerizer _scale = new OMV.Vector3(1f, 1f, 1f); + DetailLog("{0},CreateGeomMesh,done", LocalID); return; } @@ -1008,13 +1081,17 @@ public sealed class BSPrim : PhysicsActor // if the hull hasn't changed, don't rebuild it if (newHullKey == _hullKey) return; + DetailLog("{0},CreateGeomHull,create,key={1}", LocalID, _meshKey); + // Since we're recreating new, get rid of any previously generated shape if (_hullKey != 0) { // m_log.DebugFormat("{0}: CreateGeom: deleting old hull. Key={1}", LogHeader, _hullKey); + DetailLog("{0},CreateGeomHull,deleteOldHull,key={1}", LocalID, _meshKey); BulletSimAPI.DestroyHull(_scene.WorldID, _hullKey); _hullKey = 0; _hulls.Clear(); + DetailLog("{0},CreateGeomHull,deleteOldMesh,key={1}", LocalID, _meshKey); BulletSimAPI.DestroyMesh(_scene.WorldID, _meshKey); _mesh = null; // the mesh cannot match either _meshKey = 0; @@ -1111,6 +1188,7 @@ public sealed class BSPrim : PhysicsActor _shapeType = ShapeData.PhysicsShapeType.SHAPE_HULL; // meshes are already scaled by the meshmerizer _scale = new OMV.Vector3(1f, 1f, 1f); + DetailLog("{0},CreateGeomHull,done", LocalID); return; } @@ -1124,47 +1202,21 @@ public sealed class BSPrim : PhysicsActor // Create an object in Bullet if it has not already been created // No locking here because this is done when the physics engine is not simulating - private void CreateObject() + // Returns 'true' if an object was actually created. + private bool CreateObject() { - if (IsRootOfLinkset) - { - // Create a linkset around this object - // CreateLinksetWithCompoundHull(); - CreateLinksetWithConstraints(); - } - else - { - // simple object - // the mesh or hull must have already been created in Bullet - ShapeData shape; - FillShapeInfo(out shape); - // m_log.DebugFormat("{0}: CreateObject: lID={1}, shape={2}", LogHeader, _localID, shape.Type); - BulletSimAPI.CreateObject(_scene.WorldID, shape); - } - } + // this routine is called when objects are rebuilt. - // Create a linkset by creating a compound hull at the root prim that consists of all - // the children. - // NOTE: This does not allow proper collisions with the children prims so it is not a workable solution - void CreateLinksetWithCompoundHull() - { - // If I am the root prim of a linkset, replace my physical shape with all the - // pieces of the children. - // All of the children should have called CreateGeom so they have a hull - // in the physics engine already. Here we pull together all of those hulls - // into one shape. - int totalPrimsInLinkset = _childrenPrims.Count + 1; - // m_log.DebugFormat("{0}: CreateLinkset. Root prim={1}, prims={2}", LogHeader, LocalID, totalPrimsInLinkset); - ShapeData[] shapes = new ShapeData[totalPrimsInLinkset]; - FillShapeInfo(out shapes[0]); - int ii = 1; - foreach (BSPrim prim in _childrenPrims) - { - // m_log.DebugFormat("{0}: CreateLinkset: adding prim {1}", LogHeader, prim.LocalID); - prim.FillShapeInfo(out shapes[ii]); - ii++; - } - BulletSimAPI.CreateLinkset(_scene.WorldID, totalPrimsInLinkset, shapes); + // the mesh or hull must have already been created in Bullet + ShapeData shape; + FillShapeInfo(out shape); + // m_log.DebugFormat("{0}: CreateObject: lID={1}, shape={2}", LogHeader, _localID, shape.Type); + bool ret = BulletSimAPI.CreateObject(_scene.WorldID, shape); + + // the CreateObject() may have recreated the rigid body. Make sure we have the latest. + m_body.Ptr = BulletSimAPI.GetBodyHandle2(_scene.World.Ptr, LocalID); + + return ret; } // Copy prim's info into the BulletSim shape description structure @@ -1186,44 +1238,6 @@ public sealed class BSPrim : PhysicsActor shape.Static = _isPhysical ? ShapeData.numericFalse : ShapeData.numericTrue; } - // Create the linkset by putting constraints between the objects of the set so they cannot move - // relative to each other. - // TODO: make this more effeicient: a large linkset gets rebuilt over and over and prims are added - void CreateLinksetWithConstraints() - { - // m_log.DebugFormat("{0}: CreateLinkset. Root prim={1}, prims={2}", LogHeader, LocalID, _childrenPrims.Count+1); - - // remove any constraints that might be in place - foreach (BSPrim prim in _childrenPrims) - { - // m_log.DebugFormat("{0}: CreateLinkset: RemoveConstraint between root prim {1} and child prim {2}", LogHeader, LocalID, prim.LocalID); - BulletSimAPI.RemoveConstraint(_scene.WorldID, LocalID, prim.LocalID); - } - // create constraints between the root prim and each of the children - foreach (BSPrim prim in _childrenPrims) - { - // m_log.DebugFormat("{0}: CreateLinkset: AddConstraint between root prim {1} and child prim {2}", LogHeader, LocalID, prim.LocalID); - - // Zero motion for children so they don't interpolate - prim.ZeroMotion(); - - // relative position normalized to the root prim - OMV.Vector3 childRelativePosition = (prim._position - this._position) * OMV.Quaternion.Inverse(this._orientation); - - // relative rotation of the child to the parent - OMV.Quaternion relativeRotation = OMV.Quaternion.Inverse(prim._orientation) * this._orientation; - - // this is a constraint that allows no freedom of movement between the two objects - // http://bulletphysics.org/Bullet/phpBB3/viewtopic.php?t=4818 - BulletSimAPI.AddConstraint(_scene.WorldID, LocalID, prim.LocalID, - childRelativePosition, - relativeRotation, - OMV.Vector3.Zero, - OMV.Quaternion.Identity, - OMV.Vector3.Zero, OMV.Vector3.Zero, - OMV.Vector3.Zero, OMV.Vector3.Zero); - } - } // Rebuild the geometry and object. // This is called when the shape changes so we need to recreate the mesh/hull. @@ -1231,8 +1245,8 @@ public sealed class BSPrim : PhysicsActor private void RecreateGeomAndObject() { // m_log.DebugFormat("{0}: RecreateGeomAndObject. lID={1}", LogHeader, _localID); - CreateGeom(true); - CreateObject(); + if (CreateGeom(true)) + CreateObject(); return; } @@ -1252,77 +1266,77 @@ public sealed class BSPrim : PhysicsActor const float POSITION_TOLERANCE = 0.05f; const float ACCELERATION_TOLERANCE = 0.01f; const float ROTATIONAL_VELOCITY_TOLERANCE = 0.01f; - const bool SHOULD_DAMP_UPDATES = false; public void UpdateProperties(EntityProperties entprop) { + /* UpdatedProperties changed = 0; - if (SHOULD_DAMP_UPDATES) + // assign to the local variables so the normal set action does not happen + // if (_position != entprop.Position) + if (!_position.ApproxEquals(entprop.Position, POSITION_TOLERANCE)) { - // assign to the local variables so the normal set action does not happen - // if (_position != entprop.Position) - if (!_position.ApproxEquals(entprop.Position, POSITION_TOLERANCE)) + _position = entprop.Position; + changed |= UpdatedProperties.Position; + } + // if (_orientation != entprop.Rotation) + if (!_orientation.ApproxEquals(entprop.Rotation, ROTATION_TOLERANCE)) + { + _orientation = entprop.Rotation; + changed |= UpdatedProperties.Rotation; + } + // if (_velocity != entprop.Velocity) + if (!_velocity.ApproxEquals(entprop.Velocity, VELOCITY_TOLERANCE)) + { + _velocity = entprop.Velocity; + changed |= UpdatedProperties.Velocity; + } + // if (_acceleration != entprop.Acceleration) + if (!_acceleration.ApproxEquals(entprop.Acceleration, ACCELERATION_TOLERANCE)) + { + _acceleration = entprop.Acceleration; + changed |= UpdatedProperties.Acceleration; + } + // if (_rotationalVelocity != entprop.RotationalVelocity) + if (!_rotationalVelocity.ApproxEquals(entprop.RotationalVelocity, ROTATIONAL_VELOCITY_TOLERANCE)) + { + _rotationalVelocity = entprop.RotationalVelocity; + changed |= UpdatedProperties.RotationalVel; + } + if (changed != 0) + { + // Only update the position of single objects and linkset roots + if (this._parentPrim == null) { - _position = entprop.Position; - // m_log.DebugFormat("{0}: UpdateProperties: id={1}, pos = {2}", LogHeader, LocalID, _position); - changed |= UpdatedProperties.Position; - } - // if (_orientation != entprop.Rotation) - if (!_orientation.ApproxEquals(entprop.Rotation, ROTATION_TOLERANCE)) - { - _orientation = entprop.Rotation; - // m_log.DebugFormat("{0}: UpdateProperties: id={1}, rot = {2}", LogHeader, LocalID, _orientation); - changed |= UpdatedProperties.Rotation; - } - // if (_velocity != entprop.Velocity) - if (!_velocity.ApproxEquals(entprop.Velocity, VELOCITY_TOLERANCE)) - { - _velocity = entprop.Velocity; - // m_log.DebugFormat("{0}: UpdateProperties: velocity = {1}", LogHeader, _velocity); - changed |= UpdatedProperties.Velocity; - } - // if (_acceleration != entprop.Acceleration) - if (!_acceleration.ApproxEquals(entprop.Acceleration, ACCELERATION_TOLERANCE)) - { - _acceleration = entprop.Acceleration; - // m_log.DebugFormat("{0}: UpdateProperties: acceleration = {1}", LogHeader, _acceleration); - changed |= UpdatedProperties.Acceleration; - } - // if (_rotationalVelocity != entprop.RotationalVelocity) - if (!_rotationalVelocity.ApproxEquals(entprop.RotationalVelocity, ROTATIONAL_VELOCITY_TOLERANCE)) - { - _rotationalVelocity = entprop.RotationalVelocity; - // m_log.DebugFormat("{0}: UpdateProperties: rotationalVelocity = {1}", LogHeader, _rotationalVelocity); - changed |= UpdatedProperties.RotationalVel; - } - if (changed != 0) - { - // m_log.DebugFormat("{0}: UpdateProperties: id={1}, c={2}, pos={3}, rot={4}", LogHeader, LocalID, changed, _position, _orientation); - // Only update the position of single objects and linkset roots - if (this._parentPrim == null) - { - // m_log.DebugFormat("{0}: RequestTerseUpdate. id={1}, ch={2}, pos={3}, rot={4}", LogHeader, LocalID, changed, _position, _orientation); - base.RequestPhysicsterseUpdate(); - } + base.RequestPhysicsterseUpdate(); } } + */ + + // Don't check for damping here -- it's done in BulletSim and SceneObjectPart. + + // Updates only for individual prims and for the root object of a linkset. + if (_linkset.IsRoot(this)) + { + // Assign to the local variables so the normal set action does not happen + _position = entprop.Position; + _orientation = entprop.Rotation; + _velocity = entprop.Velocity; + _acceleration = entprop.Acceleration; + _rotationalVelocity = entprop.RotationalVelocity; + + // m_log.DebugFormat("{0}: RequestTerseUpdate. id={1}, ch={2}, pos={3}, rot={4}, vel={5}, acc={6}, rvel={7}", + // LogHeader, LocalID, changed, _position, _orientation, _velocity, _acceleration, _rotationalVelocity); + DetailLog("{0},UpdateProperties,call,pos={1},orient={2},vel={3},accel={4},rotVel={5}", + LocalID, _position, _orientation, _velocity, _acceleration, _rotationalVelocity); + + base.RequestPhysicsterseUpdate(); + } else { - // Don't check for damping here -- it's done in BulletSim and SceneObjectPart. - - // Only updates only for individual prims and for the root object of a linkset. - if (this._parentPrim == null) - { - // Assign to the local variables so the normal set action does not happen - _position = entprop.Position; - _orientation = entprop.Rotation; - _velocity = entprop.Velocity; - _acceleration = entprop.Acceleration; - _rotationalVelocity = entprop.RotationalVelocity; - // m_log.DebugFormat("{0}: RequestTerseUpdate. id={1}, ch={2}, pos={3}, rot={4}, vel={5}, acc={6}, rvel={7}", - // LogHeader, LocalID, changed, _position, _orientation, _velocity, _acceleration, _rotationalVelocity); - base.RequestPhysicsterseUpdate(); - } + // For debugging, we can also report the movement of children + DetailLog("{0},UpdateProperties,child,pos={1},orient={2},vel={3},accel={4},rotVel={5}", + LocalID, entprop.Position, entprop.Rotation, entprop.Velocity, + entprop.Acceleration, entprop.RotationalVelocity); } } @@ -1362,5 +1376,11 @@ public sealed class BSPrim : PhysicsActor collisionCollection.Clear(); } } + + // Invoke the detailed logger and output something if it's enabled. + private void DetailLog(string msg, params Object[] args) + { + Scene.PhysicsLogging.Write(msg, args); + } } } diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs b/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs index 417cb5f3f0..28d5cb5b62 100644 --- a/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs @@ -29,12 +29,14 @@ using System.Collections.Generic; using System.Runtime.InteropServices; using System.Text; using System.Threading; +using OpenSim.Framework; +using OpenSim.Region.Framework; +using OpenSim.Region.CoreModules; +using Logging = OpenSim.Region.CoreModules.Framework.Statistics.Logging; +using OpenSim.Region.Physics.Manager; using Nini.Config; using log4net; -using OpenSim.Framework; -using OpenSim.Region.Physics.Manager; using OpenMetaverse; -using OpenSim.Region.Framework; // TODOs for BulletSim (for BSScene, BSPrim, BSCharacter and BulletSim) // Debug linkset @@ -44,15 +46,17 @@ using OpenSim.Region.Framework; // Compute physics FPS reasonably // Based on material, set density and friction // More efficient memory usage when passing hull information from BSPrim to BulletSim +// Move all logic out of the C++ code and into the C# code for easier future modifications. // Four states of prim: Physical, regular, phantom and selected. Are we modeling these correctly? // In SL one can set both physical and phantom (gravity, does not effect others, makes collisions with ground) // At the moment, physical and phantom causes object to drop through the terrain // Physical phantom objects and related typing (collision options ) +// Use collision masks for collision with terrain and phantom objects // Check out llVolumeDetect. Must do something for that. // Should prim.link() and prim.delink() membership checking happen at taint time? +// changing the position and orientation of a linked prim must rebuild the constraint with the root. // Mesh sharing. Use meshHash to tell if we already have a hull of that shape and only create once // Do attachments need to be handled separately? Need collision events. Do not collide with VolumeDetect -// Use collision masks for collision with terrain and phantom objects // Implement the genCollisions feature in BulletSim::SetObjectProperties (don't pass up unneeded collisions) // Implement LockAngularMotion // Decide if clearing forces is the right thing to do when setting position (BulletSim::SetObjectTranslation) @@ -60,9 +64,6 @@ using OpenSim.Region.Framework; // Remove mesh and Hull stuff. Use mesh passed to bullet and use convexdecom from bullet. // Add PID movement operations. What does ScenePresence.MoveToTarget do? // Check terrain size. 128 or 127? -// Multiple contact points on collision? -// See code in ode::near... calls to collision_accounting_events() -// (This might not be a problem. ODE collects all the collisions with one object in one tick.) // Raycast // namespace OpenSim.Region.Physics.BulletSPlugin @@ -72,6 +73,8 @@ public class BSScene : PhysicsScene, IPhysicsParameters private static readonly ILog m_log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); private static readonly string LogHeader = "[BULLETS SCENE]"; + public void DebugLog(string mm, params Object[] xx) { if (shouldDebugLog) m_log.DebugFormat(mm, xx); } + public string BulletSimVersion = "?"; private Dictionary m_avatars = new Dictionary(); @@ -84,6 +87,9 @@ public class BSScene : PhysicsScene, IPhysicsParameters private uint m_worldID; public uint WorldID { get { return m_worldID; } } + // let my minuions use my logger + public ILog Logger { get { return m_log; } } + private bool m_initialized = false; private int m_detailedStatsStep = 0; @@ -100,11 +106,24 @@ public class BSScene : PhysicsScene, IPhysicsParameters get { return m_sculptLOD; } } + private BulletSim m_worldSim; + public BulletSim World + { + get { return m_worldSim; } + } + private BSConstraintCollection m_constraintCollection; + public BSConstraintCollection Constraints + { + get { return m_constraintCollection; } + } + private int m_maxSubSteps; private float m_fixedTimeStep; private long m_simulationStep = 0; public long SimulationStep { get { return m_simulationStep; } } + public float LastSimulatedTimestep { get; private set; } + // A value of the time now so all the collision and update routines do not have to get their own // Set to 'now' just before all the prims and actors are called for collisions and updates private int m_simulationNowTime; @@ -121,6 +140,9 @@ public class BSScene : PhysicsScene, IPhysicsParameters private bool _meshSculptedPrim = true; // cause scuplted prims to get meshed private bool _forceSimplePrimMeshing = false; // if a cube or sphere, let Bullet do internal shapes + public float PID_D { get; private set; } // derivative + public float PID_P { get; private set; } // proportional + public const uint TERRAIN_ID = 0; // OpenSim senses terrain with a localID of zero public const uint GROUNDPLANE_ID = 1; @@ -147,8 +169,20 @@ public class BSScene : PhysicsScene, IPhysicsParameters ConfigurationParameters[] m_params; GCHandle m_paramsHandle; + public bool shouldDebugLog { get; private set; } + private BulletSimAPI.DebugLogCallback m_DebugLogCallbackHandle; + // Sometimes you just have to log everything. + public Logging.LogWriter PhysicsLogging; + private bool m_physicsLoggingEnabled; + private string m_physicsLoggingDir; + private string m_physicsLoggingPrefix; + private int m_physicsLoggingFileMinutes; + + private bool m_vehicleLoggingEnabled; + public bool VehicleLoggingEnabled { get { return m_vehicleLoggingEnabled; } } + public BSScene(string identifier) { m_initialized = false; @@ -169,17 +203,32 @@ public class BSScene : PhysicsScene, IPhysicsParameters m_updateArray = new EntityProperties[m_maxUpdatesPerFrame]; m_updateArrayPinnedHandle = GCHandle.Alloc(m_updateArray, GCHandleType.Pinned); + // Enable very detailed logging. + // By creating an empty logger when not logging, the log message invocation code + // can be left in and every call doesn't have to check for null. + if (m_physicsLoggingEnabled) + { + PhysicsLogging = new Logging.LogWriter(m_physicsLoggingDir, m_physicsLoggingPrefix, m_physicsLoggingFileMinutes); + } + else + { + PhysicsLogging = new Logging.LogWriter(); + } + // Get the version of the DLL // TODO: this doesn't work yet. Something wrong with marshaling the returned string. // BulletSimVersion = BulletSimAPI.GetVersion(); // m_log.WarnFormat("{0}: BulletSim.dll version='{1}'", LogHeader, BulletSimVersion); // if Debug, enable logging from the unmanaged code - if (m_log.IsDebugEnabled) + if (m_log.IsDebugEnabled || PhysicsLogging.Enabled) { m_log.DebugFormat("{0}: Initialize: Setting debug callback for unmanaged code", LogHeader); - // the handle is saved to it doesn't get freed after this call - m_DebugLogCallbackHandle = new BulletSimAPI.DebugLogCallback(BulletLogger); + if (PhysicsLogging.Enabled) + m_DebugLogCallbackHandle = new BulletSimAPI.DebugLogCallback(BulletLoggerPhysLog); + else + m_DebugLogCallbackHandle = new BulletSimAPI.DebugLogCallback(BulletLogger); + // the handle is saved in a variable to make sure it doesn't get freed after this call BulletSimAPI.SetDebugLogCallback(m_DebugLogCallbackHandle); } @@ -194,6 +243,11 @@ public class BSScene : PhysicsScene, IPhysicsParameters m_maxCollisionsPerFrame, m_collisionArrayPinnedHandle.AddrOfPinnedObject(), m_maxUpdatesPerFrame, m_updateArrayPinnedHandle.AddrOfPinnedObject()); + // Initialization to support the transition to a new API which puts most of the logic + // into the C# code so it is easier to modify and add to. + m_worldSim = new BulletSim(m_worldID, BulletSimAPI.GetSimHandle2(m_worldID)); + m_constraintCollection = new BSConstraintCollection(World); + m_initialized = true; } @@ -202,110 +256,27 @@ public class BSScene : PhysicsScene, IPhysicsParameters private void GetInitialParameterValues(IConfigSource config) { ConfigurationParameters parms = new ConfigurationParameters(); + m_params[0] = parms; - _meshSculptedPrim = true; // mesh sculpted prims - _forceSimplePrimMeshing = false; // use complex meshing if called for - - m_meshLOD = 8f; - m_sculptLOD = 32f; - - m_detailedStatsStep = 0; // disabled - - m_maxSubSteps = 10; - m_fixedTimeStep = 1f / 60f; - m_maxCollisionsPerFrame = 2048; - m_maxUpdatesPerFrame = 2048; - m_maximumObjectMass = 10000.01f; - - parms.defaultFriction = 0.5f; - parms.defaultDensity = 10.000006836f; // Aluminum g/cm3 - parms.defaultRestitution = 0f; - parms.collisionMargin = 0.0f; - parms.gravity = -9.80665f; - - parms.linearDamping = 0.0f; - parms.angularDamping = 0.0f; - parms.deactivationTime = 0.2f; - parms.linearSleepingThreshold = 0.8f; - parms.angularSleepingThreshold = 1.0f; - parms.ccdMotionThreshold = 0.0f; // set to zero to disable - parms.ccdSweptSphereRadius = 0.0f; - parms.contactProcessingThreshold = 0.1f; - - parms.terrainFriction = 0.5f; - parms.terrainHitFraction = 0.8f; - parms.terrainRestitution = 0f; - parms.avatarFriction = 0.5f; - parms.avatarRestitution = 0.0f; - parms.avatarDensity = 60f; - parms.avatarCapsuleRadius = 0.37f; - parms.avatarCapsuleHeight = 1.5f; // 2.140599f - parms.avatarContactProcessingThreshold = 0.1f; - - parms.maxPersistantManifoldPoolSize = 0f; - parms.shouldDisableContactPoolDynamicAllocation = ConfigurationParameters.numericTrue; - parms.shouldForceUpdateAllAabbs = ConfigurationParameters.numericFalse; - parms.shouldRandomizeSolverOrder = ConfigurationParameters.numericFalse; - parms.shouldSplitSimulationIslands = ConfigurationParameters.numericFalse; - parms.shouldEnableFrictionCaching = ConfigurationParameters.numericFalse; - parms.numberOfSolverIterations = 0f; // means use default + SetParameterDefaultValues(); if (config != null) { // If there are specifications in the ini file, use those values - // WHEN ADDING OR UPDATING THIS SECTION, BE SURE TO UPDATE OpenSimDefaults.ini - // ALSO REMEMBER TO UPDATE THE RUNTIME SETTING OF THE PARAMETERS. IConfig pConfig = config.Configs["BulletSim"]; if (pConfig != null) { - _meshSculptedPrim = pConfig.GetBoolean("MeshSculptedPrim", _meshSculptedPrim); - _forceSimplePrimMeshing = pConfig.GetBoolean("ForceSimplePrimMeshing", _forceSimplePrimMeshing); + SetParameterConfigurationValues(pConfig); - m_detailedStatsStep = pConfig.GetInt("DetailedStatsStep", m_detailedStatsStep); - m_meshLOD = pConfig.GetFloat("MeshLevelOfDetail", m_meshLOD); - m_sculptLOD = pConfig.GetFloat("SculptLevelOfDetail", m_sculptLOD); - - m_maxSubSteps = pConfig.GetInt("MaxSubSteps", m_maxSubSteps); - m_fixedTimeStep = pConfig.GetFloat("FixedTimeStep", m_fixedTimeStep); - m_maxCollisionsPerFrame = pConfig.GetInt("MaxCollisionsPerFrame", m_maxCollisionsPerFrame); - m_maxUpdatesPerFrame = pConfig.GetInt("MaxUpdatesPerFrame", m_maxUpdatesPerFrame); - m_maximumObjectMass = pConfig.GetFloat("MaxObjectMass", m_maximumObjectMass); - - parms.defaultFriction = pConfig.GetFloat("DefaultFriction", parms.defaultFriction); - parms.defaultDensity = pConfig.GetFloat("DefaultDensity", parms.defaultDensity); - parms.defaultRestitution = pConfig.GetFloat("DefaultRestitution", parms.defaultRestitution); - parms.collisionMargin = pConfig.GetFloat("CollisionMargin", parms.collisionMargin); - parms.gravity = pConfig.GetFloat("Gravity", parms.gravity); - - parms.linearDamping = pConfig.GetFloat("LinearDamping", parms.linearDamping); - parms.angularDamping = pConfig.GetFloat("AngularDamping", parms.angularDamping); - parms.deactivationTime = pConfig.GetFloat("DeactivationTime", parms.deactivationTime); - parms.linearSleepingThreshold = pConfig.GetFloat("LinearSleepingThreshold", parms.linearSleepingThreshold); - parms.angularSleepingThreshold = pConfig.GetFloat("AngularSleepingThreshold", parms.angularSleepingThreshold); - parms.ccdMotionThreshold = pConfig.GetFloat("CcdMotionThreshold", parms.ccdMotionThreshold); - parms.ccdSweptSphereRadius = pConfig.GetFloat("CcdSweptSphereRadius", parms.ccdSweptSphereRadius); - parms.contactProcessingThreshold = pConfig.GetFloat("ContactProcessingThreshold", parms.contactProcessingThreshold); - - parms.terrainFriction = pConfig.GetFloat("TerrainFriction", parms.terrainFriction); - parms.terrainHitFraction = pConfig.GetFloat("TerrainHitFraction", parms.terrainHitFraction); - parms.terrainRestitution = pConfig.GetFloat("TerrainRestitution", parms.terrainRestitution); - parms.avatarFriction = pConfig.GetFloat("AvatarFriction", parms.avatarFriction); - parms.avatarRestitution = pConfig.GetFloat("AvatarRestitution", parms.avatarRestitution); - parms.avatarDensity = pConfig.GetFloat("AvatarDensity", parms.avatarDensity); - parms.avatarCapsuleRadius = pConfig.GetFloat("AvatarCapsuleRadius", parms.avatarCapsuleRadius); - parms.avatarCapsuleHeight = pConfig.GetFloat("AvatarCapsuleHeight", parms.avatarCapsuleHeight); - parms.avatarContactProcessingThreshold = pConfig.GetFloat("AvatarContactProcessingThreshold", parms.avatarContactProcessingThreshold); - - parms.maxPersistantManifoldPoolSize = pConfig.GetFloat("MaxPersistantManifoldPoolSize", parms.maxPersistantManifoldPoolSize); - parms.shouldDisableContactPoolDynamicAllocation = ParamBoolean(pConfig, "ShouldDisableContactPoolDynamicAllocation", parms.shouldDisableContactPoolDynamicAllocation); - parms.shouldForceUpdateAllAabbs = ParamBoolean(pConfig, "ShouldForceUpdateAllAabbs", parms.shouldForceUpdateAllAabbs); - parms.shouldRandomizeSolverOrder = ParamBoolean(pConfig, "ShouldRandomizeSolverOrder", parms.shouldRandomizeSolverOrder); - parms.shouldSplitSimulationIslands = ParamBoolean(pConfig, "ShouldSplitSimulationIslands", parms.shouldSplitSimulationIslands); - parms.shouldEnableFrictionCaching = ParamBoolean(pConfig, "ShouldEnableFrictionCaching", parms.shouldEnableFrictionCaching); - parms.numberOfSolverIterations = pConfig.GetFloat("NumberOfSolverIterations", parms.numberOfSolverIterations); + // Very detailed logging for physics debugging + m_physicsLoggingEnabled = pConfig.GetBoolean("PhysicsLoggingEnabled", false); + m_physicsLoggingDir = pConfig.GetString("PhysicsLoggingDir", "."); + m_physicsLoggingPrefix = pConfig.GetString("PhysicsLoggingPrefix", "physics-"); + m_physicsLoggingFileMinutes = pConfig.GetInt("PhysicsLoggingFileMinutes", 5); + // Very detailed logging for vehicle debugging + m_vehicleLoggingEnabled = pConfig.GetBoolean("VehicleLoggingEnabled", false); } } - m_params[0] = parms; } // A helper function that handles a true/false parameter and returns the proper float number encoding @@ -323,12 +294,17 @@ public class BSScene : PhysicsScene, IPhysicsParameters return ret; } - // Called directly from unmanaged code so don't do much private void BulletLogger(string msg) { m_log.Debug("[BULLETS UNMANAGED]:" + msg); } + + // Called directly from unmanaged code so don't do much + private void BulletLoggerPhysLog(string msg) + { + PhysicsLogging.Write("[BULLETS UNMANAGED]:" + msg); + } public override PhysicsActor AddAvatar(string avName, Vector3 position, Vector3 size, bool isFlying) { @@ -339,6 +315,9 @@ public class BSScene : PhysicsScene, IPhysicsParameters public override PhysicsActor AddAvatar(uint localID, string avName, Vector3 position, Vector3 size, bool isFlying) { // m_log.DebugFormat("{0}: AddAvatar: {1}", LogHeader, avName); + + if (!m_initialized) return null; + BSCharacter actor = new BSCharacter(localID, avName, this, position, size, isFlying); lock (m_avatars) m_avatars.Add(localID, actor); return actor; @@ -347,34 +326,47 @@ public class BSScene : PhysicsScene, IPhysicsParameters public override void RemoveAvatar(PhysicsActor actor) { // m_log.DebugFormat("{0}: RemoveAvatar", LogHeader); - if (actor is BSCharacter) + + if (!m_initialized) return; + + BSCharacter bsactor = actor as BSCharacter; + if (bsactor != null) { - ((BSCharacter)actor).Destroy(); - } - try - { - lock (m_avatars) m_avatars.Remove(actor.LocalID); - } - catch (Exception e) - { - m_log.WarnFormat("{0}: Attempt to remove avatar that is not in physics scene: {1}", LogHeader, e); + try + { + lock (m_avatars) m_avatars.Remove(actor.LocalID); + } + catch (Exception e) + { + m_log.WarnFormat("{0}: Attempt to remove avatar that is not in physics scene: {1}", LogHeader, e); + } + bsactor.Destroy(); + // bsactor.dispose(); } } public override void RemovePrim(PhysicsActor prim) { - // m_log.DebugFormat("{0}: RemovePrim", LogHeader); - if (prim is BSPrim) + if (!m_initialized) return; + + BSPrim bsprim = prim as BSPrim; + if (bsprim != null) { - ((BSPrim)prim).Destroy(); + m_log.DebugFormat("{0}: RemovePrim. id={1}/{2}", LogHeader, bsprim.Name, bsprim.LocalID); + try + { + lock (m_prims) m_prims.Remove(bsprim.LocalID); + } + catch (Exception e) + { + m_log.ErrorFormat("{0}: Attempt to remove prim that is not in physics scene: {1}", LogHeader, e); + } + bsprim.Destroy(); + // bsprim.dispose(); } - try + else { - lock (m_prims) m_prims.Remove(prim.LocalID); - } - catch (Exception e) - { - m_log.WarnFormat("{0}: Attempt to remove prim that is not in physics scene: {1}", LogHeader, e); + m_log.ErrorFormat("{0}: Attempt to remove prim that is not a BSPrim type.", LogHeader); } } @@ -382,6 +374,9 @@ public class BSScene : PhysicsScene, IPhysicsParameters Vector3 size, Quaternion rotation, bool isPhysical, uint localID) { // m_log.DebugFormat("{0}: AddPrimShape2: {1}", LogHeader, primName); + + if (!m_initialized) return null; + BSPrim prim = new BSPrim(localID, primName, this, position, size, rotation, pbs, isPhysical); lock (m_prims) m_prims.Add(localID, prim); return prim; @@ -400,6 +395,8 @@ public class BSScene : PhysicsScene, IPhysicsParameters int collidersCount; IntPtr collidersPtr; + LastSimulatedTimestep = timeStep; + // prevent simulation until we've been initialized if (!m_initialized) return 10.0f; @@ -459,7 +456,6 @@ public class BSScene : PhysicsScene, IPhysicsParameters for (int ii = 0; ii < updatedEntityCount; ii++) { EntityProperties entprop = m_updateArray[ii]; - // m_log.DebugFormat("{0}: entprop[{1}]: id={2}, pos={3}", LogHeader, ii, entprop.ID, entprop.Position); BSPrim prim; if (m_prims.TryGetValue(entprop.ID, out prim)) { @@ -532,8 +528,17 @@ public class BSScene : PhysicsScene, IPhysicsParameters }); } + // Someday we will have complex terrain with caves and tunnels + // For the moment, it's flat and convex + public float GetTerrainHeightAtXYZ(Vector3 loc) + { + return GetTerrainHeightAtXY(loc.X, loc.Y); + } + public float GetTerrainHeightAtXY(float tX, float tY) { + if (tX < 0 || tX >= Constants.RegionSize || tY < 0 || tY >= Constants.RegionSize) + return 30; return m_heightMap[((int)tX) * Constants.RegionSize + ((int)tY)]; } @@ -555,6 +560,33 @@ public class BSScene : PhysicsScene, IPhysicsParameters public override void Dispose() { // m_log.DebugFormat("{0}: Dispose()", LogHeader); + + // make sure no stepping happens while we're deleting stuff + m_initialized = false; + + if (m_constraintCollection != null) + { + m_constraintCollection.Dispose(); + m_constraintCollection = null; + } + + foreach (KeyValuePair kvp in m_avatars) + { + kvp.Value.Destroy(); + } + m_avatars.Clear(); + + foreach (KeyValuePair kvp in m_prims) + { + kvp.Value.Destroy(); + } + m_prims.Clear(); + + // Anything left in the unmanaged code should be cleaned out + BulletSimAPI.Shutdown(WorldID); + + // Not logging any more + PhysicsLogging.Close(); } public override Dictionary GetTopColliders() @@ -680,10 +712,12 @@ public class BSScene : PhysicsScene, IPhysicsParameters } // The calls to the PhysicsActors can't directly call into the physics engine - // because it might be busy. We we delay changes to a known time. + // because it might be busy. We delay changes to a known time. // We rely on C#'s closure to save and restore the context for the delegate. public void TaintedObject(TaintCallback callback) { + if (!m_initialized) return; + lock (_taintLock) _taintedObjects.Add(callback); return; @@ -757,61 +791,392 @@ public class BSScene : PhysicsScene, IPhysicsParameters } #endregion Vehicles - #region Runtime settable parameters - public static PhysParameterEntry[] SettableParameters = new PhysParameterEntry[] + #region Parameters + + delegate void ParamUser(BSScene scene, IConfig conf, string paramName, float val); + delegate float ParamGet(BSScene scene); + delegate void ParamSet(BSScene scene, string paramName, uint localID, float val); + + private struct ParameterDefn { - new PhysParameterEntry("MeshLOD", "Level of detail to render meshes (32, 16, 8 or 4. 32=most detailed)"), - new PhysParameterEntry("SculptLOD", "Level of detail to render sculpties (32, 16, 8 or 4. 32=most detailed)"), - new PhysParameterEntry("MaxSubStep", "In simulation step, maximum number of substeps"), - new PhysParameterEntry("FixedTimeStep", "In simulation step, seconds of one substep (1/60)"), - new PhysParameterEntry("MaxObjectMass", "Maximum object mass (10000.01)"), - new PhysParameterEntry("DetailedStats", "Frames between outputting detailed phys stats. Zero is off"), + public string name; + public string desc; + public float defaultValue; + public ParamUser userParam; + public ParamGet getter; + public ParamSet setter; + public ParameterDefn(string n, string d, float v, ParamUser u, ParamGet g, ParamSet s) + { + name = n; + desc = d; + defaultValue = v; + userParam = u; + getter = g; + setter = s; + } + } - new PhysParameterEntry("DefaultFriction", "Friction factor used on new objects"), - new PhysParameterEntry("DefaultDensity", "Density for new objects" ), - new PhysParameterEntry("DefaultRestitution", "Bouncyness of an object" ), - // new PhysParameterEntry("CollisionMargin", "Margin around objects before collisions are calculated (must be zero!!)" ), - new PhysParameterEntry("Gravity", "Vertical force of gravity (negative means down)" ), + // List of all of the externally visible parameters. + // For each parameter, this table maps a text name to getter and setters. + // To add a new externally referencable/settable parameter, add the paramter storage + // location somewhere in the program and make an entry in this table with the + // getters and setters. + // To add a new variable, it is easiest to find an existing definition and copy it. + // Parameter values are floats. Booleans are converted to a floating value. + // + // A ParameterDefn() takes the following parameters: + // -- the text name of the parameter. This is used for console input and ini file. + // -- a short text description of the parameter. This shows up in the console listing. + // -- a delegate for fetching the parameter from the ini file. + // Should handle fetching the right type from the ini file and converting it. + // -- a delegate for getting the value as a float + // -- a delegate for setting the value from a float + // + // The single letter parameters for the delegates are: + // s = BSScene + // p = string parameter name + // l = localID of referenced object + // v = float value + // cf = parameter configuration class (for fetching values from ini file) + private ParameterDefn[] ParameterDefinitions = + { + new ParameterDefn("MeshSculptedPrim", "Whether to create meshes for sculpties", + ConfigurationParameters.numericTrue, + (s,cf,p,v) => { s._meshSculptedPrim = cf.GetBoolean(p, s.BoolNumeric(v)); }, + (s) => { return s.NumericBool(s._meshSculptedPrim); }, + (s,p,l,v) => { s._meshSculptedPrim = s.BoolNumeric(v); } ), + new ParameterDefn("ForceSimplePrimMeshing", "If true, only use primitive meshes for objects", + ConfigurationParameters.numericFalse, + (s,cf,p,v) => { s._forceSimplePrimMeshing = cf.GetBoolean(p, s.BoolNumeric(v)); }, + (s) => { return s.NumericBool(s._forceSimplePrimMeshing); }, + (s,p,l,v) => { s._forceSimplePrimMeshing = s.BoolNumeric(v); } ), - new PhysParameterEntry("LinearDamping", "Factor to damp linear movement per second (0.0 - 1.0)" ), - new PhysParameterEntry("AngularDamping", "Factor to damp angular movement per second (0.0 - 1.0)" ), - new PhysParameterEntry("DeactivationTime", "Seconds before considering an object potentially static" ), - new PhysParameterEntry("LinearSleepingThreshold", "Seconds to measure linear movement before considering static" ), - new PhysParameterEntry("AngularSleepingThreshold", "Seconds to measure angular movement before considering static" ), - new PhysParameterEntry("CcdMotionThreshold", "Continuious collision detection threshold (0 means no CCD)" ), - new PhysParameterEntry("CcdSweptSphereRadius", "Continuious collision detection test radius" ), - new PhysParameterEntry("ContactProcessingThreshold", "Distance between contacts before doing collision check" ), - // Can only change the following at initialization time. Change the INI file and reboot. - new PhysParameterEntry("MaxPersistantManifoldPoolSize", "Number of manifolds pooled (0 means default)"), - new PhysParameterEntry("ShouldDisableContactPoolDynamicAllocation", "Enable to allow large changes in object count"), - new PhysParameterEntry("ShouldForceUpdateAllAabbs", "Enable to recomputer AABBs every simulator step"), - new PhysParameterEntry("ShouldRandomizeSolverOrder", "Enable for slightly better stacking interaction"), - new PhysParameterEntry("ShouldSplitSimulationIslands", "Enable splitting active object scanning islands"), - new PhysParameterEntry("ShouldEnableFrictionCaching", "Enable friction computation caching"), - new PhysParameterEntry("NumberOfSolverIterations", "Number of internal iterations (0 means default)"), + new ParameterDefn("MeshLOD", "Level of detail to render meshes (32, 16, 8 or 4. 32=most detailed)", + 8f, + (s,cf,p,v) => { s.m_meshLOD = cf.GetInt(p, (int)v); }, + (s) => { return (float)s.m_meshLOD; }, + (s,p,l,v) => { s.m_meshLOD = (int)v; } ), + new ParameterDefn("SculptLOD", "Level of detail to render sculpties (32, 16, 8 or 4. 32=most detailed)", + 32, + (s,cf,p,v) => { s.m_sculptLOD = cf.GetInt(p, (int)v); }, + (s) => { return (float)s.m_sculptLOD; }, + (s,p,l,v) => { s.m_sculptLOD = (int)v; } ), - new PhysParameterEntry("Friction", "Set friction parameter for a specific object" ), - new PhysParameterEntry("Restitution", "Set restitution parameter for a specific object" ), + new ParameterDefn("MaxSubStep", "In simulation step, maximum number of substeps", + 10f, + (s,cf,p,v) => { s.m_maxSubSteps = cf.GetInt(p, (int)v); }, + (s) => { return (float)s.m_maxSubSteps; }, + (s,p,l,v) => { s.m_maxSubSteps = (int)v; } ), + new ParameterDefn("FixedTimeStep", "In simulation step, seconds of one substep (1/60)", + 1f / 60f, + (s,cf,p,v) => { s.m_fixedTimeStep = cf.GetFloat(p, v); }, + (s) => { return (float)s.m_fixedTimeStep; }, + (s,p,l,v) => { s.m_fixedTimeStep = v; } ), + new ParameterDefn("MaxCollisionsPerFrame", "Max collisions returned at end of each frame", + 2048f, + (s,cf,p,v) => { s.m_maxCollisionsPerFrame = cf.GetInt(p, (int)v); }, + (s) => { return (float)s.m_maxCollisionsPerFrame; }, + (s,p,l,v) => { s.m_maxCollisionsPerFrame = (int)v; } ), + new ParameterDefn("MaxUpdatesPerFrame", "Max updates returned at end of each frame", + 8000f, + (s,cf,p,v) => { s.m_maxUpdatesPerFrame = cf.GetInt(p, (int)v); }, + (s) => { return (float)s.m_maxUpdatesPerFrame; }, + (s,p,l,v) => { s.m_maxUpdatesPerFrame = (int)v; } ), + new ParameterDefn("MaxObjectMass", "Maximum object mass (10000.01)", + 10000.01f, + (s,cf,p,v) => { s.m_maximumObjectMass = cf.GetFloat(p, v); }, + (s) => { return (float)s.m_maximumObjectMass; }, + (s,p,l,v) => { s.m_maximumObjectMass = v; } ), - new PhysParameterEntry("Friction", "Set friction parameter for a specific object" ), - new PhysParameterEntry("Restitution", "Set restitution parameter for a specific object" ), + new ParameterDefn("PID_D", "Derivitive factor for motion smoothing", + 2200f, + (s,cf,p,v) => { s.PID_D = cf.GetFloat(p, v); }, + (s) => { return (float)s.PID_D; }, + (s,p,l,v) => { s.PID_D = v; } ), + new ParameterDefn("PID_P", "Parameteric factor for motion smoothing", + 900f, + (s,cf,p,v) => { s.PID_P = cf.GetFloat(p, v); }, + (s) => { return (float)s.PID_P; }, + (s,p,l,v) => { s.PID_P = v; } ), - new PhysParameterEntry("TerrainFriction", "Factor to reduce movement against terrain surface" ), - new PhysParameterEntry("TerrainHitFraction", "Distance to measure hit collisions" ), - new PhysParameterEntry("TerrainRestitution", "Bouncyness" ), - new PhysParameterEntry("AvatarFriction", "Factor to reduce movement against an avatar. Changed on avatar recreation." ), - new PhysParameterEntry("AvatarDensity", "Density of an avatar. Changed on avatar recreation." ), - new PhysParameterEntry("AvatarRestitution", "Bouncyness. Changed on avatar recreation." ), - new PhysParameterEntry("AvatarCapsuleRadius", "Radius of space around an avatar" ), - new PhysParameterEntry("AvatarCapsuleHeight", "Default height of space around avatar" ), - new PhysParameterEntry("AvatarContactProcessingThreshold", "Distance from capsule to check for collisions") + new ParameterDefn("DefaultFriction", "Friction factor used on new objects", + 0.5f, + (s,cf,p,v) => { s.m_params[0].defaultFriction = cf.GetFloat(p, v); }, + (s) => { return s.m_params[0].defaultFriction; }, + (s,p,l,v) => { s.m_params[0].defaultFriction = v; } ), + new ParameterDefn("DefaultDensity", "Density for new objects" , + 10.000006836f, // Aluminum g/cm3 + (s,cf,p,v) => { s.m_params[0].defaultDensity = cf.GetFloat(p, v); }, + (s) => { return s.m_params[0].defaultDensity; }, + (s,p,l,v) => { s.m_params[0].defaultDensity = v; } ), + new ParameterDefn("DefaultRestitution", "Bouncyness of an object" , + 0f, + (s,cf,p,v) => { s.m_params[0].defaultRestitution = cf.GetFloat(p, v); }, + (s) => { return s.m_params[0].defaultRestitution; }, + (s,p,l,v) => { s.m_params[0].defaultRestitution = v; } ), + new ParameterDefn("CollisionMargin", "Margin around objects before collisions are calculated (must be zero!)", + 0f, + (s,cf,p,v) => { s.m_params[0].collisionMargin = cf.GetFloat(p, v); }, + (s) => { return s.m_params[0].collisionMargin; }, + (s,p,l,v) => { s.m_params[0].collisionMargin = v; } ), + new ParameterDefn("Gravity", "Vertical force of gravity (negative means down)", + -9.80665f, + (s,cf,p,v) => { s.m_params[0].gravity = cf.GetFloat(p, v); }, + (s) => { return s.m_params[0].gravity; }, + (s,p,l,v) => { s.m_params[0].gravity = v; s.TaintedUpdateParameter(p,l,v); } ), + + + new ParameterDefn("LinearDamping", "Factor to damp linear movement per second (0.0 - 1.0)", + 0f, + (s,cf,p,v) => { s.m_params[0].linearDamping = cf.GetFloat(p, v); }, + (s) => { return s.m_params[0].linearDamping; }, + (s,p,l,v) => { s.UpdateParameterPrims(ref s.m_params[0].linearDamping, p, l, v); } ), + new ParameterDefn("AngularDamping", "Factor to damp angular movement per second (0.0 - 1.0)", + 0f, + (s,cf,p,v) => { s.m_params[0].angularDamping = cf.GetFloat(p, v); }, + (s) => { return s.m_params[0].angularDamping; }, + (s,p,l,v) => { s.UpdateParameterPrims(ref s.m_params[0].angularDamping, p, l, v); } ), + new ParameterDefn("DeactivationTime", "Seconds before considering an object potentially static", + 0.2f, + (s,cf,p,v) => { s.m_params[0].deactivationTime = cf.GetFloat(p, v); }, + (s) => { return s.m_params[0].deactivationTime; }, + (s,p,l,v) => { s.UpdateParameterPrims(ref s.m_params[0].deactivationTime, p, l, v); } ), + new ParameterDefn("LinearSleepingThreshold", "Seconds to measure linear movement before considering static", + 0.8f, + (s,cf,p,v) => { s.m_params[0].linearSleepingThreshold = cf.GetFloat(p, v); }, + (s) => { return s.m_params[0].linearSleepingThreshold; }, + (s,p,l,v) => { s.UpdateParameterPrims(ref s.m_params[0].linearSleepingThreshold, p, l, v); } ), + new ParameterDefn("AngularSleepingThreshold", "Seconds to measure angular movement before considering static", + 1.0f, + (s,cf,p,v) => { s.m_params[0].angularSleepingThreshold = cf.GetFloat(p, v); }, + (s) => { return s.m_params[0].angularSleepingThreshold; }, + (s,p,l,v) => { s.UpdateParameterPrims(ref s.m_params[0].angularSleepingThreshold, p, l, v); } ), + new ParameterDefn("CcdMotionThreshold", "Continuious collision detection threshold (0 means no CCD)" , + 0f, // set to zero to disable + (s,cf,p,v) => { s.m_params[0].ccdMotionThreshold = cf.GetFloat(p, v); }, + (s) => { return s.m_params[0].ccdMotionThreshold; }, + (s,p,l,v) => { s.UpdateParameterPrims(ref s.m_params[0].ccdMotionThreshold, p, l, v); } ), + new ParameterDefn("CcdSweptSphereRadius", "Continuious collision detection test radius" , + 0f, + (s,cf,p,v) => { s.m_params[0].ccdSweptSphereRadius = cf.GetFloat(p, v); }, + (s) => { return s.m_params[0].ccdSweptSphereRadius; }, + (s,p,l,v) => { s.UpdateParameterPrims(ref s.m_params[0].ccdSweptSphereRadius, p, l, v); } ), + new ParameterDefn("ContactProcessingThreshold", "Distance between contacts before doing collision check" , + 0.1f, + (s,cf,p,v) => { s.m_params[0].contactProcessingThreshold = cf.GetFloat(p, v); }, + (s) => { return s.m_params[0].contactProcessingThreshold; }, + (s,p,l,v) => { s.UpdateParameterPrims(ref s.m_params[0].contactProcessingThreshold, p, l, v); } ), + + new ParameterDefn("TerrainFriction", "Factor to reduce movement against terrain surface" , + 0.5f, + (s,cf,p,v) => { s.m_params[0].terrainFriction = cf.GetFloat(p, v); }, + (s) => { return s.m_params[0].terrainFriction; }, + (s,p,l,v) => { s.m_params[0].terrainFriction = v; s.TaintedUpdateParameter(p,l,v); } ), + new ParameterDefn("TerrainHitFraction", "Distance to measure hit collisions" , + 0.8f, + (s,cf,p,v) => { s.m_params[0].terrainHitFraction = cf.GetFloat(p, v); }, + (s) => { return s.m_params[0].terrainHitFraction; }, + (s,p,l,v) => { s.m_params[0].terrainHitFraction = v; s.TaintedUpdateParameter(p,l,v); } ), + new ParameterDefn("TerrainRestitution", "Bouncyness" , + 0f, + (s,cf,p,v) => { s.m_params[0].terrainRestitution = cf.GetFloat(p, v); }, + (s) => { return s.m_params[0].terrainRestitution; }, + (s,p,l,v) => { s.m_params[0].terrainRestitution = v; s.TaintedUpdateParameter(p,l,v); } ), + new ParameterDefn("AvatarFriction", "Factor to reduce movement against an avatar. Changed on avatar recreation.", + 0.5f, + (s,cf,p,v) => { s.m_params[0].avatarFriction = cf.GetFloat(p, v); }, + (s) => { return s.m_params[0].avatarFriction; }, + (s,p,l,v) => { s.UpdateParameterAvatars(ref s.m_params[0].avatarFriction, p, l, v); } ), + new ParameterDefn("AvatarDensity", "Density of an avatar. Changed on avatar recreation.", + 60f, + (s,cf,p,v) => { s.m_params[0].avatarDensity = cf.GetFloat(p, v); }, + (s) => { return s.m_params[0].avatarDensity; }, + (s,p,l,v) => { s.UpdateParameterAvatars(ref s.m_params[0].avatarDensity, p, l, v); } ), + new ParameterDefn("AvatarRestitution", "Bouncyness. Changed on avatar recreation.", + 0f, + (s,cf,p,v) => { s.m_params[0].avatarRestitution = cf.GetFloat(p, v); }, + (s) => { return s.m_params[0].avatarRestitution; }, + (s,p,l,v) => { s.UpdateParameterAvatars(ref s.m_params[0].avatarRestitution, p, l, v); } ), + new ParameterDefn("AvatarCapsuleRadius", "Radius of space around an avatar", + 0.37f, + (s,cf,p,v) => { s.m_params[0].avatarCapsuleRadius = cf.GetFloat(p, v); }, + (s) => { return s.m_params[0].avatarCapsuleRadius; }, + (s,p,l,v) => { s.UpdateParameterAvatars(ref s.m_params[0].avatarCapsuleRadius, p, l, v); } ), + new ParameterDefn("AvatarCapsuleHeight", "Default height of space around avatar", + 1.5f, + (s,cf,p,v) => { s.m_params[0].avatarCapsuleHeight = cf.GetFloat(p, v); }, + (s) => { return s.m_params[0].avatarCapsuleHeight; }, + (s,p,l,v) => { s.UpdateParameterAvatars(ref s.m_params[0].avatarCapsuleHeight, p, l, v); } ), + new ParameterDefn("AvatarContactProcessingThreshold", "Distance from capsule to check for collisions", + 0.1f, + (s,cf,p,v) => { s.m_params[0].avatarContactProcessingThreshold = cf.GetFloat(p, v); }, + (s) => { return s.m_params[0].avatarContactProcessingThreshold; }, + (s,p,l,v) => { s.UpdateParameterAvatars(ref s.m_params[0].avatarContactProcessingThreshold, p, l, v); } ), + + + new ParameterDefn("MaxPersistantManifoldPoolSize", "Number of manifolds pooled (0 means default)", + 0f, // zero to disable + (s,cf,p,v) => { s.m_params[0].maxPersistantManifoldPoolSize = cf.GetFloat(p, v); }, + (s) => { return s.m_params[0].maxPersistantManifoldPoolSize; }, + (s,p,l,v) => { s.m_params[0].maxPersistantManifoldPoolSize = v; } ), + new ParameterDefn("ShouldDisableContactPoolDynamicAllocation", "Enable to allow large changes in object count", + ConfigurationParameters.numericTrue, + (s,cf,p,v) => { s.m_params[0].maxPersistantManifoldPoolSize = s.NumericBool(cf.GetBoolean(p, s.BoolNumeric(v))); }, + (s) => { return s.m_params[0].shouldDisableContactPoolDynamicAllocation; }, + (s,p,l,v) => { s.m_params[0].shouldDisableContactPoolDynamicAllocation = v; } ), + new ParameterDefn("ShouldForceUpdateAllAabbs", "Enable to recomputer AABBs every simulator step", + ConfigurationParameters.numericFalse, + (s,cf,p,v) => { s.m_params[0].shouldForceUpdateAllAabbs = s.NumericBool(cf.GetBoolean(p, s.BoolNumeric(v))); }, + (s) => { return s.m_params[0].shouldForceUpdateAllAabbs; }, + (s,p,l,v) => { s.m_params[0].shouldForceUpdateAllAabbs = v; } ), + new ParameterDefn("ShouldRandomizeSolverOrder", "Enable for slightly better stacking interaction", + ConfigurationParameters.numericFalse, + (s,cf,p,v) => { s.m_params[0].shouldRandomizeSolverOrder = s.NumericBool(cf.GetBoolean(p, s.BoolNumeric(v))); }, + (s) => { return s.m_params[0].shouldRandomizeSolverOrder; }, + (s,p,l,v) => { s.m_params[0].shouldRandomizeSolverOrder = v; } ), + new ParameterDefn("ShouldSplitSimulationIslands", "Enable splitting active object scanning islands", + ConfigurationParameters.numericFalse, + (s,cf,p,v) => { s.m_params[0].shouldSplitSimulationIslands = s.NumericBool(cf.GetBoolean(p, s.BoolNumeric(v))); }, + (s) => { return s.m_params[0].shouldSplitSimulationIslands; }, + (s,p,l,v) => { s.m_params[0].shouldSplitSimulationIslands = v; } ), + new ParameterDefn("ShouldEnableFrictionCaching", "Enable friction computation caching", + ConfigurationParameters.numericFalse, + (s,cf,p,v) => { s.m_params[0].shouldEnableFrictionCaching = s.NumericBool(cf.GetBoolean(p, s.BoolNumeric(v))); }, + (s) => { return s.m_params[0].shouldEnableFrictionCaching; }, + (s,p,l,v) => { s.m_params[0].shouldEnableFrictionCaching = v; } ), + new ParameterDefn("NumberOfSolverIterations", "Number of internal iterations (0 means default)", + 0f, // zero says use Bullet default + (s,cf,p,v) => { s.m_params[0].numberOfSolverIterations = cf.GetFloat(p, v); }, + (s) => { return s.m_params[0].numberOfSolverIterations; }, + (s,p,l,v) => { s.m_params[0].numberOfSolverIterations = v; } ), + + new ParameterDefn("LinkConstraintUseFrameOffset", "For linksets built with constraints, enable frame offsetFor linksets built with constraints, enable frame offset.", + ConfigurationParameters.numericFalse, + (s,cf,p,v) => { s.m_params[0].linkConstraintUseFrameOffset = s.NumericBool(cf.GetBoolean(p, s.BoolNumeric(v))); }, + (s) => { return s.m_params[0].linkConstraintUseFrameOffset; }, + (s,p,l,v) => { s.m_params[0].linkConstraintUseFrameOffset = v; } ), + new ParameterDefn("LinkConstraintEnableTransMotor", "Whether to enable translational motor on linkset constraints", + ConfigurationParameters.numericTrue, + (s,cf,p,v) => { s.m_params[0].linkConstraintEnableTransMotor = s.NumericBool(cf.GetBoolean(p, s.BoolNumeric(v))); }, + (s) => { return s.m_params[0].linkConstraintEnableTransMotor; }, + (s,p,l,v) => { s.m_params[0].linkConstraintEnableTransMotor = v; } ), + new ParameterDefn("LinkConstraintTransMotorMaxVel", "Maximum velocity to be applied by translational motor in linkset constraints", + 5.0f, + (s,cf,p,v) => { s.m_params[0].linkConstraintTransMotorMaxVel = cf.GetFloat(p, v); }, + (s) => { return s.m_params[0].linkConstraintTransMotorMaxVel; }, + (s,p,l,v) => { s.m_params[0].linkConstraintTransMotorMaxVel = v; } ), + new ParameterDefn("LinkConstraintTransMotorMaxForce", "Maximum force to be applied by translational motor in linkset constraints", + 0.1f, + (s,cf,p,v) => { s.m_params[0].linkConstraintTransMotorMaxForce = cf.GetFloat(p, v); }, + (s) => { return s.m_params[0].linkConstraintTransMotorMaxForce; }, + (s,p,l,v) => { s.m_params[0].linkConstraintTransMotorMaxForce = v; } ), + new ParameterDefn("LinkConstraintCFM", "Amount constraint can be violated. 0=none, 1=all. Default=0", + 0.0f, + (s,cf,p,v) => { s.m_params[0].linkConstraintCFM = cf.GetFloat(p, v); }, + (s) => { return s.m_params[0].linkConstraintCFM; }, + (s,p,l,v) => { s.m_params[0].linkConstraintCFM = v; } ), + new ParameterDefn("LinkConstraintERP", "Amount constraint is corrected each tick. 0=none, 1=all. Default = 0.2", + 0.2f, + (s,cf,p,v) => { s.m_params[0].linkConstraintERP = cf.GetFloat(p, v); }, + (s) => { return s.m_params[0].linkConstraintERP; }, + (s,p,l,v) => { s.m_params[0].linkConstraintERP = v; } ), + + new ParameterDefn("DetailedStats", "Frames between outputting detailed phys stats. (0 is off)", + 0f, + (s,cf,p,v) => { s.m_detailedStatsStep = cf.GetInt(p, (int)v); }, + (s) => { return (float)s.m_detailedStatsStep; }, + (s,p,l,v) => { s.m_detailedStatsStep = (int)v; } ), + new ParameterDefn("ShouldDebugLog", "Enables detailed DEBUG log statements", + ConfigurationParameters.numericFalse, + (s,cf,p,v) => { s.shouldDebugLog = cf.GetBoolean(p, s.BoolNumeric(v)); }, + (s) => { return s.NumericBool(s.shouldDebugLog); }, + (s,p,l,v) => { s.shouldDebugLog = s.BoolNumeric(v); } ), }; + // Convert a boolean to our numeric true and false values + public float NumericBool(bool b) + { + return (b ? ConfigurationParameters.numericTrue : ConfigurationParameters.numericFalse); + } + + // Convert numeric true and false values to a boolean + public bool BoolNumeric(float b) + { + return (b == ConfigurationParameters.numericTrue ? true : false); + } + + // Search through the parameter definitions and return the matching + // ParameterDefn structure. + // Case does not matter as names are compared after converting to lower case. + // Returns 'false' if the parameter is not found. + private bool TryGetParameter(string paramName, out ParameterDefn defn) + { + bool ret = false; + ParameterDefn foundDefn = new ParameterDefn(); + string pName = paramName.ToLower(); + + foreach (ParameterDefn parm in ParameterDefinitions) + { + if (pName == parm.name.ToLower()) + { + foundDefn = parm; + ret = true; + break; + } + } + defn = foundDefn; + return ret; + } + + // Pass through the settable parameters and set the default values + private void SetParameterDefaultValues() + { + foreach (ParameterDefn parm in ParameterDefinitions) + { + parm.setter(this, parm.name, PhysParameterEntry.APPLY_TO_NONE, parm.defaultValue); + } + } + + // Get user set values out of the ini file. + private void SetParameterConfigurationValues(IConfig cfg) + { + foreach (ParameterDefn parm in ParameterDefinitions) + { + parm.userParam(this, cfg, parm.name, parm.defaultValue); + } + } + + private PhysParameterEntry[] SettableParameters = new PhysParameterEntry[1]; + + private void BuildParameterTable() + { + if (SettableParameters.Length < ParameterDefinitions.Length) + { + + List entries = new List(); + for (int ii = 0; ii < ParameterDefinitions.Length; ii++) + { + ParameterDefn pd = ParameterDefinitions[ii]; + entries.Add(new PhysParameterEntry(pd.name, pd.desc)); + } + + // make the list in alphabetical order for estetic reasons + entries.Sort(delegate(PhysParameterEntry ppe1, PhysParameterEntry ppe2) + { + return ppe1.name.CompareTo(ppe2.name); + }); + + SettableParameters = entries.ToArray(); + } + } + + #region IPhysicsParameters // Get the list of parameters this physics engine supports public PhysParameterEntry[] GetParameterList() { + BuildParameterTable(); return SettableParameters; } @@ -823,63 +1188,18 @@ public class BSScene : PhysicsScene, IPhysicsParameters // value activated ('terrainFriction' for instance). public bool SetPhysicsParameter(string parm, float val, uint localID) { - bool ret = true; - string lparm = parm.ToLower(); - switch (lparm) + bool ret = false; + ParameterDefn theParam; + if (TryGetParameter(parm, out theParam)) { - case "detailedstats": m_detailedStatsStep = (int)val; break; - - case "meshlod": m_meshLOD = (int)val; break; - case "sculptlod": m_sculptLOD = (int)val; break; - case "maxsubstep": m_maxSubSteps = (int)val; break; - case "fixedtimestep": m_fixedTimeStep = val; break; - case "maxobjectmass": m_maximumObjectMass = val; break; - - case "defaultfriction": m_params[0].defaultFriction = val; break; - case "defaultdensity": m_params[0].defaultDensity = val; break; - case "defaultrestitution": m_params[0].defaultRestitution = val; break; - case "collisionmargin": m_params[0].collisionMargin = val; break; - case "gravity": m_params[0].gravity = val; TaintedUpdateParameter(lparm, localID, val); break; - - case "lineardamping": UpdateParameterPrims(ref m_params[0].linearDamping, lparm, localID, val); break; - case "angulardamping": UpdateParameterPrims(ref m_params[0].angularDamping, lparm, localID, val); break; - case "deactivationtime": UpdateParameterPrims(ref m_params[0].deactivationTime, lparm, localID, val); break; - case "linearsleepingthreshold": UpdateParameterPrims(ref m_params[0].linearSleepingThreshold, lparm, localID, val); break; - case "angularsleepingthreshold": UpdateParameterPrims(ref m_params[0].angularDamping, lparm, localID, val); break; - case "ccdmotionthreshold": UpdateParameterPrims(ref m_params[0].ccdMotionThreshold, lparm, localID, val); break; - case "ccdsweptsphereradius": UpdateParameterPrims(ref m_params[0].ccdSweptSphereRadius, lparm, localID, val); break; - case "contactprocessingthreshold": UpdateParameterPrims(ref m_params[0].contactProcessingThreshold, lparm, localID, val); break; - // the following are used only at initialization time so setting them makes no sense - // case "maxPersistantmanifoldpoolSize": m_params[0].maxPersistantManifoldPoolSize = val; break; - // case "shoulddisablecontactpooldynamicallocation": m_params[0].shouldDisableContactPoolDynamicAllocation = val; break; - // case "shouldforceupdateallaabbs": m_params[0].shouldForceUpdateAllAabbs = val; break; - // case "shouldrandomizesolverorder": m_params[0].shouldRandomizeSolverOrder = val; break; - // case "shouldsplitsimulationislands": m_params[0].shouldSplitSimulationIslands = val; break; - // case "shouldenablefrictioncaching": m_params[0].shouldEnableFrictionCaching = val; break; - // case "numberofsolveriterations": m_params[0].numberOfSolverIterations = val; break; - - case "friction": TaintedUpdateParameter(lparm, localID, val); break; - case "restitution": TaintedUpdateParameter(lparm, localID, val); break; - - // set a terrain physical feature and cause terrain to be recalculated - case "terrainfriction": m_params[0].terrainFriction = val; TaintedUpdateParameter("terrain", 0, val); break; - case "terrainhitfraction": m_params[0].terrainHitFraction = val; TaintedUpdateParameter("terrain", 0, val); break; - case "terrainrestitution": m_params[0].terrainRestitution = val; TaintedUpdateParameter("terrain", 0, val); break; - // set an avatar physical feature and cause avatar(s) to be recalculated - case "avatarfriction": UpdateParameterAvatars(ref m_params[0].avatarFriction, "avatar", localID, val); break; - case "avatardensity": UpdateParameterAvatars(ref m_params[0].avatarDensity, "avatar", localID, val); break; - case "avatarrestitution": UpdateParameterAvatars(ref m_params[0].avatarRestitution, "avatar", localID, val); break; - case "avatarcapsuleradius": UpdateParameterAvatars(ref m_params[0].avatarCapsuleRadius, "avatar", localID, val); break; - case "avatarcapsuleheight": UpdateParameterAvatars(ref m_params[0].avatarCapsuleHeight, "avatar", localID, val); break; - case "avatarcontactprocessingthreshold": UpdateParameterAvatars(ref m_params[0].avatarContactProcessingThreshold, "avatar", localID, val); break; - - default: ret = false; break; + theParam.setter(this, parm, localID, val); + ret = true; } return ret; } // check to see if we are updating a parameter for a particular or all of the prims - private void UpdateParameterPrims(ref float loc, string parm, uint localID, float val) + protected void UpdateParameterPrims(ref float loc, string parm, uint localID, float val) { List operateOn; lock (m_prims) operateOn = new List(m_prims.Keys); @@ -887,7 +1207,7 @@ public class BSScene : PhysicsScene, IPhysicsParameters } // check to see if we are updating a parameter for a particular or all of the avatars - private void UpdateParameterAvatars(ref float loc, string parm, uint localID, float val) + protected void UpdateParameterAvatars(ref float loc, string parm, uint localID, float val) { List operateOn; lock (m_avatars) operateOn = new List(m_avatars.Keys); @@ -898,7 +1218,7 @@ public class BSScene : PhysicsScene, IPhysicsParameters // If the local ID is APPLY_TO_NONE, just change the default value // If the localID is APPLY_TO_ALL change the default value and apply the new value to all the lIDs // If the localID is a specific object, apply the parameter change to only that object - private void UpdateParameterSet(List lIDs, ref float defaultLoc, string parm, uint localID, float val) + protected void UpdateParameterSet(List lIDs, ref float defaultLoc, string parm, uint localID, float val) { switch (localID) { @@ -925,7 +1245,7 @@ public class BSScene : PhysicsScene, IPhysicsParameters } // schedule the actual updating of the paramter to when the phys engine is not busy - private void TaintedUpdateParameter(string parm, uint localID, float val) + protected void TaintedUpdateParameter(string parm, uint localID, float val) { uint xlocalID = localID; string xparm = parm.ToLower(); @@ -940,50 +1260,12 @@ public class BSScene : PhysicsScene, IPhysicsParameters public bool GetPhysicsParameter(string parm, out float value) { float val = 0f; - bool ret = true; - switch (parm.ToLower()) + bool ret = false; + ParameterDefn theParam; + if (TryGetParameter(parm, out theParam)) { - case "detailedstats": val = (int)m_detailedStatsStep; break; - case "meshlod": val = (float)m_meshLOD; break; - case "sculptlod": val = (float)m_sculptLOD; break; - case "maxsubstep": val = (float)m_maxSubSteps; break; - case "fixedtimestep": val = m_fixedTimeStep; break; - case "maxobjectmass": val = m_maximumObjectMass; break; - - case "defaultfriction": val = m_params[0].defaultFriction; break; - case "defaultdensity": val = m_params[0].defaultDensity; break; - case "defaultrestitution": val = m_params[0].defaultRestitution; break; - case "collisionmargin": val = m_params[0].collisionMargin; break; - case "gravity": val = m_params[0].gravity; break; - - case "lineardamping": val = m_params[0].linearDamping; break; - case "angulardamping": val = m_params[0].angularDamping; break; - case "deactivationtime": val = m_params[0].deactivationTime; break; - case "linearsleepingthreshold": val = m_params[0].linearSleepingThreshold; break; - case "angularsleepingthreshold": val = m_params[0].angularDamping; break; - case "ccdmotionthreshold": val = m_params[0].ccdMotionThreshold; break; - case "ccdsweptsphereradius": val = m_params[0].ccdSweptSphereRadius; break; - case "contactprocessingthreshold": val = m_params[0].contactProcessingThreshold; break; - case "maxPersistantmanifoldpoolSize": val = m_params[0].maxPersistantManifoldPoolSize; break; - case "shoulddisablecontactpooldynamicallocation": val = m_params[0].shouldDisableContactPoolDynamicAllocation; break; - case "shouldforceupdateallaabbs": val = m_params[0].shouldForceUpdateAllAabbs; break; - case "shouldrandomizesolverorder": val = m_params[0].shouldRandomizeSolverOrder; break; - case "shouldsplitsimulationislands": val = m_params[0].shouldSplitSimulationIslands; break; - case "shouldenablefrictioncaching": val = m_params[0].shouldEnableFrictionCaching; break; - case "numberofsolveriterations": val = m_params[0].numberOfSolverIterations; break; - - case "terrainfriction": val = m_params[0].terrainFriction; break; - case "terrainhitfraction": val = m_params[0].terrainHitFraction; break; - case "terrainrestitution": val = m_params[0].terrainRestitution; break; - - case "avatarfriction": val = m_params[0].avatarFriction; break; - case "avatardensity": val = m_params[0].avatarDensity; break; - case "avatarrestitution": val = m_params[0].avatarRestitution; break; - case "avatarcapsuleradius": val = m_params[0].avatarCapsuleRadius; break; - case "avatarcapsuleheight": val = m_params[0].avatarCapsuleHeight; break; - case "avatarcontactprocessingthreshold": val = m_params[0].avatarContactProcessingThreshold; break; - default: ret = false; break; - + val = theParam.getter(this); + ret = true; } value = val; return ret; diff --git a/OpenSim/Region/Physics/BulletSPlugin/BulletSimAPI.cs b/OpenSim/Region/Physics/BulletSPlugin/BulletSimAPI.cs index 086f0dc9d7..0ffbc94040 100644 --- a/OpenSim/Region/Physics/BulletSPlugin/BulletSimAPI.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BulletSimAPI.cs @@ -32,6 +32,28 @@ using OpenMetaverse; namespace OpenSim.Region.Physics.BulletSPlugin { +// Classes to allow some type checking for the API +public struct BulletSim +{ + public BulletSim(uint id, IntPtr xx) { ID = id; Ptr = xx; } + public IntPtr Ptr; + public uint ID; +} + +public struct BulletBody +{ + public BulletBody(uint id, IntPtr xx) { ID = id; Ptr = xx; } + public IntPtr Ptr; + public uint ID; +} + +public struct BulletConstraint +{ + public BulletConstraint(IntPtr xx) { Ptr = xx; } + public IntPtr Ptr; +} + +// =============================================================================== [StructLayout(LayoutKind.Sequential)] public struct ConvexHull { @@ -44,13 +66,14 @@ public struct ShapeData { public enum PhysicsShapeType { - SHAPE_AVATAR = 0, - SHAPE_BOX = 1, - SHAPE_CONE = 2, - SHAPE_CYLINDER = 3, - SHAPE_SPHERE = 4, - SHAPE_MESH = 5, - SHAPE_HULL = 6 + SHAPE_UNKNOWN = 0, + SHAPE_AVATAR = 1, + SHAPE_BOX = 2, + SHAPE_CONE = 3, + SHAPE_CYLINDER = 4, + SHAPE_SPHERE = 5, + SHAPE_MESH = 6, + SHAPE_HULL = 7 }; public uint ID; public PhysicsShapeType Type; @@ -64,12 +87,12 @@ public struct ShapeData public System.UInt64 MeshKey; public float Friction; public float Restitution; - public int Collidable; - public int Static; // true if a static object. Otherwise gravity, etc. + public float Collidable; // true of things bump into this + public float Static; // true if a static object. Otherwise gravity, etc. - // note that bools are passed as ints since bool size changes by language and architecture - public const int numericTrue = 1; - public const int numericFalse = 0; + // note that bools are passed as floats since bool size changes by language and architecture + public const float numericTrue = 1f; + public const float numericFalse = 0f; } [StructLayout(LayoutKind.Sequential)] public struct SweepHit @@ -142,10 +165,56 @@ public struct ConfigurationParameters public float shouldEnableFrictionCaching; public float numberOfSolverIterations; + public float linkConstraintUseFrameOffset; + public float linkConstraintEnableTransMotor; + public float linkConstraintTransMotorMaxVel; + public float linkConstraintTransMotorMaxForce; + public float linkConstraintERP; + public float linkConstraintCFM; + public const float numericTrue = 1f; public const float numericFalse = 0f; } +// Values used by Bullet and BulletSim to control collisions +public enum CollisionFlags : uint +{ + STATIC_OBJECT = 1 << 0, + KINEMATIC_OBJECT = 1 << 1, + NO_CONTACT_RESPONSE = 1 << 2, + CUSTOM_MATERIAL_CALLBACK = 1 << 3, + CHARACTER_OBJECT = 1 << 4, + DISABLE_VISUALIZE_OBJECT = 1 << 5, + DISABLE_SPU_COLLISION_PROCESS = 1 << 6, + // Following used by BulletSim to control collisions + VOLUME_DETECT_OBJECT = 1 << 10, + PHANTOM_OBJECT = 1 << 11, + PHYSICAL_OBJECT = 1 << 12, +}; + +// CFM controls the 'hardness' of the constraint. 0=fixed, 0..1=violatable. Default=0 +// ERP controls amount of correction per tick. Usable range=0.1..0.8. Default=0.2. +public enum ConstraintParams : int +{ + BT_CONSTRAINT_ERP = 1, // this one is not used in Bullet as of 20120730 + BT_CONSTRAINT_STOP_ERP, + BT_CONSTRAINT_CFM, + BT_CONSTRAINT_STOP_CFM, +}; +public enum ConstraintParamAxis : int +{ + AXIS_LINEAR_X = 0, + AXIS_LINEAR_Y, + AXIS_LINEAR_Z, + AXIS_ANGULAR_X, + AXIS_ANGULAR_Y, + AXIS_ANGULAR_Z, + AXIS_LINEAR_ALL = 20, // these last three added by BulletSim so we don't have to do zillions of calls + AXIS_ANGULAR_ALL, + AXIS_ALL +}; + +// =============================================================================== static class BulletSimAPI { [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] @@ -195,6 +264,7 @@ public static extern bool DestroyMesh(uint worldID, System.UInt64 meshKey); [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] public static extern bool CreateObject(uint worldID, ShapeData shapeData); +/* Remove old functionality [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] public static extern void CreateLinkset(uint worldID, int objectCount, ShapeData[] shapeDatas); @@ -209,10 +279,14 @@ public static extern bool RemoveConstraintByID(uint worldID, uint id1); [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] public static extern bool RemoveConstraint(uint worldID, uint id1, uint id2); + */ [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] public static extern Vector3 GetObjectPosition(uint WorldID, uint id); +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern Quaternion GetObjectOrientation(uint WorldID, uint id); + [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] public static extern bool SetObjectTranslation(uint worldID, uint id, Vector3 position, Quaternion rotation); @@ -268,5 +342,179 @@ public static extern void DumpBulletStatistics(); public delegate void DebugLogCallback([MarshalAs(UnmanagedType.LPStr)]string msg); [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] public static extern void SetDebugLogCallback(DebugLogCallback callback); + +// =============================================================================== +// =============================================================================== +// =============================================================================== +// A new version of the API that enables moving all the logic out of the C++ code and into +// the C# code. This will make modifications easier for the next person. +// This interface passes the actual pointers to the objects in the unmanaged +// address space. All the management (calls for creation/destruction/lookup) +// is done in the C# code. +// The names have a "2" tacked on. This will be removed as the C# code gets rebuilt +// and the old code is removed. + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern IntPtr GetSimHandle2(uint worldID); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern IntPtr GetBodyHandleWorldID2(uint worldID, uint id); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern IntPtr GetBodyHandle2(IntPtr sim, uint id); + +// =============================================================================== +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern IntPtr Initialize2(Vector3 maxPosition, IntPtr parms, + int maxCollisions, IntPtr collisionArray, + int maxUpdates, IntPtr updateArray); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern bool UpdateParameter2(IntPtr sim, uint localID, String parm, float value); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern void SetHeightmap2(IntPtr sim, float[] heightmap); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern void Shutdown2(IntPtr sim); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern int PhysicsStep2(IntPtr sim, float timeStep, int maxSubSteps, float fixedTimeStep, + out int updatedEntityCount, + out IntPtr updatedEntitiesPtr, + out int collidersCount, + out IntPtr collidersPtr); + +/* +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern IntPtr CreateMesh2(IntPtr sim, int indicesCount, int* indices, int verticesCount, float* vertices ); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern bool BuildHull2(IntPtr sim, IntPtr mesh); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern bool ReleaseHull2(IntPtr sim, IntPtr mesh); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern bool DestroyMesh2(IntPtr sim, IntPtr mesh); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern IntPtr CreateObject2(IntPtr sim, ShapeData shapeData); +*/ + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern IntPtr CreateConstraint2(IntPtr sim, IntPtr obj1, IntPtr obj2, + Vector3 frame1loc, Quaternion frame1rot, + Vector3 frame2loc, Quaternion frame2rot, + bool useLinearReferenceFrameA, bool disableCollisionsBetweenLinkedBodies); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern bool SetLinearLimits2(IntPtr constrain, Vector3 low, Vector3 hi); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern bool SetAngularLimits2(IntPtr constrain, Vector3 low, Vector3 hi); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern bool UseFrameOffset2(IntPtr constrain, float enable); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern bool TranslationalLimitMotor2(IntPtr constrain, float enable, float targetVel, float maxMotorForce); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern bool CalculateTransforms2(IntPtr constrain); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern bool SetConstraintParam2(IntPtr constrain, ConstraintParams paramIndex, float value, ConstraintParamAxis axis); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern bool DestroyConstraint2(IntPtr sim, IntPtr constrain); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern Vector3 GetPosition2(IntPtr obj); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern Quaternion GetOrientation2(IntPtr obj); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern bool SetTranslation2(IntPtr obj, Vector3 position, Quaternion rotation); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern bool SetVelocity2(IntPtr obj, Vector3 velocity); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern bool SetAngularVelocity2(IntPtr obj, Vector3 angularVelocity); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern bool SetObjectForce2(IntPtr obj, Vector3 force); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern bool SetCcdMotionThreshold2(IntPtr obj, float val); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern bool SetCcdSweepSphereRadius2(IntPtr obj, float val); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern bool SetDamping2(IntPtr obj, float lin_damping, float ang_damping); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern bool SetDeactivationTime2(IntPtr obj, float val); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern bool SetSleepingThresholds2(IntPtr obj, float lin_threshold, float ang_threshold); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern bool SetContactProcessingThreshold2(IntPtr obj, float val); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern bool SetFriction2(IntPtr obj, float val); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern bool SetRestitution2(IntPtr obj, float val); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern bool SetLinearVelocity2(IntPtr obj, Vector3 val); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern bool SetInterpolation2(IntPtr obj, Vector3 lin, Vector3 ang); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern IntPtr SetCollisionFlags2(IntPtr obj, uint flags); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern IntPtr AddToCollisionFlags2(IntPtr obj, uint flags); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern IntPtr RemoveFromCollisionFlags2(IntPtr obj, uint flags); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern bool SetMassProps2(IntPtr obj, float mass, Vector3 inertia); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern bool UpdateInertiaTensor2(IntPtr obj); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern bool SetGravity2(IntPtr obj, Vector3 val); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern IntPtr ClearForces2(IntPtr obj); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern bool SetMargin2(IntPtr obj, float val); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern bool UpdateSingleAabb2(IntPtr world, IntPtr obj); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern bool AddObjectToWorld2(IntPtr world, IntPtr obj); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern bool RemoveObjectFromWorld2(IntPtr world, IntPtr obj); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern bool DestroyObject2(IntPtr world, uint id); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern void DumpPhysicsStatistics2(IntPtr sim); + } } diff --git a/OpenSim/Region/Physics/Manager/PhysicsScene.cs b/OpenSim/Region/Physics/Manager/PhysicsScene.cs index cfede557fc..5274f3b471 100644 --- a/OpenSim/Region/Physics/Manager/PhysicsScene.cs +++ b/OpenSim/Region/Physics/Manager/PhysicsScene.cs @@ -71,6 +71,9 @@ namespace OpenSim.Region.Physics.Manager All = 0x3f } + public delegate void RequestAssetDelegate(UUID assetID, AssetReceivedDelegate callback); + public delegate void AssetReceivedDelegate(AssetBase asset); + /// /// Contact result from a raycast. /// @@ -103,6 +106,8 @@ namespace OpenSim.Region.Physics.Manager get { return new NullPhysicsScene(); } } + public RequestAssetDelegate RequestAssetMethod { private get; set; } + public virtual void TriggerPhysicsBasedRestart() { physicsCrash handler = OnPhysicsCrash; diff --git a/OpenSim/Region/Physics/OdePlugin/OdeScene.cs b/OpenSim/Region/Physics/OdePlugin/OdeScene.cs index 32e81e2ea1..929b019ecf 100644 --- a/OpenSim/Region/Physics/OdePlugin/OdeScene.cs +++ b/OpenSim/Region/Physics/OdePlugin/OdeScene.cs @@ -290,7 +290,6 @@ namespace OpenSim.Region.Physics.OdePlugin private readonly IntPtr contactgroup; - internal IntPtr LandGeom; internal IntPtr WaterGeom; private float nmTerrainContactFriction = 255.0f; @@ -489,6 +488,8 @@ namespace OpenSim.Region.Physics.OdePlugin /// internal Object OdeLock = new Object(); + private bool _worldInitialized = false; + public IMesher mesher; private IConfigSource m_config; @@ -875,6 +876,8 @@ namespace OpenSim.Region.Physics.OdePlugin staticPrimspace[i, j] = IntPtr.Zero; } } + + _worldInitialized = true; } // internal void waitForSpaceUnlock(IntPtr space) @@ -1508,8 +1511,7 @@ namespace OpenSim.Region.Physics.OdePlugin { if (((Math.Abs(contactGeom.normal.X - contact.normal.X) < 1.026f) && (Math.Abs(contactGeom.normal.Y - contact.normal.Y) < 0.303f) - && (Math.Abs(contactGeom.normal.Z - contact.normal.Z) < 0.065f)) - && contactGeom.g1 != LandGeom && contactGeom.g2 != LandGeom) + && (Math.Abs(contactGeom.normal.Z - contact.normal.Z) < 0.065f))) { if (Math.Abs(contact.depth - contactGeom.depth) < 0.052f) { @@ -1538,7 +1540,7 @@ namespace OpenSim.Region.Physics.OdePlugin //d.GeomGetAABB(contactGeom.g2, out aabb2); //d.GeomGetAABB(contactGeom.g1, out aabb1); //aabb1. - if (((Math.Abs(contactGeom.normal.X - contact.normal.X) < 1.026f) && (Math.Abs(contactGeom.normal.Y - contact.normal.Y) < 0.303f) && (Math.Abs(contactGeom.normal.Z - contact.normal.Z) < 0.065f)) && contactGeom.g1 != LandGeom && contactGeom.g2 != LandGeom) + if (((Math.Abs(contactGeom.normal.X - contact.normal.X) < 1.026f) && (Math.Abs(contactGeom.normal.Y - contact.normal.Y) < 0.303f) && (Math.Abs(contactGeom.normal.Z - contact.normal.Z) < 0.065f))) { if (contactGeom.normal.X == contact.normal.X && contactGeom.normal.Y == contact.normal.Y && contactGeom.normal.Z == contact.normal.Z) { @@ -2896,6 +2898,8 @@ namespace OpenSim.Region.Physics.OdePlugin /// The number of frames simulated over that period. public override float Simulate(float timeStep) { + if (!_worldInitialized) return 11f; + int startFrameTick = CollectStats ? Util.EnvironmentTickCount() : 0; int tempTick = 0, tempTick2 = 0; @@ -4017,6 +4021,8 @@ namespace OpenSim.Region.Physics.OdePlugin public override void Dispose() { + _worldInitialized = false; + m_rayCastManager.Dispose(); m_rayCastManager = null; @@ -4037,6 +4043,7 @@ namespace OpenSim.Region.Physics.OdePlugin d.WorldDestroy(world); //d.CloseODE(); } + } public override Dictionary GetTopColliders() diff --git a/OpenSim/Region/RegionCombinerModule/RegionCombinerModule.cs b/OpenSim/Region/RegionCombinerModule/RegionCombinerModule.cs index 204c4ff5fb..3144d76432 100644 --- a/OpenSim/Region/RegionCombinerModule/RegionCombinerModule.cs +++ b/OpenSim/Region/RegionCombinerModule/RegionCombinerModule.cs @@ -99,6 +99,8 @@ namespace OpenSim.Region.RegionCombinerModule public void RemoveRegion(Scene scene) { + lock (m_startingScenes) + m_startingScenes.Remove(scene.RegionInfo.originRegionID); } public void RegionLoaded(Scene scene) diff --git a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/AsyncCommandManager.cs b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/AsyncCommandManager.cs index 693992afe4..94fd940dc1 100644 --- a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/AsyncCommandManager.cs +++ b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/AsyncCommandManager.cs @@ -31,6 +31,7 @@ using System.Collections.Generic; using System.Threading; using OpenMetaverse; using OpenSim.Framework; +using OpenSim.Framework.Monitoring; using OpenSim.Region.Framework.Interfaces; using OpenSim.Region.ScriptEngine.Interfaces; using OpenSim.Region.ScriptEngine.Shared; diff --git a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs index cd72dc2d4b..ce1c36470c 100644 --- a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs +++ b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs @@ -6885,22 +6885,22 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api if (folderID == UUID.Zero) return; - byte[] bucket = new byte[1]; - bucket[0] = (byte)AssetType.Folder; - //byte[] objBytes = folderID.GetBytes(); - //Array.Copy(objBytes, 0, bucket, 1, 16); - - GridInstantMessage msg = new GridInstantMessage(World, - m_host.OwnerID, m_host.Name, destID, - (byte)InstantMessageDialog.TaskInventoryOffered, - false, category+". "+m_host.Name+" is located at "+ - World.RegionInfo.RegionName+" "+ - m_host.AbsolutePosition.ToString(), - folderID, true, m_host.AbsolutePosition, - bucket); - if (m_TransferModule != null) + { + byte[] bucket = new byte[] { (byte)AssetType.Folder }; + + GridInstantMessage msg = new GridInstantMessage(World, + m_host.UUID, m_host.Name + ", an object owned by " + + resolveName(m_host.OwnerID) + ",", destID, + (byte)InstantMessageDialog.TaskInventoryOffered, + false, category + "\n" + m_host.Name + " is located at " + + World.RegionInfo.RegionName + " " + + m_host.AbsolutePosition.ToString(), + folderID, true, m_host.AbsolutePosition, + bucket); + m_TransferModule.SendInstantMessage(msg, delegate(bool success) {}); + } } public void llSetVehicleType(int type) @@ -11847,7 +11847,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api break; case ScriptBaseClass.OBJECT_ROT: { - Quaternion rot = Quaternion.Identity; + Quaternion rot = Quaternion.Identity; if (obj.ParentGroup.RootPart == obj) rot = obj.ParentGroup.GroupRotation; diff --git a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/MOD_Api.cs b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/MOD_Api.cs index 4bd3dffd30..7844c75155 100644 --- a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/MOD_Api.cs +++ b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/MOD_Api.cs @@ -200,24 +200,34 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api for (int i = 0; i < result.Length; i++) { if (result[i] is string) + { llist[i] = new LSL_String((string)result[i]); + } else if (result[i] is int) + { llist[i] = new LSL_Integer((int)result[i]); + } else if (result[i] is float) + { llist[i] = new LSL_Float((float)result[i]); + } + else if (result[i] is UUID) + { + llist[i] = new LSL_Key(result[i].ToString()); + } else if (result[i] is OpenMetaverse.Vector3) { OpenMetaverse.Vector3 vresult = (OpenMetaverse.Vector3)result[i]; - llist[i] = new LSL_Vector(vresult.X,vresult.Y,vresult.Z); + llist[i] = new LSL_Vector(vresult.X, vresult.Y, vresult.Z); } else if (result[i] is OpenMetaverse.Quaternion) { OpenMetaverse.Quaternion qresult = (OpenMetaverse.Quaternion)result[i]; - llist[i] = new LSL_Rotation(qresult.X,qresult.Y,qresult.Z,qresult.W); + llist[i] = new LSL_Rotation(qresult.X, qresult.Y, qresult.Z, qresult.W); } else { - MODError(String.Format("unknown list element returned by {0}",fname)); + MODError(String.Format("unknown list element {1} returned by {0}", fname, result[i].GetType().Name)); } } diff --git a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/OSSL_Api.cs b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/OSSL_Api.cs index 29d0342d60..1181c105a1 100644 --- a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/OSSL_Api.cs +++ b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/OSSL_Api.cs @@ -3290,8 +3290,6 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api ((LSL_Api)m_LSL_Api).llSay(0, string.Format("Unable to attach, item '{0}' is not an object.", itemName)); throw new Exception(String.Format("The inventory item '{0}' is not an object", itemName)); - - return; } ScenePresence sp = World.GetScenePresence(avatarId); @@ -3322,5 +3320,47 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api InitLSL(); ((LSL_Api)m_LSL_Api).DetachFromAvatar(); } + + /// + /// Checks if thing is a UUID. + /// + /// + /// 1 if thing is a valid UUID, 0 otherwise + public LSL_Integer osIsUUID(string thing) + { + CheckThreatLevel(ThreatLevel.None, "osIsUUID"); + m_host.AddScriptLPS(1); + + UUID test; + return UUID.TryParse(thing, out test) ? 1 : 0; + } + + /// + /// Wraps to Math.Min() + /// + /// + /// + /// + public LSL_Float osMin(double a, double b) + { + CheckThreatLevel(ThreatLevel.None, "osMin"); + m_host.AddScriptLPS(1); + + return Math.Min(a, b); + } + + /// + /// Wraps to Math.max() + /// + /// + /// + /// + public LSL_Float osMax(double a, double b) + { + CheckThreatLevel(ThreatLevel.None, "osMax"); + m_host.AddScriptLPS(1); + + return Math.Max(a, b); + } } } diff --git a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/Plugins/SensorRepeat.cs b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/Plugins/SensorRepeat.cs index 19f3ce191c..678f9d54fe 100644 --- a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/Plugins/SensorRepeat.cs +++ b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/Plugins/SensorRepeat.cs @@ -51,8 +51,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api.Plugins { get { - lock (SenseRepeatListLock) - return SenseRepeaters.Count; + return SenseRepeaters.Count; } } @@ -61,8 +60,11 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api.Plugins m_CmdManager = CmdManager; maximumRange = CmdManager.m_ScriptEngine.Config.GetDouble("SensorMaxRange", 96.0d); maximumToReturn = CmdManager.m_ScriptEngine.Config.GetInt("SensorMaxResults", 16); + m_npcModule = m_CmdManager.m_ScriptEngine.World.RequestModuleInterface(); } + private INPCModule m_npcModule; + private Object SenseLock = new Object(); private const int AGENT = 1; @@ -115,6 +117,16 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api.Plugins public double distance; } + /// + /// Sensors to process. + /// + /// + /// Do not add or remove sensors from this list directly. Instead, copy the list and substitute the updated + /// copy. This is to avoid locking the list for the duration of the sensor sweep, which increases the danger + /// of deadlocks with future code updates. + /// + /// Always lock SenseRepeatListLock when updating this list. + /// private List SenseRepeaters = new List(); private object SenseRepeatListLock = new object(); @@ -124,6 +136,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api.Plugins { // Always remove first, in case this is a re-set UnSetSenseRepeaterEvents(m_localID, m_itemID); + if (sec == 0) // Disabling timer return; @@ -143,9 +156,17 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api.Plugins ts.host = host; ts.next = DateTime.Now.ToUniversalTime().AddSeconds(ts.interval); + + AddSenseRepeater(ts); + } + + private void AddSenseRepeater(SenseRepeatClass senseRepeater) + { lock (SenseRepeatListLock) { - SenseRepeaters.Add(ts); + List newSenseRepeaters = new List(SenseRepeaters); + newSenseRepeaters.Add(senseRepeater); + SenseRepeaters = newSenseRepeaters; } } @@ -154,39 +175,32 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api.Plugins // Remove from timer lock (SenseRepeatListLock) { - List NewSensors = new List(); + List newSenseRepeaters = new List(); foreach (SenseRepeatClass ts in SenseRepeaters) { if (ts.localID != m_localID || ts.itemID != m_itemID) { - NewSensors.Add(ts); + newSenseRepeaters.Add(ts); } } - SenseRepeaters.Clear(); - SenseRepeaters = NewSensors; + + SenseRepeaters = newSenseRepeaters; } } public void CheckSenseRepeaterEvents() { - lock (SenseRepeatListLock) + // Go through all timers + foreach (SenseRepeatClass ts in SenseRepeaters) { - // Nothing to do here? - if (SenseRepeaters.Count == 0) - return; - - // Go through all timers - foreach (SenseRepeatClass ts in SenseRepeaters) + // Time has passed? + if (ts.next.ToUniversalTime() < DateTime.Now.ToUniversalTime()) { - // Time has passed? - if (ts.next.ToUniversalTime() < DateTime.Now.ToUniversalTime()) - { - SensorSweep(ts); - // set next interval - ts.next = DateTime.Now.ToUniversalTime().AddSeconds(ts.interval); - } + SensorSweep(ts); + // set next interval + ts.next = DateTime.Now.ToUniversalTime().AddSeconds(ts.interval); } - } // lock + } } public void SenseOnce(uint m_localID, UUID m_itemID, @@ -440,8 +454,6 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api.Plugins private List doAgentSensor(SenseRepeatClass ts) { - INPCModule npcModule = m_CmdManager.m_ScriptEngine.World.RequestModuleInterface(); - List sensedEntities = new List(); // If nobody about quit fast @@ -477,7 +489,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api.Plugins bool attached = (SensePoint.ParentGroup.AttachmentPoint != 0); Vector3 toRegionPos; double dis; - + Action senseEntity = new Action(presence => { // m_log.DebugFormat( @@ -486,7 +498,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api.Plugins if ((ts.type & NPC) == 0 && presence.PresenceType == PresenceType.Npc) { - INPC npcData = npcModule.GetNPC(presence.UUID, presence.Scene); + INPC npcData = m_npcModule.GetNPC(presence.UUID, presence.Scene); if (npcData == null || !npcData.SenseAsAgent) { // m_log.DebugFormat( @@ -504,7 +516,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api.Plugins } else { - INPC npcData = npcModule.GetNPC(presence.UUID, presence.Scene); + INPC npcData = m_npcModule.GetNPC(presence.UUID, presence.Scene); if (npcData != null && npcData.SenseAsAgent) { // m_log.DebugFormat( @@ -619,21 +631,19 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api.Plugins { List data = new List(); - lock (SenseRepeatListLock) + foreach (SenseRepeatClass ts in SenseRepeaters) { - foreach (SenseRepeatClass ts in SenseRepeaters) + if (ts.itemID == itemID) { - if (ts.itemID == itemID) - { - data.Add(ts.interval); - data.Add(ts.name); - data.Add(ts.keyID); - data.Add(ts.type); - data.Add(ts.range); - data.Add(ts.arc); - } + data.Add(ts.interval); + data.Add(ts.name); + data.Add(ts.keyID); + data.Add(ts.type); + data.Add(ts.range); + data.Add(ts.arc); } } + return data.ToArray(); } @@ -667,8 +677,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api.Plugins ts.next = DateTime.Now.ToUniversalTime().AddSeconds(ts.interval); - lock (SenseRepeatListLock) - SenseRepeaters.Add(ts); + AddSenseRepeater(ts); idx += 6; } diff --git a/OpenSim/Region/ScriptEngine/Shared/Api/Interface/IOSSL_Api.cs b/OpenSim/Region/ScriptEngine/Shared/Api/Interface/IOSSL_Api.cs index 1facc96d0c..aba66d32b2 100644 --- a/OpenSim/Region/ScriptEngine/Shared/Api/Interface/IOSSL_Api.cs +++ b/OpenSim/Region/ScriptEngine/Shared/Api/Interface/IOSSL_Api.cs @@ -276,5 +276,28 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api.Interfaces void osSetTerrainTexture(int level, LSL_Key texture); void osSetTerrainTextureHeight(int corner, double low, double high); + + /// + /// Checks if thing is a UUID. + /// + /// + /// 1 if thing is a valid UUID, 0 otherwise + LSL_Integer osIsUUID(string thing); + + /// + /// Wraps to Math.Min() + /// + /// + /// + /// + LSL_Float osMin(double a, double b); + + /// + /// Wraps to Math.max() + /// + /// + /// + /// + LSL_Float osMax(double a, double b); } } diff --git a/OpenSim/Region/ScriptEngine/Shared/Api/Runtime/OSSL_Stub.cs b/OpenSim/Region/ScriptEngine/Shared/Api/Runtime/OSSL_Stub.cs index b40bdf0b17..53daa13d49 100644 --- a/OpenSim/Region/ScriptEngine/Shared/Api/Runtime/OSSL_Stub.cs +++ b/OpenSim/Region/ScriptEngine/Shared/Api/Runtime/OSSL_Stub.cs @@ -930,5 +930,20 @@ namespace OpenSim.Region.ScriptEngine.Shared.ScriptBase { m_OSSL_Functions.osSetTerrainTextureHeight(corner, low, high); } + + public LSL_Integer osIsUUID(string thing) + { + return m_OSSL_Functions.osIsUUID(thing); + } + + public LSL_Float osMin(double a, double b) + { + return m_OSSL_Functions.osMin(a, b); + } + + public LSL_Float osMax(double a, double b) + { + return m_OSSL_Functions.osMax(a, b); + } } } diff --git a/OpenSim/Region/ScriptEngine/Shared/CodeTools/CSCodeGenerator.cs b/OpenSim/Region/ScriptEngine/Shared/CodeTools/CSCodeGenerator.cs index b24f016a08..97dd0f6122 100644 --- a/OpenSim/Region/ScriptEngine/Shared/CodeTools/CSCodeGenerator.cs +++ b/OpenSim/Region/ScriptEngine/Shared/CodeTools/CSCodeGenerator.cs @@ -38,7 +38,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.CodeTools { public class CSCodeGenerator : ICodeConverter { -// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); +// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); private SYMBOL m_astRoot = null; private Dictionary, KeyValuePair> m_positionMap; @@ -255,7 +255,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.CodeTools else if (s is IdentDotExpression) retstr += Generate(CheckName(((IdentDotExpression) s).Name) + "." + ((IdentDotExpression) s).Member, s); else if (s is IdentExpression) - retstr += Generate(CheckName(((IdentExpression) s).Name), s); + retstr += GenerateIdentifier(((IdentExpression) s).Name, s); else if (s is IDENT) retstr += Generate(CheckName(((TOKEN) s).yytext), s); else @@ -867,6 +867,41 @@ namespace OpenSim.Region.ScriptEngine.Shared.CodeTools return retstr; } + /// + /// Generates the code for an identifier + /// + /// The symbol name + /// The Symbol node. + /// String containing C# code for identifier reference. + private string GenerateIdentifier(string id, SYMBOL s) + { + if (m_comms != null) + { + object value = m_comms.LookupModConstant(id); + if (value != null) + { + string retval = null; + if (value is int) + retval = ((int)value).ToString(); + else if (value is float) + retval = String.Format("new LSL_Types.LSLFloat({0})",((float)value).ToString()); + else if (value is string) + retval = String.Format("new LSL_Types.LSLString(\"{0}\")",((string)value)); + else if (value is OpenMetaverse.UUID) + retval = String.Format("new LSL_Types.key(\"{0}\")",((OpenMetaverse.UUID)value).ToString()); + else if (value is OpenMetaverse.Vector3) + retval = String.Format("new LSL_Types.Vector3(\"{0}\")",((OpenMetaverse.Vector3)value).ToString()); + else if (value is OpenMetaverse.Quaternion) + retval = String.Format("new LSL_Types.Quaternion(\"{0}\")",((OpenMetaverse.Quaternion)value).ToString()); + else retval = id; + + return Generate(retval, s); + } + } + + return Generate(CheckName(id), s); + } + /// /// Generates the code for a FunctionCall node. /// diff --git a/OpenSim/Region/ScriptEngine/Shared/Helpers.cs b/OpenSim/Region/ScriptEngine/Shared/Helpers.cs index 5c9d30f80c..9e5fb2420d 100644 --- a/OpenSim/Region/ScriptEngine/Shared/Helpers.cs +++ b/OpenSim/Region/ScriptEngine/Shared/Helpers.cs @@ -36,6 +36,7 @@ using OpenSim.Framework; using OpenSim.Region.CoreModules; using OpenSim.Region.Framework.Scenes; using OpenSim.Services.Interfaces; +using OpenSim.Region.Framework.Interfaces; namespace OpenSim.Region.ScriptEngine.Shared { @@ -83,6 +84,12 @@ namespace OpenSim.Region.ScriptEngine.Shared public class DetectParams { + public const int AGENT = 1; + public const int ACTIVE = 2; + public const int PASSIVE = 4; + public const int SCRIPTED = 8; + public const int OS_NPC = 0x01000000; + public DetectParams() { Key = UUID.Zero; @@ -199,8 +206,27 @@ namespace OpenSim.Region.ScriptEngine.Shared Type = 0x01; // Avatar if (presence.PresenceType == PresenceType.Npc) Type = 0x20; + + // Cope Impl. We don't use OS_NPC. + //if (presence.PresenceType != PresenceType.Npc) + //{ + // Type = AGENT; + //} + //else + //{ + // Type = OS_NPC; + + // INPCModule npcModule = scene.RequestModuleInterface(); + // INPC npcData = npcModule.GetNPC(presence.UUID, presence.Scene); + + // if (npcData.SenseAsAgent) + // { + // Type |= AGENT; + // } + //} + if (presence.Velocity != Vector3.Zero) - Type |= 0x02; // Active + Type |= ACTIVE; Group = presence.ControllingClient.ActiveGroupId; @@ -215,15 +241,15 @@ namespace OpenSim.Region.ScriptEngine.Shared Name = part.Name; Owner = part.OwnerID; if (part.Velocity == Vector3.Zero) - Type = 0x04; // Passive + Type = PASSIVE; else - Type = 0x02; // Passive + Type = ACTIVE; foreach (SceneObjectPart p in part.ParentGroup.Parts) { if (p.Inventory.ContainsScripts()) { - Type |= 0x08; // Scripted + Type |= SCRIPTED; // Scripted break; } } diff --git a/OpenSim/Region/ScriptEngine/Shared/Instance/ScriptInstance.cs b/OpenSim/Region/ScriptEngine/Shared/Instance/ScriptInstance.cs index 983eed2a03..771db0c238 100644 --- a/OpenSim/Region/ScriptEngine/Shared/Instance/ScriptInstance.cs +++ b/OpenSim/Region/ScriptEngine/Shared/Instance/ScriptInstance.cs @@ -312,11 +312,11 @@ namespace OpenSim.Region.ScriptEngine.Shared.Instance part.SetScriptEvents(ItemID, (int)m_Script.GetStateEventFlags(State)); - Running = false; - - if (ShuttingDown) + if (!Running) m_startOnInit = false; + Running = false; + // we get new rez events on sim restart, too // but if there is state, then we fire the change // event @@ -352,12 +352,13 @@ namespace OpenSim.Region.ScriptEngine.Shared.Instance public void Init() { - if (!m_startOnInit) + if (ShuttingDown) return; if (m_startedFromSavedState) { - Start(); + if (m_startOnInit) + Start(); if (m_postOnRez) { PostEvent(new EventParams("on_rez", @@ -389,7 +390,8 @@ namespace OpenSim.Region.ScriptEngine.Shared.Instance } else { - Start(); + if (m_startOnInit) + Start(); PostEvent(new EventParams("state_entry", new Object[0], new DetectParams[0])); if (m_postOnRez) diff --git a/OpenSim/Region/ScriptEngine/XEngine/XEngine.cs b/OpenSim/Region/ScriptEngine/XEngine/XEngine.cs index 35a0200f0f..f6cb7dfa95 100644 --- a/OpenSim/Region/ScriptEngine/XEngine/XEngine.cs +++ b/OpenSim/Region/ScriptEngine/XEngine/XEngine.cs @@ -109,6 +109,8 @@ namespace OpenSim.Region.ScriptEngine.XEngine private bool m_KillTimedOutScripts; private string m_ScriptEnginesPath = null; + private ExpiringCache m_runFlags = new ExpiringCache(); + /// /// Is the entire simulator in the process of shutting down? /// @@ -715,6 +717,10 @@ namespace OpenSim.Region.ScriptEngine.XEngine m_Scene.EventManager.OnGetScriptRunning += OnGetScriptRunning; m_Scene.EventManager.OnShutdown += OnShutdown; + // If region ready has been triggered, then the region had no scripts to compile and completed its other + // work. + m_Scene.EventManager.OnRegionReadyStatusChange += s => { if (s.Ready) m_InitialStartup = false; }; + if (m_SleepTime > 0) { m_ThreadPool.QueueWorkItem(new WorkItemCallback(this.DoMaintenance), @@ -1269,7 +1275,15 @@ namespace OpenSim.Region.ScriptEngine.XEngine if (instance!=null) instance.Init(); - + + bool runIt; + if (m_runFlags.TryGetValue(itemID, out runIt)) + { + if (!runIt) + StopScript(itemID); + m_runFlags.Remove(itemID); + } + return true; } @@ -1660,6 +1674,8 @@ namespace OpenSim.Region.ScriptEngine.XEngine IScriptInstance instance = GetInstance(itemID); if (instance != null) instance.Start(); + else + m_runFlags.AddOrUpdate(itemID, true, 240); } public void StopScript(UUID itemID) @@ -1671,6 +1687,10 @@ namespace OpenSim.Region.ScriptEngine.XEngine // cause issues on mono 2.6, 2.10 and possibly later where locks are not released properly on abort. instance.Stop(1000); } + else + { + m_runFlags.AddOrUpdate(itemID, false, 240); + } } public DetectParams GetDetectParams(UUID itemID, int idx) diff --git a/OpenSim/Region/UserStatistics/ActiveConnectionsAJAX.cs b/OpenSim/Region/UserStatistics/ActiveConnectionsAJAX.cs index dcbd7173d9..3243a9a952 100644 --- a/OpenSim/Region/UserStatistics/ActiveConnectionsAJAX.cs +++ b/OpenSim/Region/UserStatistics/ActiveConnectionsAJAX.cs @@ -34,7 +34,7 @@ using Mono.Data.SqliteClient; using OpenMetaverse; using OpenSim.Framework; using OpenSim.Region.Framework.Scenes; -using OpenSim.Framework.Statistics; +using OpenSim.Framework.Monitoring; namespace OpenSim.Region.UserStatistics { diff --git a/OpenSim/Region/UserStatistics/Default_Report.cs b/OpenSim/Region/UserStatistics/Default_Report.cs index 0e1763046d..cdc615caff 100644 --- a/OpenSim/Region/UserStatistics/Default_Report.cs +++ b/OpenSim/Region/UserStatistics/Default_Report.cs @@ -33,7 +33,7 @@ using System.Text; using Mono.Data.SqliteClient; using OpenMetaverse; using OpenSim.Region.Framework.Scenes; -using OpenSim.Framework.Statistics; +using OpenSim.Framework.Monitoring; namespace OpenSim.Region.UserStatistics diff --git a/OpenSim/Region/UserStatistics/LogLinesAJAX.cs b/OpenSim/Region/UserStatistics/LogLinesAJAX.cs index 811baba998..74de46b227 100644 --- a/OpenSim/Region/UserStatistics/LogLinesAJAX.cs +++ b/OpenSim/Region/UserStatistics/LogLinesAJAX.cs @@ -34,7 +34,7 @@ using System.Text.RegularExpressions; using Mono.Data.SqliteClient; using OpenMetaverse; using OpenSim.Region.Framework.Scenes; -using OpenSim.Framework.Statistics; +using OpenSim.Framework.Monitoring; namespace OpenSim.Region.UserStatistics { diff --git a/OpenSim/Region/UserStatistics/SimStatsAJAX.cs b/OpenSim/Region/UserStatistics/SimStatsAJAX.cs index 8c04e71a61..28051fb674 100644 --- a/OpenSim/Region/UserStatistics/SimStatsAJAX.cs +++ b/OpenSim/Region/UserStatistics/SimStatsAJAX.cs @@ -33,7 +33,7 @@ using System.Text; using Mono.Data.SqliteClient; using OpenMetaverse; using OpenSim.Region.Framework.Scenes; -using OpenSim.Framework.Statistics; +using OpenSim.Framework.Monitoring; namespace OpenSim.Region.UserStatistics { diff --git a/OpenSim/Services/Connectors/Inventory/XInventoryServicesConnector.cs b/OpenSim/Services/Connectors/Inventory/XInventoryServicesConnector.cs index 9d96703c1a..44f5e0180c 100644 --- a/OpenSim/Services/Connectors/Inventory/XInventoryServicesConnector.cs +++ b/OpenSim/Services/Connectors/Inventory/XInventoryServicesConnector.cs @@ -122,7 +122,7 @@ namespace OpenSim.Services.Connectors } catch (Exception e) { - m_log.DebugFormat("[XINVENTORY CONNECTOR STUB]: Exception unwrapping folder list: {0}", e.Message); + m_log.Error("[XINVENTORY SERVICES CONNECTOR]: Exception unwrapping folder list: ", e); } return fldrs; @@ -191,7 +191,7 @@ namespace OpenSim.Services.Connectors } catch (Exception e) { - m_log.DebugFormat("[XINVENTORY CONNECTOR STUB]: Exception in GetFolderContent: {0}", e.Message); + m_log.WarnFormat("[XINVENTORY SERVICES CONNECTOR]: Exception in GetFolderContent: {0}", e.Message); } return inventory; @@ -432,7 +432,7 @@ namespace OpenSim.Services.Connectors } catch (Exception e) { - m_log.DebugFormat("[XINVENTORY CONNECTOR STUB]: Exception in GetItem: {0}", e.Message); + m_log.Error("[XINVENTORY SERVICES CONNECTOR]: Exception in GetItem: ", e); } return null; @@ -456,7 +456,7 @@ namespace OpenSim.Services.Connectors } catch (Exception e) { - m_log.DebugFormat("[XINVENTORY CONNECTOR STUB]: Exception in GetFolder: {0}", e.Message); + m_log.Error("[XINVENTORY SERVICES CONNECTOR]: Exception in GetFolder: ", e); } return null; @@ -474,7 +474,7 @@ namespace OpenSim.Services.Connectors List items = new List(); - foreach (Object o in ret.Values) // getting the values directly, we don't care about the keys item_i + foreach (Object o in ((Dictionary)ret["ITEMS"]).Values) items.Add(BuildItem((Dictionary)o)); return items; @@ -525,7 +525,7 @@ namespace OpenSim.Services.Connectors } catch (Exception e) { - m_log.DebugFormat("[XINVENTORY CONNECTOR STUB]: Exception in GetUserInventory: {0}", e.Message); + m_log.Error("[XINVENTORY SERVICES CONNECTOR]: Exception in GetUserInventory: ", e); } return inventory; @@ -574,7 +574,7 @@ namespace OpenSim.Services.Connectors } catch (Exception e) { - m_log.DebugFormat("[XINVENTORY CONNECTOR STUB]: Exception building folder: {0}", e.Message); + m_log.Error("[XINVENTORY SERVICES CONNECTOR]: Exception building folder: ", e); } return folder; @@ -613,11 +613,10 @@ namespace OpenSim.Services.Connectors } catch (Exception e) { - m_log.DebugFormat("[XINVENTORY CONNECTOR STUB]: Exception building item: {0}", e.Message); + m_log.Error("[XINVENTORY CONNECTOR]: Exception building item: ", e); } return item; } - } -} +} \ No newline at end of file diff --git a/OpenSim/Tests/Common/Helpers/SceneHelpers.cs b/OpenSim/Tests/Common/Helpers/SceneHelpers.cs index 769de83c9c..7598cc360e 100644 --- a/OpenSim/Tests/Common/Helpers/SceneHelpers.cs +++ b/OpenSim/Tests/Common/Helpers/SceneHelpers.cs @@ -190,7 +190,7 @@ namespace OpenSim.Tests.Common = physicsPluginManager.GetPhysicsScene("basicphysics", "ZeroMesher", new IniConfigSource(), "test"); testScene.RegionInfo.EstateSettings = new EstateSettings(); - testScene.LoginsDisabled = false; + testScene.LoginsEnabled = true; testScene.RegisterRegionWithGrid(); SceneManager.Add(testScene); diff --git a/OpenSim/Tests/Common/Helpers/TaskInventoryHelpers.cs b/OpenSim/Tests/Common/Helpers/TaskInventoryHelpers.cs index fba03abcf0..0a2b30a279 100644 --- a/OpenSim/Tests/Common/Helpers/TaskInventoryHelpers.cs +++ b/OpenSim/Tests/Common/Helpers/TaskInventoryHelpers.cs @@ -79,9 +79,27 @@ namespace OpenSim.Tests.Common /// /// The item that was added public static TaskInventoryItem AddScript(Scene scene, SceneObjectPart part) + { + return AddScript(scene, part, "scriptItem", "default { state_entry() { llSay(0, \"Hello World\"); } }"); + } + + /// + /// Add a simple script to the given part. + /// + /// + /// TODO: Accept input for item and asset IDs to avoid mysterious script failures that try to use any of these + /// functions more than once in a test. + /// + /// + /// + /// Name of the script to add + /// LSL script source + /// The item that was added + public static TaskInventoryItem AddScript( + Scene scene, SceneObjectPart part, string scriptName, string scriptSource) { AssetScriptText ast = new AssetScriptText(); - ast.Source = "default { state_entry() { llSay(0, \"Hello World\"); } }"; + ast.Source = scriptSource; ast.Encode(); UUID assetUuid = new UUID("00000000-0000-0000-1000-000000000000"); @@ -91,7 +109,7 @@ namespace OpenSim.Tests.Common scene.AssetService.Store(asset); TaskInventoryItem item = new TaskInventoryItem - { Name = "scriptItem", AssetID = assetUuid, ItemID = itemUuid, + { Name = scriptName, AssetID = assetUuid, ItemID = itemUuid, Type = (int)AssetType.LSLText, InvType = (int)InventoryType.LSL }; part.Inventory.AddInventoryItem(item, true); diff --git a/OpenSim/Tools/pCampBot/Behaviours/GrabbingBehaviour.cs b/OpenSim/Tools/pCampBot/Behaviours/GrabbingBehaviour.cs index 701881e32f..66a336a0c6 100644 --- a/OpenSim/Tools/pCampBot/Behaviours/GrabbingBehaviour.cs +++ b/OpenSim/Tools/pCampBot/Behaviours/GrabbingBehaviour.cs @@ -47,6 +47,9 @@ namespace pCampBot { Dictionary objects = Bot.Objects; + if (objects.Count <= 0) + return; + Primitive prim = objects.ElementAt(Bot.Random.Next(0, objects.Count - 1)).Value; // This appears to be a typical message sent when a viewer user clicks a clickable object diff --git a/bin/lib32/BulletSim.dll b/bin/lib32/BulletSim.dll deleted file mode 100755 index 06a3cf1b0a..0000000000 Binary files a/bin/lib32/BulletSim.dll and /dev/null differ diff --git a/bin/lib32/libBulletSim.so b/bin/lib32/libBulletSim.so deleted file mode 100755 index ce187b0dbf..0000000000 Binary files a/bin/lib32/libBulletSim.so and /dev/null differ diff --git a/bin/lib64/BulletSim.dll b/bin/lib64/BulletSim.dll deleted file mode 100755 index f51c978349..0000000000 Binary files a/bin/lib64/BulletSim.dll and /dev/null differ diff --git a/bin/lib64/libBulletSim.so b/bin/lib64/libBulletSim.so deleted file mode 100755 index 2476865e56..0000000000 Binary files a/bin/lib64/libBulletSim.so and /dev/null differ diff --git a/prebuild.xml b/prebuild.xml index cb62aecaea..1b599a918e 100644 --- a/prebuild.xml +++ b/prebuild.xml @@ -112,6 +112,32 @@ + + + + ../../../bin/ + + + + + ../../../bin/ + + + + ../../../bin/ + + + + + + + + + + + + + @@ -130,6 +156,7 @@ + @@ -235,30 +262,6 @@ - - - - ../../../bin/ - - - - - ../../../bin/ - - - - ../../../bin/ - - - - - - - - - - - @@ -436,7 +439,7 @@ - + @@ -640,39 +643,6 @@ - - - - ../../../../bin/Physics/ - - - - - ../../../../bin/Physics/ - - - - ../../../../bin/ - - - - - - - - - - - - - - - - - - - - @@ -753,9 +723,9 @@ + - @@ -787,13 +757,13 @@ + - + + - - @@ -839,10 +809,10 @@ + - @@ -1558,10 +1528,10 @@ + - @@ -1649,11 +1619,11 @@ + + - - @@ -1686,15 +1656,15 @@ - - + + - + @@ -1777,9 +1747,9 @@ + - @@ -1834,6 +1804,64 @@ + + + + ../../../../bin/ + + + + + ../../../../bin/ + + + + ../../../../bin/ + + + + + + + + + + + + + + + + ../../../../bin/Physics/ + + + + + ../../../../bin/Physics/ + + + + ../../../../bin/ + + + + + + + + + + + + + + + + + + + + @@ -1857,15 +1885,15 @@ + - + - - + @@ -2378,10 +2406,11 @@ + + - @@ -2504,7 +2533,6 @@ - @@ -2533,9 +2561,9 @@ + - @@ -3019,9 +3047,9 @@ + - @@ -3091,9 +3119,9 @@ + - @@ -3150,8 +3178,8 @@ + - @@ -3207,9 +3235,9 @@ + - @@ -3244,7 +3272,7 @@ - + diff --git a/runprebuild.sh b/runprebuild.sh index b3b5c9d1d3..d276edbee4 100755 --- a/runprebuild.sh +++ b/runprebuild.sh @@ -1,4 +1,42 @@ #!/bin/sh -mono bin/Prebuild.exe /target nant -mono bin/Prebuild.exe /target vs2008 +case "$1" in + + 'clean') + + mono bin/Prebuild.exe /clean + + ;; + + + 'autoclean') + + echo y|mono bin/Prebuild.exe /clean + + ;; + + + 'vs2010') + + mono bin/Prebuild.exe /target vs2010 + + ;; + + + 'vs2008') + + mono bin/Prebuild.exe /target vs2008 + + ;; + + + *) + + mono bin/Prebuild.exe /target nant + mono bin/Prebuild.exe /target vs2008 + + ;; + + +esac +