diff --git a/OpenSim/Region/Application/OpenSim.cs b/OpenSim/Region/Application/OpenSim.cs index daae3e64b4..caf74cf801 100644 --- a/OpenSim/Region/Application/OpenSim.cs +++ b/OpenSim/Region/Application/OpenSim.cs @@ -28,6 +28,7 @@ using System; using System.Collections; using System.Collections.Generic; +using System.Diagnostics; using System.IO; using System.Reflection; using System.Text; @@ -138,7 +139,7 @@ namespace OpenSim m_log.Info("===================================================================="); m_log.Info("========================= STARTING OPENSIM ========================="); m_log.Info("===================================================================="); - + //m_log.InfoFormat("[OPENSIM MAIN]: GC Is Server GC: {0}", GCSettings.IsServerGC.ToString()); // http://msdn.microsoft.com/en-us/library/bb384202.aspx //GCSettings.LatencyMode = GCLatencyMode.Batch; diff --git a/OpenSim/Region/CoreModules/Avatar/Commands/UserCommandsModule.cs b/OpenSim/Region/CoreModules/Avatar/Commands/UserCommandsModule.cs new file mode 100644 index 0000000000..4bcd2ac499 --- /dev/null +++ b/OpenSim/Region/CoreModules/Avatar/Commands/UserCommandsModule.cs @@ -0,0 +1,189 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Text; +using System.Text.RegularExpressions; +using log4net; +using Mono.Addins; +using NDesk.Options; +using Nini.Config; +using OpenMetaverse; +using OpenSim.Framework; +using OpenSim.Framework.Console; +using OpenSim.Framework.Statistics; +using OpenSim.Region.Framework.Interfaces; +using OpenSim.Region.Framework.Scenes; + +namespace OpenSim.Region.CoreModules.Avatars.Commands +{ + /// + /// A module that holds commands for manipulating objects in the scene. + /// + [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "UserCommandsModule")] + public class UserCommandsModule : ISharedRegionModule + { +// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + public const string TeleportUserCommandSyntax = "teleport user "; + + public static Regex InterRegionDestinationRegex + = new Regex(@"^(?.+)/(?\d+)/(?\d+)/(?\d+)$", RegexOptions.Compiled); + + public static Regex WithinRegionDestinationRegex + = new Regex(@"^(?\d+)/(?\d+)/(?\d+)$", RegexOptions.Compiled); + + private Dictionary m_scenes = new Dictionary(); + + public string Name { get { return "User Commands Module"; } } + + public Type ReplaceableInterface { get { return null; } } + + public void Initialise(IConfigSource source) + { +// m_log.DebugFormat("[USER COMMANDS MODULE]: INITIALIZED MODULE"); + } + + public void PostInitialise() + { +// m_log.DebugFormat("[USER COMMANDS MODULE]: POST INITIALIZED MODULE"); + } + + public void Close() + { +// m_log.DebugFormat("[USER COMMANDS MODULE]: CLOSED MODULE"); + } + + public void AddRegion(Scene scene) + { +// m_log.DebugFormat("[USER COMMANDS MODULE]: REGION {0} ADDED", scene.RegionInfo.RegionName); + + lock (m_scenes) + m_scenes[scene.RegionInfo.RegionID] = scene; + + scene.AddCommand( + "Users", + this, + "teleport user", + TeleportUserCommandSyntax, + "Teleport a user in this simulator to the given destination", + " is in format []///, e.g. regionone/20/30/40 or just 20/30/40 to teleport within same region." + + "\nIf the region contains a space then the whole destination must be in quotes, e.g. \"region one/20/30/40\"", + HandleTeleportUser); + } + + public void RemoveRegion(Scene scene) + { +// m_log.DebugFormat("[USER COMMANDS MODULE]: REGION {0} REMOVED", scene.RegionInfo.RegionName); + + lock (m_scenes) + m_scenes.Remove(scene.RegionInfo.RegionID); + } + + public void RegionLoaded(Scene scene) + { +// m_log.DebugFormat("[USER COMMANDS MODULE]: REGION {0} LOADED", scene.RegionInfo.RegionName); + } + + private ScenePresence GetUser(string firstName, string lastName) + { + ScenePresence userFound = null; + + lock (m_scenes) + { + foreach (Scene scene in m_scenes.Values) + { + ScenePresence user = scene.GetScenePresence(firstName, lastName); + if (user != null && !user.IsChildAgent) + { + userFound = user; + break; + } + } + } + + return userFound; + } + + private void HandleTeleportUser(string module, string[] cmd) + { + if (cmd.Length < 5) + { + MainConsole.Instance.OutputFormat("Usage: " + TeleportUserCommandSyntax); + return; + } + + string firstName = cmd[2]; + string lastName = cmd[3]; + string rawDestination = cmd[4]; + + ScenePresence user = GetUser(firstName, lastName); + + if (user == null) + { + MainConsole.Instance.OutputFormat("No user found with name {0} {1}", firstName, lastName); + return; + } + +// MainConsole.Instance.OutputFormat("rawDestination [{0}]", rawDestination); + + Match m = WithinRegionDestinationRegex.Match(rawDestination); + + if (!m.Success) + { + m = InterRegionDestinationRegex.Match(rawDestination); + + if (!m.Success) + { + MainConsole.Instance.OutputFormat("Invalid destination {0}", rawDestination); + return; + } + } + + string regionName + = m.Groups["regionName"].Success ? m.Groups["regionName"].Value : user.Scene.RegionInfo.RegionName; + + MainConsole.Instance.OutputFormat( + "Teleporting {0} to {1},{2},{3} in {4}", + user.Name, + m.Groups["x"], m.Groups["y"], m.Groups["z"], + regionName); + + user.Scene.RequestTeleportLocation( + user.ControllingClient, + regionName, + new Vector3( + float.Parse(m.Groups["x"].Value), + float.Parse(m.Groups["y"].Value), + float.Parse(m.Groups["z"].Value)), + user.Lookat, + (uint)TeleportFlags.ViaLocation); + } + } +} \ No newline at end of file diff --git a/OpenSim/Region/CoreModules/Framework/Monitoring/MonitorModule.cs b/OpenSim/Region/CoreModules/Framework/Monitoring/MonitorModule.cs index 7f8271d401..4a8c369335 100644 --- a/OpenSim/Region/CoreModules/Framework/Monitoring/MonitorModule.cs +++ b/OpenSim/Region/CoreModules/Framework/Monitoring/MonitorModule.cs @@ -49,7 +49,16 @@ namespace OpenSim.Region.CoreModules.Framework.Monitoring public bool Enabled { get; private set; } private Scene m_scene; - private readonly List m_monitors = new List(); + + /// + /// These are monitors where we know the static details in advance. + /// + /// + /// Dynamic monitors also exist (we don't know any of the details of what stats we get back here) + /// but these are currently hardcoded. + /// + private readonly List m_staticMonitors = new List(); + private readonly List m_alerts = new List(); private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); @@ -84,9 +93,18 @@ namespace OpenSim.Region.CoreModules.Framework.Monitoring public void DebugMonitors(string module, string[] args) { - foreach (IMonitor monitor in m_monitors) + foreach (IMonitor monitor in m_staticMonitors) { - m_log.Info("[MonitorModule]: " + m_scene.RegionInfo.RegionName + " reports " + monitor.GetFriendlyName() + " = " + monitor.GetFriendlyValue()); + m_log.InfoFormat( + "[MONITOR MODULE]: {0} reports {1} = {2}", + m_scene.RegionInfo.RegionName, monitor.GetFriendlyName(), monitor.GetFriendlyValue()); + } + + foreach (KeyValuePair tuple in m_scene.StatsReporter.GetExtraSimStats()) + { + m_log.InfoFormat( + "[MONITOR MODULE]: {0} reports {1} = {2}", + m_scene.RegionInfo.RegionName, tuple.Key, tuple.Value); } } @@ -106,11 +124,12 @@ namespace OpenSim.Region.CoreModules.Framework.Monitoring { string monID = (string) request["monitor"]; - foreach (IMonitor monitor in m_monitors) + foreach (IMonitor monitor in m_staticMonitors) { string elemName = monitor.ToString(); if (elemName.StartsWith(monitor.GetType().Namespace)) elemName = elemName.Substring(monitor.GetType().Namespace.Length + 1); + if (elemName == monID || monitor.ToString() == monID) { Hashtable ereply3 = new Hashtable(); @@ -123,6 +142,9 @@ namespace OpenSim.Region.CoreModules.Framework.Monitoring } } + // FIXME: Arguably this should also be done with dynamic monitors but I'm not sure what the above code + // is even doing. Why are we inspecting the type of the monitor??? + // No monitor with that name Hashtable ereply2 = new Hashtable(); @@ -134,12 +156,18 @@ namespace OpenSim.Region.CoreModules.Framework.Monitoring } string xml = ""; - foreach (IMonitor monitor in m_monitors) + foreach (IMonitor monitor in m_staticMonitors) { string elemName = monitor.GetName(); xml += "<" + elemName + ">" + monitor.GetValue().ToString() + ""; // m_log.DebugFormat("[MONITOR MODULE]: {0} = {1}", elemName, monitor.GetValue()); } + + foreach (KeyValuePair tuple in m_scene.StatsReporter.GetExtraSimStats()) + { + xml += "<" + tuple.Key + ">" + tuple.Value + ""; + } + xml += ""; Hashtable ereply = new Hashtable(); @@ -156,20 +184,20 @@ namespace OpenSim.Region.CoreModules.Framework.Monitoring if (!Enabled) return; - m_monitors.Add(new AgentCountMonitor(m_scene)); - m_monitors.Add(new ChildAgentCountMonitor(m_scene)); - m_monitors.Add(new GCMemoryMonitor()); - m_monitors.Add(new ObjectCountMonitor(m_scene)); - m_monitors.Add(new PhysicsFrameMonitor(m_scene)); - m_monitors.Add(new PhysicsUpdateFrameMonitor(m_scene)); - m_monitors.Add(new PWSMemoryMonitor()); - m_monitors.Add(new ThreadCountMonitor()); - m_monitors.Add(new TotalFrameMonitor(m_scene)); - m_monitors.Add(new EventFrameMonitor(m_scene)); - m_monitors.Add(new LandFrameMonitor(m_scene)); - m_monitors.Add(new LastFrameTimeMonitor(m_scene)); + m_staticMonitors.Add(new AgentCountMonitor(m_scene)); + m_staticMonitors.Add(new ChildAgentCountMonitor(m_scene)); + m_staticMonitors.Add(new GCMemoryMonitor()); + m_staticMonitors.Add(new ObjectCountMonitor(m_scene)); + m_staticMonitors.Add(new PhysicsFrameMonitor(m_scene)); + m_staticMonitors.Add(new PhysicsUpdateFrameMonitor(m_scene)); + m_staticMonitors.Add(new PWSMemoryMonitor()); + m_staticMonitors.Add(new ThreadCountMonitor()); + m_staticMonitors.Add(new TotalFrameMonitor(m_scene)); + m_staticMonitors.Add(new EventFrameMonitor(m_scene)); + m_staticMonitors.Add(new LandFrameMonitor(m_scene)); + m_staticMonitors.Add(new LastFrameTimeMonitor(m_scene)); - m_monitors.Add( + m_staticMonitors.Add( new GenericMonitor( m_scene, "TimeDilationMonitor", @@ -177,7 +205,7 @@ namespace OpenSim.Region.CoreModules.Framework.Monitoring m => m.Scene.StatsReporter.LastReportedSimStats[0], m => m.GetValue().ToString())); - m_monitors.Add( + m_staticMonitors.Add( new GenericMonitor( m_scene, "SimFPSMonitor", @@ -185,7 +213,7 @@ namespace OpenSim.Region.CoreModules.Framework.Monitoring m => m.Scene.StatsReporter.LastReportedSimStats[1], m => string.Format("{0}", m.GetValue()))); - m_monitors.Add( + m_staticMonitors.Add( new GenericMonitor( m_scene, "PhysicsFPSMonitor", @@ -193,7 +221,7 @@ namespace OpenSim.Region.CoreModules.Framework.Monitoring m => m.Scene.StatsReporter.LastReportedSimStats[2], m => string.Format("{0}", m.GetValue()))); - m_monitors.Add( + m_staticMonitors.Add( new GenericMonitor( m_scene, "AgentUpdatesPerSecondMonitor", @@ -201,15 +229,7 @@ namespace OpenSim.Region.CoreModules.Framework.Monitoring m => m.Scene.StatsReporter.LastReportedSimStats[3], m => string.Format("{0} per second", m.GetValue()))); - m_monitors.Add( - new GenericMonitor( - m_scene, - "ObjectUpdatesPerSecondMonitor", - "Object Updates", - m => m.Scene.StatsReporter.LastReportedObjectUpdates, - m => string.Format("{0} per second", m.GetValue()))); - - m_monitors.Add( + m_staticMonitors.Add( new GenericMonitor( m_scene, "ActiveObjectCountMonitor", @@ -217,7 +237,7 @@ namespace OpenSim.Region.CoreModules.Framework.Monitoring m => m.Scene.StatsReporter.LastReportedSimStats[7], m => string.Format("{0}", m.GetValue()))); - m_monitors.Add( + m_staticMonitors.Add( new GenericMonitor( m_scene, "ActiveScriptsMonitor", @@ -225,7 +245,7 @@ namespace OpenSim.Region.CoreModules.Framework.Monitoring m => m.Scene.StatsReporter.LastReportedSimStats[19], m => string.Format("{0}", m.GetValue()))); - m_monitors.Add( + m_staticMonitors.Add( new GenericMonitor( m_scene, "ScriptEventsPerSecondMonitor", @@ -233,7 +253,7 @@ namespace OpenSim.Region.CoreModules.Framework.Monitoring m => m.Scene.StatsReporter.LastReportedSimStats[20], m => string.Format("{0} per second", m.GetValue()))); - m_monitors.Add( + m_staticMonitors.Add( new GenericMonitor( m_scene, "InPacketsPerSecondMonitor", @@ -241,7 +261,7 @@ namespace OpenSim.Region.CoreModules.Framework.Monitoring m => m.Scene.StatsReporter.LastReportedSimStats[13], m => string.Format("{0} per second", m.GetValue()))); - m_monitors.Add( + m_staticMonitors.Add( new GenericMonitor( m_scene, "OutPacketsPerSecondMonitor", @@ -249,7 +269,7 @@ namespace OpenSim.Region.CoreModules.Framework.Monitoring m => m.Scene.StatsReporter.LastReportedSimStats[14], m => string.Format("{0} per second", m.GetValue()))); - m_monitors.Add( + m_staticMonitors.Add( new GenericMonitor( m_scene, "UnackedBytesMonitor", @@ -257,7 +277,7 @@ namespace OpenSim.Region.CoreModules.Framework.Monitoring m => m.Scene.StatsReporter.LastReportedSimStats[15], m => string.Format("{0}", m.GetValue()))); - m_monitors.Add( + m_staticMonitors.Add( new GenericMonitor( m_scene, "PendingDownloadsMonitor", @@ -265,7 +285,7 @@ namespace OpenSim.Region.CoreModules.Framework.Monitoring m => m.Scene.StatsReporter.LastReportedSimStats[17], m => string.Format("{0}", m.GetValue()))); - m_monitors.Add( + m_staticMonitors.Add( new GenericMonitor( m_scene, "PendingUploadsMonitor", @@ -273,7 +293,7 @@ namespace OpenSim.Region.CoreModules.Framework.Monitoring m => m.Scene.StatsReporter.LastReportedSimStats[18], m => string.Format("{0}", m.GetValue()))); - m_monitors.Add( + m_staticMonitors.Add( new GenericMonitor( m_scene, "TotalFrameTimeMonitor", @@ -281,7 +301,7 @@ namespace OpenSim.Region.CoreModules.Framework.Monitoring m => m.Scene.StatsReporter.LastReportedSimStats[8], m => string.Format("{0} ms", m.GetValue()))); - m_monitors.Add( + m_staticMonitors.Add( new GenericMonitor( m_scene, "NetFrameTimeMonitor", @@ -289,7 +309,7 @@ namespace OpenSim.Region.CoreModules.Framework.Monitoring m => m.Scene.StatsReporter.LastReportedSimStats[9], m => string.Format("{0} ms", m.GetValue()))); - m_monitors.Add( + m_staticMonitors.Add( new GenericMonitor( m_scene, "PhysicsFrameTimeMonitor", @@ -297,7 +317,7 @@ namespace OpenSim.Region.CoreModules.Framework.Monitoring m => m.Scene.StatsReporter.LastReportedSimStats[10], m => string.Format("{0} ms", m.GetValue()))); - m_monitors.Add( + m_staticMonitors.Add( new GenericMonitor( m_scene, "SimulationFrameTimeMonitor", @@ -305,7 +325,7 @@ namespace OpenSim.Region.CoreModules.Framework.Monitoring m => m.Scene.StatsReporter.LastReportedSimStats[12], m => string.Format("{0} ms", m.GetValue()))); - m_monitors.Add( + m_staticMonitors.Add( new GenericMonitor( m_scene, "AgentFrameTimeMonitor", @@ -313,7 +333,7 @@ namespace OpenSim.Region.CoreModules.Framework.Monitoring m => m.Scene.StatsReporter.LastReportedSimStats[16], m => string.Format("{0} ms", m.GetValue()))); - m_monitors.Add( + m_staticMonitors.Add( new GenericMonitor( m_scene, "ImagesFrameTimeMonitor", @@ -321,7 +341,7 @@ namespace OpenSim.Region.CoreModules.Framework.Monitoring m => m.Scene.StatsReporter.LastReportedSimStats[11], m => string.Format("{0} ms", m.GetValue()))); - m_alerts.Add(new DeadlockAlert(m_monitors.Find(x => x is LastFrameTimeMonitor) as LastFrameTimeMonitor)); + m_alerts.Add(new DeadlockAlert(m_staticMonitors.Find(x => x is LastFrameTimeMonitor) as LastFrameTimeMonitor)); foreach (IAlert alert in m_alerts) { diff --git a/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs b/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs index 749b281470..f7c6413a67 100644 --- a/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs +++ b/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs @@ -2509,21 +2509,21 @@ namespace OpenSim.Region.Framework.Scenes { } - private bool CollisionFilteredOut(SceneObjectPart dest, UUID objectID, string objectName) + public bool CollisionFilteredOut(UUID objectID, string objectName) { - if(dest.CollisionFilter.Count == 0) + if(CollisionFilter.Count == 0) return false; - if (dest.CollisionFilter.ContainsValue(objectID.ToString()) || - dest.CollisionFilter.ContainsValue(objectID.ToString() + objectName) || - dest.CollisionFilter.ContainsValue(UUID.Zero.ToString() + objectName)) + if (CollisionFilter.ContainsValue(objectID.ToString()) || + CollisionFilter.ContainsValue(objectID.ToString() + objectName) || + CollisionFilter.ContainsValue(UUID.Zero.ToString() + objectName)) { - if (dest.CollisionFilter.ContainsKey(1)) + if (CollisionFilter.ContainsKey(1)) return false; return true; } - if (dest.CollisionFilter.ContainsKey(1)) + if (CollisionFilter.ContainsKey(1)) return true; return false; @@ -2586,7 +2586,7 @@ namespace OpenSim.Region.Framework.Scenes SceneObjectPart obj = ParentGroup.Scene.GetSceneObjectPart(localId); if (obj != null) { - if (!dest.CollisionFilteredOut(this, obj.UUID, obj.Name)) + if (!dest.CollisionFilteredOut(obj.UUID, obj.Name)) colliding.Add(CreateDetObject(obj)); } else @@ -2594,7 +2594,7 @@ namespace OpenSim.Region.Framework.Scenes ScenePresence av = ParentGroup.Scene.GetScenePresence(localId); if (av != null && (!av.IsChildAgent)) { - if (!dest.CollisionFilteredOut(this, av.UUID, av.Name)) + if (!dest.CollisionFilteredOut(av.UUID, av.Name)) colliding.Add(CreateDetObject(av)); } } diff --git a/OpenSim/Region/Framework/Scenes/SimStatsReporter.cs b/OpenSim/Region/Framework/Scenes/SimStatsReporter.cs index a4afd4705f..cb102d09d5 100644 --- a/OpenSim/Region/Framework/Scenes/SimStatsReporter.cs +++ b/OpenSim/Region/Framework/Scenes/SimStatsReporter.cs @@ -26,7 +26,7 @@ */ using System; -//using System.Collections.Generic; +using System.Collections.Generic; using System.Timers; using OpenMetaverse.Packets; using OpenSim.Framework; @@ -35,10 +35,18 @@ using OpenSim.Region.Framework.Interfaces; namespace OpenSim.Region.Framework.Scenes { + /// + /// Collect statistics from the scene to send to the client and for access by other monitoring tools. + /// + /// + /// FIXME: This should be a monitoring region module + /// public class SimStatsReporter { -// private static readonly log4net.ILog m_log -// = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + private static readonly log4net.ILog m_log + = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + public const string LastReportedObjectUpdateStatName = "LastReportedObjectUpdates"; public delegate void SendStatResult(SimStats stats); @@ -113,6 +121,14 @@ namespace OpenSim.Region.Framework.Scenes get { return lastReportedSimStats; } } + /// + /// Extra sim statistics that are used by monitors but not sent to the client. + /// + /// + /// The keys are the stat names. + /// + private Dictionary m_lastReportedExtraSimStats = new Dictionary(); + // Sending a stats update every 3 seconds- private int statsUpdatesEveryMS = 3000; private float statsUpdateFactor = 0; @@ -387,7 +403,20 @@ namespace OpenSim.Region.Framework.Scenes } // Extra statistics that aren't currently sent to clients - LastReportedObjectUpdates = m_objectUpdates / statsUpdateFactor; + lock (m_lastReportedExtraSimStats) + { + m_lastReportedExtraSimStats[LastReportedObjectUpdateStatName] = m_objectUpdates / statsUpdateFactor; + + Dictionary physicsStats = m_scene.PhysicsScene.GetStats(); + + if (physicsStats != null) + { + foreach (KeyValuePair tuple in physicsStats) + { + m_lastReportedExtraSimStats[tuple.Key] = tuple.Value / statsUpdateFactor; + } + } + } resetvalues(); } @@ -546,7 +575,10 @@ namespace OpenSim.Region.Framework.Scenes public void AddPendingDownloads(int count) { m_pendingDownloads += count; - if (m_pendingDownloads < 0) m_pendingDownloads = 0; + + if (m_pendingDownloads < 0) + m_pendingDownloads = 0; + //m_log.InfoFormat("[stats]: Adding {0} to pending downloads to make {1}", count, m_pendingDownloads); } @@ -568,5 +600,11 @@ namespace OpenSim.Region.Framework.Scenes } #endregion + + public Dictionary GetExtraSimStats() + { + lock (m_lastReportedExtraSimStats) + return new Dictionary(m_lastReportedExtraSimStats); + } } } diff --git a/OpenSim/Region/Physics/Manager/PhysicsScene.cs b/OpenSim/Region/Physics/Manager/PhysicsScene.cs index d10a2aa29d..cfede557fc 100644 --- a/OpenSim/Region/Physics/Manager/PhysicsScene.cs +++ b/OpenSim/Region/Physics/Manager/PhysicsScene.cs @@ -241,8 +241,22 @@ namespace OpenSim.Region.Physics.Manager public abstract void AddPhysicsActorTaint(PhysicsActor prim); + /// + /// Perform a simulation of the current physics scene over the given timestep. + /// + /// + /// The number of frames simulated over that period. public abstract float Simulate(float timeStep); + /// + /// Get statistics about this scene. + /// + /// This facility is currently experimental and subject to change. + /// + /// A dictionary where the key is the statistic name. If no statistics are supplied then returns null. + /// + public virtual Dictionary GetStats() { return null; } + public abstract void GetResults(); public abstract void SetTerrain(float[] heightMap); diff --git a/OpenSim/Region/Physics/OdePlugin/OdeScene.cs b/OpenSim/Region/Physics/OdePlugin/OdeScene.cs index 409b27be02..fa659453c1 100644 --- a/OpenSim/Region/Physics/OdePlugin/OdeScene.cs +++ b/OpenSim/Region/Physics/OdePlugin/OdeScene.cs @@ -131,6 +131,41 @@ namespace OpenSim.Region.Physics.OdePlugin /// internal static Object UniversalColliderSyncObject = new Object(); + /// + /// Is stats collecting enabled for this ODE scene? + /// + public bool CollectStats { get; set; } + + /// + /// Statistics for this scene. + /// + private Dictionary m_stats = new Dictionary(); + + /// + /// Stat name for recording the number of milliseconds that ODE spends in native collision code. + /// + public const string ODENativeCollisionFrameMsStatName = "ODENativeCollisionFrameMS"; + + /// + /// Used to hold tick numbers for stat collection purposes. + /// + private int m_nativeCollisionTickRecorder; + + /// + /// A messy way to tell if we need to avoid adding a collision time because this was already done in the callback. + /// + private bool m_inCollisionTiming; + + /// + /// Used in calculating physics frame time dilation + /// + private int tickCountFrameRun; + + /// + /// Used in calculating physics frame time dilation + /// + private int latertickcount; + private Random fluidRandomizer = new Random(Environment.TickCount); private const uint m_regionWidth = Constants.RegionSize; @@ -345,9 +380,6 @@ namespace OpenSim.Region.Physics.OdePlugin private OdePrim cp1; private OdeCharacter cc2; private OdePrim cp2; - private int tickCountFrameRun; - - private int latertickcount=0; //private int cStartStop = 0; //private string cDictKey = ""; @@ -440,6 +472,8 @@ namespace OpenSim.Region.Physics.OdePlugin // Initialize the mesh plugin public override void Initialise(IMesher meshmerizer, IConfigSource config) { + m_stats[ODENativeCollisionFrameMsStatName] = 0; + mesher = meshmerizer; m_config = config; // Defaults @@ -464,6 +498,8 @@ namespace OpenSim.Region.Physics.OdePlugin IConfig physicsconfig = m_config.Configs["ODEPhysicsSettings"]; if (physicsconfig != null) { + CollectStats = physicsconfig.GetBoolean("collect_stats", false); + gravityx = physicsconfig.GetFloat("world_gravityx", 0f); gravityy = physicsconfig.GetFloat("world_gravityy", 0f); gravityz = physicsconfig.GetFloat("world_gravityz", -9.8f); @@ -764,6 +800,62 @@ namespace OpenSim.Region.Physics.OdePlugin #region Collision Detection + /// + /// Collides two geometries. + /// + /// + /// + /// /param> + /// + /// + /// + private int CollideGeoms( + IntPtr geom1, IntPtr geom2, int maxContacts, Ode.NET.d.ContactGeom[] contactsArray, int contactGeomSize) + { + int count; + + lock (OdeScene.UniversalColliderSyncObject) + { + // We do this inside the lock so that we don't count any delay in acquiring it + if (CollectStats) + m_nativeCollisionTickRecorder = Util.EnvironmentTickCount(); + + count = d.Collide(geom1, geom2, maxContacts, contactsArray, contactGeomSize); + } + + // We do this outside the lock so that any waiting threads aren't held up, though the effect is probably + // negligable + if (CollectStats) + m_stats[ODENativeCollisionFrameMsStatName] + += Util.EnvironmentTickCountSubtract(m_nativeCollisionTickRecorder); + + return count; + } + + /// + /// Collide two spaces or a space and a geometry. + /// + /// + /// /param> + /// + private void CollideSpaces(IntPtr space1, IntPtr space2, IntPtr data) + { + if (CollectStats) + { + m_inCollisionTiming = true; + m_nativeCollisionTickRecorder = Util.EnvironmentTickCount(); + } + + d.SpaceCollide2(space1, space2, data, nearCallback); + + if (CollectStats && m_inCollisionTiming) + { + m_stats[ODENativeCollisionFrameMsStatName] + += Util.EnvironmentTickCountSubtract(m_nativeCollisionTickRecorder); + m_inCollisionTiming = false; + } + } + /// /// This is our near callback. A geometry is near a body /// @@ -772,6 +864,13 @@ namespace OpenSim.Region.Physics.OdePlugin /// another geometry or space private void near(IntPtr space, IntPtr g1, IntPtr g2) { + if (CollectStats && m_inCollisionTiming) + { + m_stats[ODENativeCollisionFrameMsStatName] + += Util.EnvironmentTickCountSubtract(m_nativeCollisionTickRecorder); + m_inCollisionTiming = false; + } + // m_log.DebugFormat("[PHYSICS]: Colliding {0} and {1} in {2}", g1, g2, space); // no lock here! It's invoked from within Simulate(), which is thread-locked @@ -789,7 +888,7 @@ namespace OpenSim.Region.Physics.OdePlugin // contact points in the space try { - d.SpaceCollide2(g1, g2, IntPtr.Zero, nearCallback); + CollideSpaces(g1, g2, IntPtr.Zero); } catch (AccessViolationException) { @@ -832,6 +931,7 @@ namespace OpenSim.Region.Physics.OdePlugin // Figure out how many contact points we have int count = 0; + try { // Colliding Geom To Geom @@ -843,8 +943,7 @@ namespace OpenSim.Region.Physics.OdePlugin if (b1 != IntPtr.Zero && b2 != IntPtr.Zero && d.AreConnectedExcluding(b1, b2, d.JointType.Contact)) return; - lock (OdeScene.UniversalColliderSyncObject) - count = d.Collide(g1, g2, contacts.Length, contacts, d.ContactGeom.SizeOf); + count = CollideGeoms(g1, g2, contacts.Length, contacts, d.ContactGeom.SizeOf); if (count > contacts.Length) m_log.Error("[ODE SCENE]: Got " + count + " contacts when we asked for a maximum of " + contacts.Length); @@ -1578,7 +1677,7 @@ namespace OpenSim.Region.Physics.OdePlugin // and we'll run it again on all of them. try { - d.SpaceCollide2(space, chr.Shell, IntPtr.Zero, nearCallback); + CollideSpaces(space, chr.Shell, IntPtr.Zero); } catch (AccessViolationException) { @@ -1593,6 +1692,9 @@ namespace OpenSim.Region.Physics.OdePlugin //} } +// if (framecount % 55 == 0) +// m_log.DebugFormat("Processed {0} collisions", _perloopContact.Count); + List removeprims = null; foreach (OdePrim chr in _activeprims) { @@ -1604,7 +1706,7 @@ namespace OpenSim.Region.Physics.OdePlugin { if (space != IntPtr.Zero && chr.prim_geom != IntPtr.Zero && chr.m_taintremove == false) { - d.SpaceCollide2(space, chr.prim_geom, IntPtr.Zero, nearCallback); + CollideSpaces(space, chr.prim_geom, IntPtr.Zero); } else { @@ -2689,7 +2791,7 @@ namespace OpenSim.Region.Physics.OdePlugin /// It calls the methods that report back to the object owners.. (scenepresence, SceneObjectGroup) /// /// - /// + /// The number of frames simulated over that period. public override float Simulate(float timeStep) { if (framecount >= int.MaxValue) @@ -3190,7 +3292,7 @@ namespace OpenSim.Region.Physics.OdePlugin public override bool IsThreaded { // for now we won't be multithreaded - get { return (false); } + get { return false; } } #region ODE Specific Terrain Fixes @@ -3955,5 +4057,22 @@ namespace OpenSim.Region.Physics.OdePlugin ds.SetViewpoint(ref xyz, ref hpr); } #endif + + public override Dictionary GetStats() + { + if (!CollectStats) + return null; + + Dictionary returnStats; + + lock (OdeLock) + { + returnStats = new Dictionary(m_stats); + + m_stats[ODENativeCollisionFrameMsStatName] = 0; + } + + return returnStats; + } } } \ No newline at end of file diff --git a/bin/OpenSimDefaults.ini b/bin/OpenSimDefaults.ini index 6718ccac33..a38a3d00ce 100644 --- a/bin/OpenSimDefaults.ini +++ b/bin/OpenSimDefaults.ini @@ -675,6 +675,25 @@ [ODEPhysicsSettings] + ; ## + ; ## Physics stats settings + ; + + ; If collect_stats is enabled, then extra stat information is collected which is accessible via the MonitorModule + ; (see http://opensimulator.org/wiki/Monitoring_Module for more details). + collect_stats = false + + ; ## + ; ## Physics logging settings - logfiles are saved to *.DIF files + ; ## + + ; default is false + ;physics_logging = true + ;; every n simulation iterations, the physics snapshot file is updated + ;physics_logging_interval = 50 + ;; append to existing physics logfile, or overwrite existing logfiles? + ;physics_logging_append_existing_logfile = true + ;## ;## World Settings ;## @@ -823,17 +842,6 @@ ; number^2 physical level of detail of the sculpt texture. 16x16 - 256 verticies mesh_physical_lod = 16 - ; ## - ; ## Physics logging settings - logfiles are saved to *.DIF files - ; ## - - ; default is false - ;physics_logging = true - ;; every n simulation iterations, the physics snapshot file is updated - ;physics_logging_interval = 50 - ;; append to existing physics logfile, or overwrite existing logfiles? - ;physics_logging_append_existing_logfile = true - ; ## ; ## Joint support ; ##