diff --git a/OpenSim/Region/Application/OpenSim.cs b/OpenSim/Region/Application/OpenSim.cs
index 6796f2bd33..c0913c5296 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() + "" + elemName + ">";
// m_log.DebugFormat("[MONITOR MODULE]: {0} = {1}", elemName, monitor.GetValue());
}
+
+ foreach (KeyValuePair tuple in m_scene.StatsReporter.GetExtraSimStats())
+ {
+ xml += "<" + tuple.Key + ">" + tuple.Value + "" + tuple.Key + ">";
+ }
+
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/SimStatsReporter.cs b/OpenSim/Region/Framework/Scenes/SimStatsReporter.cs
index 5c56264ec4..08d8d7c28f 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);
@@ -100,6 +108,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;
@@ -334,7 +350,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();
}
@@ -487,7 +516,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);
}
@@ -509,5 +541,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 2a6163c0e5..b32cd30bec 100644
--- a/OpenSim/Region/Physics/Manager/PhysicsScene.cs
+++ b/OpenSim/Region/Physics/Manager/PhysicsScene.cs
@@ -192,8 +192,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..04c4722b37 100644
--- a/OpenSim/Region/Physics/OdePlugin/OdeScene.cs
+++ b/OpenSim/Region/Physics/OdePlugin/OdeScene.cs
@@ -30,20 +30,21 @@
using System;
using System.Collections.Generic;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Threading;
-using System.IO;
-using System.Diagnostics;
using log4net;
using Nini.Config;
using Ode.NET;
+using OpenMetaverse;
#if USE_DRAWSTUFF
using Drawstuff.NET;
#endif
using OpenSim.Framework;
using OpenSim.Region.Physics.Manager;
-using OpenMetaverse;
namespace OpenSim.Region.Physics.OdePlugin
{
@@ -54,15 +55,15 @@ namespace OpenSim.Region.Physics.OdePlugin
End = 2
}
- public struct sCollisionData
- {
- public uint ColliderLocalId;
- public uint CollidedWithLocalId;
- public int NumberOfCollisions;
- public int CollisionType;
- public int StatusIndicator;
- public int lastframe;
- }
+// public struct sCollisionData
+// {
+// public uint ColliderLocalId;
+// public uint CollidedWithLocalId;
+// public int NumberOfCollisions;
+// public int CollisionType;
+// public int StatusIndicator;
+// public int lastframe;
+// }
[Flags]
public enum CollisionCategories : int
@@ -131,6 +132,80 @@ 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 the total time spent in ODE frame processing.
+ ///
+ ///
+ /// A sanity check for the main scene loop physics time.
+ ///
+ public const string ODETotalFrameMsStatName = "ODETotalFrameMS";
+
+ ///
+ /// The amount of time spent in native code that actually steps through the simulation.
+ ///
+ public const string ODENativeStepFrameMsStatName = "ODENativeStepFrameMS";
+
+ ///
+ /// Stat name for recording the number of milliseconds that ODE spends in native collision code.
+ ///
+ public const string ODENativeCollisionFrameMsStatName = "ODENativeCollisionFrameMS";
+
+ ///
+ /// Stat name for recording the number of milliseconds that ODE spends in native space collision code.
+ ///
+ public const string ODENativeSpaceCollisionFrameMsStatName = "ODENativeSpaceCollisionFrameMS";
+
+ ///
+ /// Stat name for recording the number of milliseconds that ODE spends in native geom collision code.
+ ///
+ public const string ODENativeGeomCollisionFrameMsStatName = "ODENativeGeomCollisionFrameMS";
+
+ ///
+ /// Stat name for the number of avatar collisions with another entity.
+ ///
+ public const string ODEAvatarContactsStatsName = "ODEAvatarContacts";
+
+ ///
+ /// Stat name for the number of prim collisions with another entity.
+ ///
+ public const string ODEPrimContactsStatName = "ODEPrimContacts";
+
+ ///
+ /// Used to hold tick numbers for stat collection purposes.
+ ///
+ private int m_nativeCollisionStartTick;
+
+ ///
+ /// 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;
+
+ ///
+ /// A temporary holder for the number of avatar collisions in a frame, so we can work out how many object
+ /// collisions occured using the _perloopcontact if stats collection is enabled.
+ ///
+ private int m_tempAvatarCollisionsThisFrame;
+
+ ///
+ /// 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 +420,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 +512,8 @@ namespace OpenSim.Region.Physics.OdePlugin
// Initialize the mesh plugin
public override void Initialise(IMesher meshmerizer, IConfigSource config)
{
+ InitializeExtraStats();
+
mesher = meshmerizer;
m_config = config;
// Defaults
@@ -464,6 +538,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 +840,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_nativeCollisionStartTick = 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[ODENativeGeomCollisionFrameMsStatName]
+ += Util.EnvironmentTickCountSubtract(m_nativeCollisionStartTick);
+
+ 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_nativeCollisionStartTick = Util.EnvironmentTickCount();
+ }
+
+ d.SpaceCollide2(space1, space2, data, nearCallback);
+
+ if (CollectStats && m_inCollisionTiming)
+ {
+ m_stats[ODENativeSpaceCollisionFrameMsStatName]
+ += Util.EnvironmentTickCountSubtract(m_nativeCollisionStartTick);
+ m_inCollisionTiming = false;
+ }
+ }
+
///
/// This is our near callback. A geometry is near a body
///
@@ -772,6 +904,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[ODENativeSpaceCollisionFrameMsStatName]
+ += Util.EnvironmentTickCountSubtract(m_nativeCollisionStartTick);
+ 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 +928,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 +971,7 @@ namespace OpenSim.Region.Physics.OdePlugin
// Figure out how many contact points we have
int count = 0;
+
try
{
// Colliding Geom To Geom
@@ -843,8 +983,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);
@@ -1113,14 +1252,12 @@ namespace OpenSim.Region.Physics.OdePlugin
{
_perloopContact.Add(curContact);
- // If we're colliding against terrain
if (name1 == "Terrain" || name2 == "Terrain")
{
- // If we're moving
if ((p2.PhysicsActorType == (int) ActorTypes.Agent) &&
(Math.Abs(p2.Velocity.X) > 0.01f || Math.Abs(p2.Velocity.Y) > 0.01f))
{
- // Use the movement terrain contact
+ // Avatar is moving on terrain, use the movement terrain contact
AvatarMovementTerrainContact.geom = curContact;
if (m_global_contactcount < maxContactsbeforedeath)
@@ -1133,7 +1270,7 @@ namespace OpenSim.Region.Physics.OdePlugin
{
if (p2.PhysicsActorType == (int)ActorTypes.Agent)
{
- // Use the non moving terrain contact
+ // Avatar is standing on terrain, use the non moving terrain contact
TerrainContact.geom = curContact;
if (m_global_contactcount < maxContactsbeforedeath)
@@ -1228,13 +1365,11 @@ namespace OpenSim.Region.Physics.OdePlugin
}
else
{
- // we're colliding with prim or avatar
- // check if we're moving
if ((p2.PhysicsActorType == (int)ActorTypes.Agent))
{
if ((Math.Abs(p2.Velocity.X) > 0.01f || Math.Abs(p2.Velocity.Y) > 0.01f))
{
- // Use the Movement prim contact
+ // Avatar is moving on a prim, use the Movement prim contact
AvatarMovementprimContact.geom = curContact;
if (m_global_contactcount < maxContactsbeforedeath)
@@ -1245,9 +1380,8 @@ namespace OpenSim.Region.Physics.OdePlugin
}
else
{
- // Use the non movement contact
+ // Avatar is standing still on a prim, use the non movement contact
contact.geom = curContact;
- _perloopContact.Add(curContact);
if (m_global_contactcount < maxContactsbeforedeath)
{
@@ -1355,7 +1489,7 @@ namespace OpenSim.Region.Physics.OdePlugin
break;
}
}
- //m_log.DebugFormat("[Collsion]: Depth {0}", Math.Abs(contact.depth - contactGeom.depth));
+ //m_log.DebugFormat("[Collision]: Depth {0}", Math.Abs(contact.depth - contactGeom.depth));
//m_log.DebugFormat("[Collision]: <{0},{1},{2}>", Math.Abs(contactGeom.normal.X - contact.normal.X), Math.Abs(contactGeom.normal.Y - contact.normal.Y), Math.Abs(contactGeom.normal.Z - contact.normal.Z));
}
}
@@ -1578,7 +1712,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 +1727,12 @@ namespace OpenSim.Region.Physics.OdePlugin
//}
}
+ if (CollectStats)
+ {
+ m_tempAvatarCollisionsThisFrame = _perloopContact.Count;
+ m_stats[ODEAvatarContactsStatsName] += m_tempAvatarCollisionsThisFrame;
+ }
+
List removeprims = null;
foreach (OdePrim chr in _activeprims)
{
@@ -1604,7 +1744,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
{
@@ -1625,6 +1765,9 @@ namespace OpenSim.Region.Physics.OdePlugin
}
}
+ if (CollectStats)
+ m_stats[ODEPrimContactsStatName] += _perloopContact.Count - m_tempAvatarCollisionsThisFrame;
+
if (removeprims != null)
{
foreach (OdePrim chr in removeprims)
@@ -2683,21 +2826,23 @@ namespace OpenSim.Region.Physics.OdePlugin
///
/// This is our main simulate loop
+ ///
+ ///
/// It's thread locked by a Mutex in the scene.
/// It holds Collisions, it instructs ODE to step through the physical reactions
/// It moves the objects around in memory
/// 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)
{
+ int startFrameTick = CollectStats ? Util.EnvironmentTickCount() : 0;
+ int quickStepTick = 0;
+
if (framecount >= int.MaxValue)
framecount = 0;
- //if (m_worldOffset != Vector3.Zero)
- // return 0;
-
framecount++;
float fps = 0;
@@ -2705,7 +2850,7 @@ namespace OpenSim.Region.Physics.OdePlugin
float timeLeft = timeStep;
//m_log.Info(timeStep.ToString());
-// step_time += timeStep;
+// step_time += timeSte
//
// // If We're loaded down by something else,
// // or debugging with the Visual Studio project on pause
@@ -2867,9 +3012,15 @@ namespace OpenSim.Region.Physics.OdePlugin
// "[PHYSICS]: Collision contacts to process this frame = {0}", m_global_contactcount);
m_global_contactcount = 0;
-
+
+ if (CollectStats)
+ quickStepTick = Util.EnvironmentTickCount();
+
d.WorldQuickStep(world, ODE_STEPSIZE);
+ if (CollectStats)
+ m_stats[ODENativeStepFrameMsStatName] += Util.EnvironmentTickCountSubtract(quickStepTick);
+
d.JointGroupEmpty(contactgroup);
}
catch (Exception e)
@@ -2937,7 +3088,7 @@ namespace OpenSim.Region.Physics.OdePlugin
d.WorldExportDIF(world, fname, physics_logging_append_existing_logfile, prefix);
}
- latertickcount = Util.EnvironmentTickCount() - tickCountFrameRun;
+ latertickcount = Util.EnvironmentTickCountSubtract(tickCountFrameRun);
// OpenSimulator above does 10 fps. 10 fps = means that the main thread loop and physics
// has a max of 100 ms to run theoretically.
@@ -2957,6 +3108,9 @@ namespace OpenSim.Region.Physics.OdePlugin
tickCountFrameRun = Util.EnvironmentTickCount();
}
+ if (CollectStats)
+ m_stats[ODETotalFrameMsStatName] += Util.EnvironmentTickCountSubtract(startFrameTick);
+
return fps;
}
@@ -3190,7 +3344,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
@@ -3766,26 +3920,19 @@ namespace OpenSim.Region.Physics.OdePlugin
public override Dictionary GetTopColliders()
{
- Dictionary returncolliders = new Dictionary();
- int cnt = 0;
+ Dictionary topColliders;
+
lock (_prims)
{
- foreach (OdePrim prm in _prims)
- {
- if (prm.CollisionScore > 0)
- {
- returncolliders.Add(prm.LocalID, prm.CollisionScore);
- cnt++;
- prm.CollisionScore = 0f;
- if (cnt > 25)
- {
- break;
- }
- }
- }
+ List orderedPrims = new List(_prims);
+ orderedPrims.OrderByDescending(p => p.CollisionScore).Take(25);
+ topColliders = orderedPrims.ToDictionary(p => p.LocalID, p => p.CollisionScore);
+
+ foreach (OdePrim p in _prims)
+ p.CollisionScore = 0;
}
- return returncolliders;
+ return topColliders;
}
public override bool SupportsRayCast()
@@ -3955,5 +4102,39 @@ 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);
+
+ returnStats[ODENativeCollisionFrameMsStatName]
+ = returnStats[ODENativeSpaceCollisionFrameMsStatName]
+ + returnStats[ODENativeGeomCollisionFrameMsStatName];
+
+ InitializeExtraStats();
+ }
+
+ return returnStats;
+ }
+
+ private void InitializeExtraStats()
+ {
+ // No need to zero since this is calculated by addition
+ // m_stats[ODENativeCollisionFrameMsStatName] = 0;
+
+ m_stats[ODETotalFrameMsStatName] = 0;
+ m_stats[ODENativeStepFrameMsStatName] = 0;
+ m_stats[ODENativeSpaceCollisionFrameMsStatName] = 0;
+ m_stats[ODENativeGeomCollisionFrameMsStatName] = 0;
+ m_stats[ODEAvatarContactsStatsName] = 0;
+ m_stats[ODEPrimContactsStatName] = 0;
+ }
}
}
\ No newline at end of file
diff --git a/bin/OpenSimDefaults.ini b/bin/OpenSimDefaults.ini
index 5da3ba2c9b..27d86e8f8c 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
; ##