From 638c3d25b0787c2fbdabbe80519389ad8ddb944d Mon Sep 17 00:00:00 2001 From: Justin Clark-Casey (justincc) Date: Thu, 8 Aug 2013 00:48:22 +0100 Subject: Remove never implemented stub modules commands (list, load, unload) from back in 2009. "show modules" is the functional console command that will show currently loaded modules. Addresses http://opensimulator.org/mantis/view.php?id=6730 --- OpenSim/Region/Application/OpenSim.cs | 40 ----------------------------------- 1 file changed, 40 deletions(-) diff --git a/OpenSim/Region/Application/OpenSim.cs b/OpenSim/Region/Application/OpenSim.cs index 9dcc8da..58f9368 100644 --- a/OpenSim/Region/Application/OpenSim.cs +++ b/OpenSim/Region/Application/OpenSim.cs @@ -348,18 +348,6 @@ namespace OpenSim m_console.Commands.AddCommand("Regions", false, "delete-region", "delete-region ", "Delete a region from disk", RunCommand); - - m_console.Commands.AddCommand("General", false, "modules list", - "modules list", - "List modules", HandleModules); - - m_console.Commands.AddCommand("General", false, "modules load", - "modules load ", - "Load a module", HandleModules); - - m_console.Commands.AddCommand("General", false, "modules unload", - "modules unload ", - "Unload a module", HandleModules); } protected override void ShutdownSpecific() @@ -557,34 +545,6 @@ namespace OpenSim } /// - /// Load, Unload, and list Region modules in use - /// - /// - /// - private void HandleModules(string module, string[] cmd) - { - List args = new List(cmd); - args.RemoveAt(0); - string[] cmdparams = args.ToArray(); - - if (cmdparams.Length > 0) - { - switch (cmdparams[0].ToLower()) - { - case "list": - //TODO: Convert to new region modules - break; - case "unload": - //TODO: Convert to new region modules - break; - case "load": - //TODO: Convert to new region modules - break; - } - } - } - - /// /// Runs commands issued by the server console from the operator /// /// The first argument of the parameter (the command) -- cgit v1.1 From e4da8d74d8b0e04a5439638c8868367d4b20050f Mon Sep 17 00:00:00 2001 From: Kevin Cozens Date: Mon, 5 Aug 2013 19:28:11 -0400 Subject: Additional regression tests for the location routines in Location.cs --- OpenSim/Framework/Tests/LocationTest.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/OpenSim/Framework/Tests/LocationTest.cs b/OpenSim/Framework/Tests/LocationTest.cs index af5f164..3d5d1d2 100644 --- a/OpenSim/Framework/Tests/LocationTest.cs +++ b/OpenSim/Framework/Tests/LocationTest.cs @@ -55,11 +55,18 @@ namespace OpenSim.Framework.Tests Location TestLocation2 = new Location(1095216660736000); Assert.That(TestLocation1 == TestLocation2); + Assert.That(TestLocation1.X == 255000 && TestLocation1.Y == 256000, "Test xy location doesn't match position in the constructor"); Assert.That(TestLocation2.X == 255000 && TestLocation2.Y == 256000, "Test xy location doesn't match regionhandle provided"); Assert.That(TestLocation2.RegionHandle == 1095216660736000, "Location RegionHandle Property didn't match regionhandle provided in constructor"); + ulong RegionHandle = TestLocation1.RegionHandle; + Assert.That(RegionHandle.Equals(1095216660736000), "Equals(regionhandle) failed to match the position in the constructor"); + + TestLocation2 = new Location(RegionHandle); + Assert.That(TestLocation2.Equals(255000, 256000), "Decoded regionhandle failed to match the original position in the constructor"); + TestLocation1 = new Location(255001, 256001); TestLocation2 = new Location(1095216660736000); -- cgit v1.1 From 43da879ea2123707190875fe2615e01be19ecced Mon Sep 17 00:00:00 2001 From: Kevin Cozens Date: Mon, 5 Aug 2013 19:29:38 -0400 Subject: Added regression tests for the routines related to fake parcel IDs. --- OpenSim/Framework/Tests/UtilTest.cs | 84 +++++++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) diff --git a/OpenSim/Framework/Tests/UtilTest.cs b/OpenSim/Framework/Tests/UtilTest.cs index 11ca068..c83651c 100644 --- a/OpenSim/Framework/Tests/UtilTest.cs +++ b/OpenSim/Framework/Tests/UtilTest.cs @@ -282,5 +282,89 @@ namespace OpenSim.Framework.Tests String.Format("Incorrect InventoryType mapped from Content-Type {0}", invcontenttypes[i])); } } + + [Test] + public void FakeParcelIDTests() + { + byte[] hexBytes8 = { 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10 }; + byte[] hexBytes16 = { + 0xf0, 0xe1, 0xd2, 0xc3, 0xb4, 0xa5, 0x96, 0x87, + 0x77, 0x69, 0x5a, 0x4b, 0x3c, 0x2d, 0x1e, 0x0f }; + UInt64 var64Bit = (UInt64)0xfedcba9876543210; + + //Region handle is for location 255000,256000. + ulong regionHandle1 = 1095216660736000; + uint x1 = 100; + uint y1 = 200; + uint z1 = 22; + ulong regionHandle2; + uint x2, y2, z2; + UUID fakeParcelID1, fakeParcelID2, uuid; + + ulong bigInt64 = Util.BytesToUInt64Big(hexBytes8); + Assert.AreEqual(var64Bit, bigInt64, + "BytesToUint64Bit conversion of 8 bytes to UInt64 failed."); + + //Test building and decoding using some typical input values + fakeParcelID1 = Util.BuildFakeParcelID(regionHandle1, x1, y1); + Util.ParseFakeParcelID(fakeParcelID1, out regionHandle2, out x2, out y2); + Assert.AreEqual(regionHandle1, regionHandle2, + "region handle decoded from FakeParcelID wth X/Y failed."); + Assert.AreEqual(x1, x2, + "X coordinate decoded from FakeParcelID wth X/Y failed."); + Assert.AreEqual(y1, y2, + "Y coordinate decoded from FakeParcelID wth X/Y failed."); + + fakeParcelID1 = Util.BuildFakeParcelID(regionHandle1, x1, y1, z1); + Util.ParseFakeParcelID(fakeParcelID1, out regionHandle2, out x2, out y2, out z2); + Assert.AreEqual(regionHandle1, regionHandle2, + "region handle decoded from FakeParcelID with X/Y/Z failed."); + Assert.AreEqual(x1, x2, + "X coordinate decoded from FakeParcelID with X/Y/Z failed."); + Assert.AreEqual(y1, y2, + "Y coordinate decoded from FakeParcelID with X/Y/Z failed."); + Assert.AreEqual(z1, z2, + "Z coordinate decoded from FakeParcelID with X/Y/Z failed."); + + //Do some more extreme tests to check the encoding and decoding + x1 = 0x55aa; + y1 = 0x9966; + z1 = 0x5a96; + + fakeParcelID1 = Util.BuildFakeParcelID(var64Bit, x1, y1); + Util.ParseFakeParcelID(fakeParcelID1, out regionHandle2, out x2, out y2); + Assert.AreEqual(var64Bit, regionHandle2, + "region handle decoded from FakeParcelID with X/Y/Z failed."); + Assert.AreEqual(x1, x2, + "X coordinate decoded from FakeParcelID with X/Y/Z failed."); + Assert.AreEqual(y1, y2, + "Y coordinate decoded from FakeParcelID with X/Y/Z failed."); + + fakeParcelID1 = Util.BuildFakeParcelID(var64Bit, x1, y1, z1); + Util.ParseFakeParcelID(fakeParcelID1, out regionHandle2, out x2, out y2, out z2); + Assert.AreEqual(var64Bit, regionHandle2, + "region handle decoded from FakeParcelID with X/Y/Z failed."); + Assert.AreEqual(x1, x2, + "X coordinate decoded from FakeParcelID with X/Y/Z failed."); + Assert.AreEqual(y1, y2, + "Y coordinate decoded from FakeParcelID with X/Y/Z failed."); + Assert.AreEqual(z1, z2, + "Z coordinate decoded from FakeParcelID with X/Y/Z failed."); + + + x1 = 64; + y1 = 192; + fakeParcelID1 = Util.BuildFakeParcelID(regionHandle1, x1, y1); + Util.FakeParcelIDToGlobalPosition(fakeParcelID1, out x2, out y2); + Assert.AreEqual(255000+x1, x2, + "Global X coordinate decoded from regionHandle failed."); + Assert.AreEqual(256000+y1, y2, + "Global Y coordinate decoded from regionHandle failed."); + + uuid = new UUID("00dd0700-00d1-0700-3800-000032000000"); + Util.FakeParcelIDToGlobalPosition(uuid, out x2, out y2); +System.Console.WriteLine("uuid: " + uuid); +System.Console.WriteLine("x2/y2: " + x2 + "," + y2); + } } } -- cgit v1.1 From 64216b34a49377f6999f6d2cf624d3c537d3f9d5 Mon Sep 17 00:00:00 2001 From: Kevin Cozens Date: Mon, 5 Aug 2013 19:30:46 -0400 Subject: Fixed error in BuildFakeParcelID() which was detected by regression tests. --- OpenSim/Framework/Util.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/OpenSim/Framework/Util.cs b/OpenSim/Framework/Util.cs index 6a15734..f0e5bc1 100644 --- a/OpenSim/Framework/Util.cs +++ b/OpenSim/Framework/Util.cs @@ -1247,7 +1247,7 @@ namespace OpenSim.Framework byte[] bytes = { (byte)regionHandle, (byte)(regionHandle >> 8), (byte)(regionHandle >> 16), (byte)(regionHandle >> 24), - (byte)(regionHandle >> 32), (byte)(regionHandle >> 40), (byte)(regionHandle >> 48), (byte)(regionHandle << 56), + (byte)(regionHandle >> 32), (byte)(regionHandle >> 40), (byte)(regionHandle >> 48), (byte)(regionHandle >> 56), (byte)x, (byte)(x >> 8), 0, 0, (byte)y, (byte)(y >> 8), 0, 0 }; return new UUID(bytes, 0); @@ -1258,7 +1258,7 @@ namespace OpenSim.Framework byte[] bytes = { (byte)regionHandle, (byte)(regionHandle >> 8), (byte)(regionHandle >> 16), (byte)(regionHandle >> 24), - (byte)(regionHandle >> 32), (byte)(regionHandle >> 40), (byte)(regionHandle >> 48), (byte)(regionHandle << 56), + (byte)(regionHandle >> 32), (byte)(regionHandle >> 40), (byte)(regionHandle >> 48), (byte)(regionHandle >> 56), (byte)x, (byte)(x >> 8), (byte)z, (byte)(z >> 8), (byte)y, (byte)(y >> 8), 0, 0 }; return new UUID(bytes, 0); -- cgit v1.1 From ce1361f2fea9c650cc774941b5cd95b14b8f01f5 Mon Sep 17 00:00:00 2001 From: Justin Clark-Casey (justincc) Date: Thu, 8 Aug 2013 01:07:30 +0100 Subject: minor: Remove console lines at bottom of FakeParcelIDTests() regression test that were accidentally left in --- OpenSim/Framework/Tests/UtilTest.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/OpenSim/Framework/Tests/UtilTest.cs b/OpenSim/Framework/Tests/UtilTest.cs index c83651c..3b7f252 100644 --- a/OpenSim/Framework/Tests/UtilTest.cs +++ b/OpenSim/Framework/Tests/UtilTest.cs @@ -363,8 +363,6 @@ namespace OpenSim.Framework.Tests uuid = new UUID("00dd0700-00d1-0700-3800-000032000000"); Util.FakeParcelIDToGlobalPosition(uuid, out x2, out y2); -System.Console.WriteLine("uuid: " + uuid); -System.Console.WriteLine("x2/y2: " + x2 + "," + y2); } } } -- cgit v1.1 From 99a4a914887c16483074b0145b9b6da765ac024a Mon Sep 17 00:00:00 2001 From: teravus Date: Wed, 7 Aug 2013 21:22:04 -0500 Subject: * This makes in-world terrain editing smoother, even in MegaRegions. This change only affects the editing user's experience. Non-editing users will see nothing different from the current 'slow' result. See comments for the thought process and how the issues surrounding terrain editing, cache, bandwidth, threading, terrain patch reliability and throttling were balanced. --- .../Region/ClientStack/Linden/UDP/LLClientView.cs | 33 ++++++++++++++++++++-- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs b/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs index 8b2440a..0dbce2f 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs @@ -327,7 +327,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP private PriorityQueue m_entityProps; private Prioritizer m_prioritizer; private bool m_disableFacelights = false; - + private volatile bool m_justEditedTerrain = false; /// /// List used in construction of data blocks for an object update packet. This is to stop us having to /// continually recreate it. @@ -1239,9 +1239,32 @@ namespace OpenSim.Region.ClientStack.LindenUDP LLHeightFieldMoronize(map); LayerDataPacket layerpack = TerrainCompressor.CreateLandPacket(heightmap, patches); - layerpack.Header.Reliable = true; + + // When a user edits the terrain, so much data is sent, the data queues up fast and presents a sub optimal editing experience. + // To alleviate this issue, when the user edits the terrain, we start skipping the queues until they're done editing the terrain. + // We also make them unreliable because it's extremely likely that multiple packets will be sent for a terrain patch area + // invalidating previous packets for that area. - OutPacket(layerpack, ThrottleOutPacketType.Land); + // It's possible for an editing user to flood themselves with edited packets but the majority of use cases are such that only a + // tiny percentage of users will be editing the terrain. Other, non-editing users will see the edits much slower. + + // One last note on this topic, by the time users are going to be editing the terrain, it's extremely likely that the sim will + // have rezzed already and therefore this is not likely going to cause any additional issues with lost packets, objects or terrain + // patches. + + // m_justEditedTerrain is volatile, so test once and duplicate two affected statements so we only have one cache miss. + if (m_justEditedTerrain) + { + layerpack.Header.Reliable = false; + OutPacket(layerpack, + ThrottleOutPacketType.Unknown ); + } + else + { + layerpack.Header.Reliable = true; + OutPacket(layerpack, + ThrottleOutPacketType.Land); + } } catch (Exception e) { @@ -6263,6 +6286,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP //m_log.Info("[LAND]: LAND:" + modify.ToString()); if (modify.ParcelData.Length > 0) { + // Note: the ModifyTerrain event handler sends out updated packets before the end of this event. Therefore, + // a simple boolean value should work and perhaps queue up just a few terrain patch packets at the end of the edit. + m_justEditedTerrain = true; // Prevent terrain packet (Land layer) from being queued, make it unreliable if (OnModifyTerrain != null) { for (int i = 0; i < modify.ParcelData.Length; i++) @@ -6278,6 +6304,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP } } } + m_justEditedTerrain = false; // Queue terrain packet (Land layer) if necessary, make it reliable again } return true; -- cgit v1.1 From 4e86674a3a671561c3a9c85925308f2004fcc922 Mon Sep 17 00:00:00 2001 From: teravus Date: Wed, 7 Aug 2013 23:33:23 -0500 Subject: * Added set water height [] [] console command following the set terrain heights console command as an example. --- .../World/Estate/EstateManagementCommands.cs | 31 +++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/OpenSim/Region/CoreModules/World/Estate/EstateManagementCommands.cs b/OpenSim/Region/CoreModules/World/Estate/EstateManagementCommands.cs index 4d49794..173b603 100644 --- a/OpenSim/Region/CoreModules/World/Estate/EstateManagementCommands.cs +++ b/OpenSim/Region/CoreModules/World/Estate/EstateManagementCommands.cs @@ -76,6 +76,13 @@ namespace OpenSim.Region.CoreModules.World.Estate " that coordinate. Corner # SW = 0, NW = 1, SE = 2, NE = 3, all corners = -1.", consoleSetTerrainHeights); + m_module.Scene.AddCommand("Regions", m_module, "set water height", + "set water height [] []", + "Sets the water height in meters. If and are specified, it will only set it on regions with a matching coordinate. " + + "Specify -1 in or to wildcard that coordinate.", + consoleSetWaterHeight); + + m_module.Scene.AddCommand( "Estates", m_module, "estate show", "estate show", "Shows all estates on the simulator.", ShowEstatesCommand); } @@ -121,7 +128,29 @@ namespace OpenSim.Region.CoreModules.World.Estate } } } - + protected void consoleSetWaterHeight(string module, string[] args) + { + string heightstring = args[3]; + + int x = (args.Length > 4 ? int.Parse(args[4]) : -1); + int y = (args.Length > 5 ? int.Parse(args[5]) : -1); + + if (x == -1 || m_module.Scene.RegionInfo.RegionLocX == x) + { + if (y == -1 || m_module.Scene.RegionInfo.RegionLocY == y) + { + double selectedheight = double.Parse(heightstring); + + m_log.Debug("[ESTATEMODULE]: Setting water height in " + m_module.Scene.RegionInfo.RegionName + " to " + + string.Format(" {0}", selectedheight)); + m_module.Scene.RegionInfo.RegionSettings.WaterHeight = selectedheight; + + m_module.Scene.RegionInfo.RegionSettings.Save(); + m_module.TriggerRegionInfoChange(); + m_module.sendRegionHandshakeToAll(); + } + } + } protected void consoleSetTerrainHeights(string module, string[] args) { string num = args[3]; -- cgit v1.1 From 50c163ae6ca734610694f4edcc109ff0bdc65ba1 Mon Sep 17 00:00:00 2001 From: Robert Adams Date: Tue, 6 Aug 2013 08:21:16 -0700 Subject: Add a JSON web fetch of the statististics managed by StatsManager. Disabled by default. Enable by setting [Startup]ManagedStatsRemoteFetchURI="Something" and thereafter "http://ServerHTTPPort/Something/" will return all the managed stats (equivilent to "show stats all" console command). Accepts queries "cat=", "cont=" and "stat=" to specify statistic category, container and statistic names. The special name "all" is the default and returns all values in that group. --- OpenSim/Framework/Monitoring/StatsManager.cs | 31 ++++++++++++++++++++++++++++ OpenSim/Region/Application/OpenSim.cs | 7 +++++++ OpenSim/Region/Application/OpenSimBase.cs | 2 ++ 3 files changed, 40 insertions(+) diff --git a/OpenSim/Framework/Monitoring/StatsManager.cs b/OpenSim/Framework/Monitoring/StatsManager.cs index c8e838c..23c6f18 100644 --- a/OpenSim/Framework/Monitoring/StatsManager.cs +++ b/OpenSim/Framework/Monitoring/StatsManager.cs @@ -26,10 +26,12 @@ */ using System; +using System.Collections; using System.Collections.Generic; using System.Linq; using System.Text; +using OpenSim.Framework; using OpenMetaverse.StructuredData; namespace OpenSim.Framework.Monitoring @@ -262,6 +264,35 @@ namespace OpenSim.Framework.Monitoring return map; } + public static Hashtable HandleStatsRequest(Hashtable request) + { + Hashtable responsedata = new Hashtable(); + string regpath = request["uri"].ToString(); + int response_code = 200; + string contenttype = "text/json"; + + string pCategoryName = StatsManager.AllSubCommand; + string pContainerName = StatsManager.AllSubCommand; + string pStatName = StatsManager.AllSubCommand; + + if (request.ContainsKey("cat")) pCategoryName = request["cat"].ToString(); + if (request.ContainsKey("cont")) pContainerName = request["cat"].ToString(); + if (request.ContainsKey("stat")) pStatName = request["cat"].ToString(); + + string strOut = StatsManager.GetStatsAsOSDMap(pCategoryName, pContainerName, pStatName).ToString(); + + // m_log.DebugFormat("{0} StatFetch: uri={1}, cat={2}, cont={3}, stat={4}, resp={5}", + // LogHeader, regpath, pCategoryName, pContainerName, pStatName, strOut); + + responsedata["int_response_code"] = response_code; + responsedata["content_type"] = contenttype; + responsedata["keepalive"] = false; + responsedata["str_response_string"] = strOut; + responsedata["access_control_allow_origin"] = "*"; + + return responsedata; + } + // /// // /// Start collecting statistics related to assets. // /// Should only be called once. diff --git a/OpenSim/Region/Application/OpenSim.cs b/OpenSim/Region/Application/OpenSim.cs index 58f9368..13fdb3b 100644 --- a/OpenSim/Region/Application/OpenSim.cs +++ b/OpenSim/Region/Application/OpenSim.cs @@ -172,6 +172,13 @@ namespace OpenSim if (userStatsURI != String.Empty) MainServer.Instance.AddStreamHandler(new OpenSim.UXSimStatusHandler(this)); + if (managedStatsURI != String.Empty) + { + string urlBase = String.Format("/{0}/", managedStatsURI); + MainServer.Instance.AddHTTPHandler(urlBase, StatsManager.HandleStatsRequest); + m_log.WarnFormat("[OPENSIM] Enabling remote managed stats fetch. URL = {0}", urlBase); + } + if (m_console is RemoteConsole) { if (m_consolePort == 0) diff --git a/OpenSim/Region/Application/OpenSimBase.cs b/OpenSim/Region/Application/OpenSimBase.cs index f0c088a..b032e7f 100644 --- a/OpenSim/Region/Application/OpenSimBase.cs +++ b/OpenSim/Region/Application/OpenSimBase.cs @@ -75,6 +75,7 @@ namespace OpenSim protected int proxyOffset = 0; public string userStatsURI = String.Empty; + public string managedStatsURI = String.Empty; protected bool m_autoCreateClientStack = true; @@ -188,6 +189,7 @@ namespace OpenSim CreatePIDFile(pidFile); userStatsURI = startupConfig.GetString("Stats_URI", String.Empty); + managedStatsURI = startupConfig.GetString("ManagedStatsRemoteFetchURI", String.Empty); } // Load the simulation data service -- cgit v1.1 From c67c55e0fcd93e5b68e61e5f1bc4341af48568d3 Mon Sep 17 00:00:00 2001 From: Robert Adams Date: Thu, 8 Aug 2013 08:56:22 -0700 Subject: Better error reporting when registering LSL function extensions (comms module). For unknown reasons, a dynamic function signature cannot have more than 5 parameters. Error message now tells you this fact so you can curse MS and then go change your function definitions. --- .../ScriptModuleComms/ScriptModuleCommsModule.cs | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/OpenSim/Region/CoreModules/Scripting/ScriptModuleComms/ScriptModuleCommsModule.cs b/OpenSim/Region/CoreModules/Scripting/ScriptModuleComms/ScriptModuleCommsModule.cs index a515346..6da2222 100644 --- a/OpenSim/Region/CoreModules/Scripting/ScriptModuleComms/ScriptModuleCommsModule.cs +++ b/OpenSim/Region/CoreModules/Scripting/ScriptModuleComms/ScriptModuleCommsModule.cs @@ -45,6 +45,7 @@ namespace OpenSim.Region.CoreModules.Scripting.ScriptModuleComms { private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + private static string LogHeader = "[MODULE COMMS]"; private Dictionary m_constants = new Dictionary(); @@ -148,7 +149,7 @@ namespace OpenSim.Region.CoreModules.Scripting.ScriptModuleComms MethodInfo mi = GetMethodInfoFromType(target.GetType(), meth, true); if (mi == null) { - m_log.WarnFormat("[MODULE COMMANDS] Failed to register method {0}", meth); + m_log.WarnFormat("{0} Failed to register method {1}", LogHeader, meth); return; } @@ -165,7 +166,7 @@ namespace OpenSim.Region.CoreModules.Scripting.ScriptModuleComms { // m_log.DebugFormat("[MODULE COMMANDS] Register method {0} from type {1}", mi.Name, (target is Type) ? ((Type)target).Name : target.GetType().Name); - Type delegateType; + Type delegateType = typeof(void); List typeArgs = mi.GetParameters() .Select(p => p.ParameterType) .ToList(); @@ -176,8 +177,16 @@ namespace OpenSim.Region.CoreModules.Scripting.ScriptModuleComms } else { - typeArgs.Add(mi.ReturnType); - delegateType = Expression.GetFuncType(typeArgs.ToArray()); + try + { + typeArgs.Add(mi.ReturnType); + delegateType = Expression.GetFuncType(typeArgs.ToArray()); + } + catch (Exception e) + { + m_log.ErrorFormat("{0} Failed to create function signature. Most likely more than 5 parameters. Method={1}. Error={2}", + LogHeader, mi.Name, e); + } } Delegate fcall; -- cgit v1.1 From d9bd6e6b5be3100141a3b1202f859c65a302d4ee Mon Sep 17 00:00:00 2001 From: Robert Adams Date: Thu, 8 Aug 2013 09:41:11 -0700 Subject: Add parameter and explanation of ManagedStats return to OpenSimDefaults.ini. Add 'callback' query parameter to managed stats return to return function form of JSON data. --- OpenSim/Framework/Monitoring/StatsManager.cs | 6 ++++++ OpenSim/Region/Application/OpenSim.cs | 4 ++-- bin/OpenSimDefaults.ini | 7 +++++++ 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/OpenSim/Framework/Monitoring/StatsManager.cs b/OpenSim/Framework/Monitoring/StatsManager.cs index 23c6f18..7cf1fa7 100644 --- a/OpenSim/Framework/Monitoring/StatsManager.cs +++ b/OpenSim/Framework/Monitoring/StatsManager.cs @@ -281,6 +281,12 @@ namespace OpenSim.Framework.Monitoring string strOut = StatsManager.GetStatsAsOSDMap(pCategoryName, pContainerName, pStatName).ToString(); + // If requestor wants it as a callback function, build response as a function rather than just the JSON string. + if (request.ContainsKey("callback")) + { + strOut = request["callback"].ToString() + "(" + strOut + ");"; + } + // m_log.DebugFormat("{0} StatFetch: uri={1}, cat={2}, cont={3}, stat={4}, resp={5}", // LogHeader, regpath, pCategoryName, pContainerName, pStatName, strOut); diff --git a/OpenSim/Region/Application/OpenSim.cs b/OpenSim/Region/Application/OpenSim.cs index 13fdb3b..1cdd868 100644 --- a/OpenSim/Region/Application/OpenSim.cs +++ b/OpenSim/Region/Application/OpenSim.cs @@ -175,8 +175,8 @@ namespace OpenSim if (managedStatsURI != String.Empty) { string urlBase = String.Format("/{0}/", managedStatsURI); - MainServer.Instance.AddHTTPHandler(urlBase, StatsManager.HandleStatsRequest); - m_log.WarnFormat("[OPENSIM] Enabling remote managed stats fetch. URL = {0}", urlBase); + MainServer.Instance.AddHTTPHandler(urlBase, StatsManager.HandleStatsRequest); + m_log.InfoFormat("[OPENSIM] Enabling remote managed stats fetch. URL = {0}", urlBase); } if (m_console is RemoteConsole) diff --git a/bin/OpenSimDefaults.ini b/bin/OpenSimDefaults.ini index d5d29ec..0a85085 100644 --- a/bin/OpenSimDefaults.ini +++ b/bin/OpenSimDefaults.ini @@ -264,8 +264,15 @@ ; Simulator Stats URI ; Enable JSON simulator data by setting a URI name (case sensitive) + ; Returns regular sim stats (SimFPS, ...) ; Stats_URI = "jsonSimStats" + ; Simulator StatsManager URI + ; Enable fetch of StatsManager registered stats. Fetch is query which can optionally + ; specify category, container and stat to fetch. If not selected, returns all of that type. + ; http://simulatorHTTPport/ManagedStats/?cat=Category&cont=Container&stat=Statistic + ; ManagedStatsRemoteFetchURI = "ManagedStats" + ; Make OpenSim start all regions woth logins disabled. They will need ; to be enabled from the console if this is set ; StartDisabled = false -- cgit v1.1 From 9fc97cbbf77f269d69aaa6234dd6e12ebf3b9563 Mon Sep 17 00:00:00 2001 From: Dan Lake Date: Thu, 8 Aug 2013 12:44:03 -0700 Subject: Make m_originRegionID in ScenePresence public to allow DSG module to work for now. Once the code churn on teleport ends, I can find a better solution --- OpenSim/Region/Framework/Scenes/ScenePresence.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/OpenSim/Region/Framework/Scenes/ScenePresence.cs b/OpenSim/Region/Framework/Scenes/ScenePresence.cs index 7fd1302..1b8c276 100644 --- a/OpenSim/Region/Framework/Scenes/ScenePresence.cs +++ b/OpenSim/Region/Framework/Scenes/ScenePresence.cs @@ -290,9 +290,9 @@ namespace OpenSim.Region.Framework.Scenes /// /// In the V1 teleport protocol, the destination simulator sends ReleaseAgent to this address. /// - string m_callbackURI; + private string m_callbackURI; - UUID m_originRegionID; + public UUID m_originRegionID; /// /// Used by the entity transfer module to signal when the presence should not be closed because a subsequent -- cgit v1.1 From 6410a25cefcb1e7f87b76f273f8c6569fbe17670 Mon Sep 17 00:00:00 2001 From: Robert Adams Date: Thu, 8 Aug 2013 13:53:12 -0700 Subject: BulletSim: adjust avatar position when the avatar's size is changed. This fixes the problem of avatars bouncing when logged in. Added a little height to the avatar height fudges to eliminate a problem of feet being in the ground a bit. --- OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs | 12 ++++++++++-- OpenSim/Region/Physics/BulletSPlugin/BSParam.cs | 4 ++-- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs b/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs index 58a417e..9af3dce 100644 --- a/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs @@ -96,8 +96,8 @@ public sealed class BSCharacter : BSPhysObject m_moveActor = new BSActorAvatarMove(PhysScene, this, AvatarMoveActorName); PhysicalActors.Add(AvatarMoveActorName, m_moveActor); - DetailLog("{0},BSCharacter.create,call,size={1},scale={2},density={3},volume={4},mass={5}", - LocalID, _size, Scale, Density, _avatarVolume, RawMass); + DetailLog("{0},BSCharacter.create,call,size={1},scale={2},density={3},volume={4},mass={5},pos={6}", + LocalID, _size, Scale, Density, _avatarVolume, RawMass, pos); // do actual creation in taint time PhysScene.TaintedObject("BSCharacter.create", delegate() @@ -190,6 +190,10 @@ public sealed class BSCharacter : BSPhysObject } set { + // This is how much the avatar size is changing. Positive means getting bigger. + // The avatar altitude must be adjusted for this change. + float heightChange = value.Z - _size.Z; + _size = value; // Old versions of ScenePresence passed only the height. If width and/or depth are zero, // replace with the default values. @@ -207,6 +211,10 @@ public sealed class BSCharacter : BSPhysObject { PhysScene.PE.SetLocalScaling(PhysShape.physShapeInfo, Scale); UpdatePhysicalMassProperties(RawMass, true); + + // Adjust the avatar's position to account for the increase/decrease in size + ForcePosition = new OMV.Vector3(RawPosition.X, RawPosition.Y, RawPosition.Z + heightChange / 2f); + // Make sure this change appears as a property update event PhysScene.PE.PushUpdate(PhysBody); } diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSParam.cs b/OpenSim/Region/Physics/BulletSPlugin/BSParam.cs index 4520171..fcb892a 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSParam.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSParam.cs @@ -570,9 +570,9 @@ public static class BSParam new ParameterDefn("AvatarHeightLowFudge", "A fudge factor to make small avatars stand on the ground", -0.2f ), new ParameterDefn("AvatarHeightMidFudge", "A fudge distance to adjust average sized avatars to be standing on ground", - 0.1f ), + 0.2f ), new ParameterDefn("AvatarHeightHighFudge", "A fudge factor to make tall avatars stand on the ground", - 0.1f ), + 0.2f ), new ParameterDefn("AvatarContactProcessingThreshold", "Distance from capsule to check for collisions", 0.1f ), new ParameterDefn("AvatarBelowGroundUpCorrectionMeters", "Meters to move avatar up if it seems to be below ground", -- cgit v1.1 From b1c26a56b3d615f3709363e3a2f91b5423f5891f Mon Sep 17 00:00:00 2001 From: Justin Clark-Casey (justincc) Date: Thu, 8 Aug 2013 23:29:30 +0100 Subject: Fix an issue where under teleport v2 protocol, teleporting from regions in an line from A->B->C would not close region A when reaching C The root cause was that v2 was only closing neighbour agents if the root connection also needed a close. However, fixing this requires the neighbour regions also detect when they should not close due to re-teleports re-establishing the child connection. This involves restructuring the code to introduce a scene presence state machine that can serialize the different add and remove client calls that are now possible with the late close of the This commit appears to fix these issues and improve teleport, but still has holes on at least quick reteleporting (and possibly occasionally on ordinary teleports). Also, has not been completely tested yet in scenarios where regions are running on different simulators --- .../Caps/EventQueue/Tests/EventQueueTests.cs | 2 +- .../Region/ClientStack/Linden/UDP/LLClientView.cs | 6 + .../Region/ClientStack/Linden/UDP/LLUDPServer.cs | 6 +- .../Linden/UDP/Tests/BasicCircuitTests.cs | 2 +- .../EntityTransfer/EntityTransferModule.cs | 48 ++-- .../World/Estate/EstateManagementModule.cs | 6 +- OpenSim/Region/Framework/Scenes/Scene.cs | 308 +++++++++++++-------- OpenSim/Region/Framework/Scenes/ScenePresence.cs | 36 ++- 8 files changed, 267 insertions(+), 147 deletions(-) diff --git a/OpenSim/Region/ClientStack/Linden/Caps/EventQueue/Tests/EventQueueTests.cs b/OpenSim/Region/ClientStack/Linden/Caps/EventQueue/Tests/EventQueueTests.cs index 141af8a..626932f 100644 --- a/OpenSim/Region/ClientStack/Linden/Caps/EventQueue/Tests/EventQueueTests.cs +++ b/OpenSim/Region/ClientStack/Linden/Caps/EventQueue/Tests/EventQueueTests.cs @@ -91,7 +91,7 @@ namespace OpenSim.Region.ClientStack.Linden.Tests public void RemoveForClient() { TestHelpers.InMethod(); -// log4net.Config.XmlConfigurator.Configure(); +// TestHelpers.EnableLogging(); UUID spId = TestHelpers.ParseTail(0x1); diff --git a/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs b/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs index 8b2440a..e775a81 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs @@ -512,7 +512,13 @@ namespace OpenSim.Region.ClientStack.LindenUDP // We still perform a force close inside the sync lock since this is intended to attempt close where // there is some unidentified connection problem, not where we have issues due to deadlock if (!IsActive && !force) + { + m_log.DebugFormat( + "[CLIENT]: Not attempting to close inactive client {0} in {1} since force flag is not set", + Name, m_scene.Name); + return; + } IsActive = false; CloseWithoutChecks(); diff --git a/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs b/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs index 5c38399..de2f9d4 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs @@ -1799,9 +1799,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP if (!client.SceneAgent.IsChildAgent) client.Kick("Simulator logged you out due to connection timeout."); - - client.CloseWithoutChecks(); } + + m_scene.IncomingCloseAgent(client.AgentId, true); } private void IncomingPacketHandler() @@ -2142,7 +2142,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP if (!client.IsLoggingOut) { client.IsLoggingOut = true; - client.Close(); + m_scene.IncomingCloseAgent(client.AgentId, false); } } } diff --git a/OpenSim/Region/ClientStack/Linden/UDP/Tests/BasicCircuitTests.cs b/OpenSim/Region/ClientStack/Linden/UDP/Tests/BasicCircuitTests.cs index b47ff54..9700224 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/Tests/BasicCircuitTests.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/Tests/BasicCircuitTests.cs @@ -200,7 +200,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP.Tests public void TestLogoutClientDueToAck() { TestHelpers.InMethod(); -// TestHelpers.EnableLogging(); + TestHelpers.EnableLogging(); IniConfigSource ics = new IniConfigSource(); IConfig config = ics.AddConfig("ClientStack.LindenUDP"); diff --git a/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferModule.cs b/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferModule.cs index 01ef710..2f74253 100644 --- a/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferModule.cs +++ b/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferModule.cs @@ -1064,8 +1064,12 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer // Now let's make it officially a child agent sp.MakeChildAgent(); - // Finally, let's close this previously-known-as-root agent, when the jump is outside the view zone + // May still need to signal neighbours whether child agents may need closing irrespective of whether this + // one needed closing. Neighbour regions also contain logic to prevent a close if a subsequent move or + // teleport re-established the child connection. + sp.CloseChildAgents(newRegionX, newRegionY); + // Finally, let's close this previously-known-as-root agent, when the jump is outside the view zone if (NeedsClosing(sp.DrawDistance, oldRegionX, newRegionX, oldRegionY, newRegionY, reg)) { sp.DoNotCloseAfterTeleport = false; @@ -1081,14 +1085,12 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer if (!sp.DoNotCloseAfterTeleport) { // OK, it got this agent. Let's close everything - m_log.DebugFormat("[ENTITY TRANSFER MODULE]: Closing in agent {0} in region {1}", sp.Name, Scene.Name); - sp.CloseChildAgents(newRegionX, newRegionY); + m_log.DebugFormat("[ENTITY TRANSFER MODULE]: Closing agent {0} in {1}", sp.Name, Scene.Name); sp.Scene.IncomingCloseAgent(sp.UUID, false); - } else { - m_log.DebugFormat("[ENTITY TRANSFER MODULE]: Not closing agent {0}, user is back in {0}", sp.Name, Scene.Name); + m_log.DebugFormat("[ENTITY TRANSFER MODULE]: Not closing agent {0}, user is back in {1}", sp.Name, Scene.Name); sp.DoNotCloseAfterTeleport = false; } } @@ -1863,10 +1865,10 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer List newRegions = NewNeighbours(neighbourHandles, previousRegionNeighbourHandles); List oldRegions = OldNeighbours(neighbourHandles, previousRegionNeighbourHandles); - //Dump("Current Neighbors", neighbourHandles); - //Dump("Previous Neighbours", previousRegionNeighbourHandles); - //Dump("New Neighbours", newRegions); - //Dump("Old Neighbours", oldRegions); + Dump("Current Neighbors", neighbourHandles); + Dump("Previous Neighbours", previousRegionNeighbourHandles); + Dump("New Neighbours", newRegions); + Dump("Old Neighbours", oldRegions); /// Update the scene presence's known regions here on this region sp.DropOldNeighbours(oldRegions); @@ -1874,8 +1876,7 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer /// Collect as many seeds as possible Dictionary seeds; if (sp.Scene.CapsModule != null) - seeds - = new Dictionary(sp.Scene.CapsModule.GetChildrenSeeds(sp.UUID)); + seeds = new Dictionary(sp.Scene.CapsModule.GetChildrenSeeds(sp.UUID)); else seeds = new Dictionary(); @@ -1945,6 +1946,7 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer newAgent = true; else newAgent = false; +// continue; if (neighbour.RegionHandle != sp.Scene.RegionInfo.RegionHandle) { @@ -2178,18 +2180,18 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer return handles; } -// private void Dump(string msg, List handles) -// { -// m_log.InfoFormat("-------------- HANDLE DUMP ({0}) ---------", msg); -// foreach (ulong handle in handles) -// { -// uint x, y; -// Utils.LongToUInts(handle, out x, out y); -// x = x / Constants.RegionSize; -// y = y / Constants.RegionSize; -// m_log.InfoFormat("({0}, {1})", x, y); -// } -// } + private void Dump(string msg, List handles) + { + m_log.InfoFormat("-------------- HANDLE DUMP ({0}) ---------", msg); + foreach (ulong handle in handles) + { + uint x, y; + Utils.LongToUInts(handle, out x, out y); + x = x / Constants.RegionSize; + y = y / Constants.RegionSize; + m_log.InfoFormat("({0}, {1})", x, y); + } + } #endregion diff --git a/OpenSim/Region/CoreModules/World/Estate/EstateManagementModule.cs b/OpenSim/Region/CoreModules/World/Estate/EstateManagementModule.cs index 121b2aa..31547a6 100644 --- a/OpenSim/Region/CoreModules/World/Estate/EstateManagementModule.cs +++ b/OpenSim/Region/CoreModules/World/Estate/EstateManagementModule.cs @@ -562,7 +562,7 @@ namespace OpenSim.Region.CoreModules.World.Estate if (!Scene.TeleportClientHome(user, s.ControllingClient)) { s.ControllingClient.Kick("Your access to the region was revoked and TP home failed - you have been logged out."); - s.ControllingClient.Close(); + Scene.IncomingCloseAgent(s.UUID, false); } } } @@ -797,7 +797,7 @@ namespace OpenSim.Region.CoreModules.World.Estate if (!Scene.TeleportClientHome(prey, s.ControllingClient)) { s.ControllingClient.Kick("You were teleported home by the region owner, but the TP failed - you have been logged out."); - s.ControllingClient.Close(); + Scene.IncomingCloseAgent(s.UUID, false); } } } @@ -820,7 +820,7 @@ namespace OpenSim.Region.CoreModules.World.Estate if (!Scene.TeleportClientHome(p.UUID, p.ControllingClient)) { p.ControllingClient.Kick("You were teleported home by the region owner, but the TP failed - you have been logged out."); - p.ControllingClient.Close(); + Scene.IncomingCloseAgent(p.UUID, false); } } } diff --git a/OpenSim/Region/Framework/Scenes/Scene.cs b/OpenSim/Region/Framework/Scenes/Scene.cs index 56cd57e..5f10869 100644 --- a/OpenSim/Region/Framework/Scenes/Scene.cs +++ b/OpenSim/Region/Framework/Scenes/Scene.cs @@ -151,7 +151,7 @@ namespace OpenSim.Region.Framework.Scenes public SynchronizeSceneHandler SynchronizeScene; /// - /// Used to prevent simultaneous calls to RemoveClient() for the same agent from interfering with each other. + /// Used to prevent simultaneous calls to code that adds and removes agents. /// private object m_removeClientLock = new object(); @@ -1312,7 +1312,7 @@ namespace OpenSim.Region.Framework.Scenes Thread.Sleep(500); // Stop all client threads. - ForEachScenePresence(delegate(ScenePresence avatar) { avatar.ControllingClient.Close(); }); + ForEachScenePresence(delegate(ScenePresence avatar) { IncomingCloseAgent(avatar.UUID, false); }); m_log.Debug("[SCENE]: Persisting changed objects"); EventManager.TriggerSceneShuttingDown(this); @@ -2972,7 +2972,7 @@ namespace OpenSim.Region.Framework.Scenes { PresenceService.LogoutAgent(sp.ControllingClient.SessionId); - sp.ControllingClient.Close(); + IncomingCloseAgent(sp.UUID, false); } else { @@ -3384,47 +3384,48 @@ namespace OpenSim.Region.Framework.Scenes public override void RemoveClient(UUID agentID, bool closeChildAgents) { -// CheckHeartbeat(); - bool isChildAgent = false; - AgentCircuitData acd; + AgentCircuitData acd = m_authenticateHandler.GetAgentCircuitData(agentID); - lock (m_removeClientLock) + // Shouldn't be necessary since RemoveClient() is currently only called by IClientAPI.Close() which + // in turn is only called by Scene.IncomingCloseAgent() which checks whether the presence exists or not + // However, will keep for now just in case. + if (acd == null) { - acd = m_authenticateHandler.GetAgentCircuitData(agentID); + m_log.ErrorFormat( + "[SCENE]: No agent circuit found for {0} in {1}, aborting Scene.RemoveClient", agentID, Name); - if (acd == null) - { - m_log.ErrorFormat("[SCENE]: No agent circuit found for {0}, aborting Scene.RemoveClient", agentID); - return; - } - else - { - // We remove the acd up here to avoid later race conditions if two RemoveClient() calls occurred - // simultaneously. - // We also need to remove by agent ID since NPCs will have no circuit code. - m_authenticateHandler.RemoveCircuit(agentID); - } + return; + } + else + { + m_authenticateHandler.RemoveCircuit(agentID); } + // TODO: Can we now remove this lock? lock (acd) - { + { + bool isChildAgent = false; + ScenePresence avatar = GetScenePresence(agentID); - + + // Shouldn't be necessary since RemoveClient() is currently only called by IClientAPI.Close() which + // in turn is only called by Scene.IncomingCloseAgent() which checks whether the presence exists or not + // However, will keep for now just in case. if (avatar == null) { - m_log.WarnFormat( + m_log.ErrorFormat( "[SCENE]: Called RemoveClient() with agent ID {0} but no such presence is in the scene.", agentID); return; } - + try { isChildAgent = avatar.IsChildAgent; m_log.DebugFormat( "[SCENE]: Removing {0} agent {1} {2} from {3}", - (isChildAgent ? "child" : "root"), avatar.Name, agentID, RegionInfo.RegionName); + isChildAgent ? "child" : "root", avatar.Name, agentID, Name); // Don't do this to root agents, it's not nice for the viewer if (closeChildAgents && isChildAgent) @@ -3587,13 +3588,13 @@ namespace OpenSim.Region.Framework.Scenes /// is activated later when the viewer sends the initial UseCircuitCodePacket UDP packet (in the case of /// the LLUDP stack). /// - /// CircuitData of the agent who is connecting + /// CircuitData of the agent who is connecting /// Outputs the reason for the false response on this string /// True for normal presence. False for NPC /// or other applications where a full grid/Hypergrid presence may not be required. /// True if the region accepts this agent. False if it does not. False will /// also return a reason. - public bool NewUserConnection(AgentCircuitData agent, uint teleportFlags, out string reason, bool requirePresenceLookup) + public bool NewUserConnection(AgentCircuitData acd, uint teleportFlags, out string reason, bool requirePresenceLookup) { bool vialogin = ((teleportFlags & (uint)TPFlags.ViaLogin) != 0 || (teleportFlags & (uint)TPFlags.ViaHGLogin) != 0); @@ -3613,15 +3614,15 @@ namespace OpenSim.Region.Framework.Scenes m_log.DebugFormat( "[SCENE]: Region {0} told of incoming {1} agent {2} {3} {4} (circuit code {5}, IP {6}, viewer {7}, teleportflags ({8}), position {9})", RegionInfo.RegionName, - (agent.child ? "child" : "root"), - agent.firstname, - agent.lastname, - agent.AgentID, - agent.circuitcode, - agent.IPAddress, - agent.Viewer, + (acd.child ? "child" : "root"), + acd.firstname, + acd.lastname, + acd.AgentID, + acd.circuitcode, + acd.IPAddress, + acd.Viewer, ((TPFlags)teleportFlags).ToString(), - agent.startpos + acd.startpos ); if (!LoginsEnabled) @@ -3639,7 +3640,7 @@ namespace OpenSim.Region.Framework.Scenes { foreach (string viewer in m_AllowedViewers) { - if (viewer == agent.Viewer.Substring(0, viewer.Length).Trim().ToLower()) + if (viewer == acd.Viewer.Substring(0, viewer.Length).Trim().ToLower()) { ViewerDenied = false; break; @@ -3656,7 +3657,7 @@ namespace OpenSim.Region.Framework.Scenes { foreach (string viewer in m_BannedViewers) { - if (viewer == agent.Viewer.Substring(0, viewer.Length).Trim().ToLower()) + if (viewer == acd.Viewer.Substring(0, viewer.Length).Trim().ToLower()) { ViewerDenied = true; break; @@ -3668,61 +3669,104 @@ namespace OpenSim.Region.Framework.Scenes { m_log.DebugFormat( "[SCENE]: Access denied for {0} {1} using {2}", - agent.firstname, agent.lastname, agent.Viewer); + acd.firstname, acd.lastname, acd.Viewer); reason = "Access denied, your viewer is banned by the region owner"; return false; } ILandObject land; + ScenePresence sp; - lock (agent) + lock (m_removeClientLock) { - ScenePresence sp = GetScenePresence(agent.AgentID); - - if (sp != null) + sp = GetScenePresence(acd.AgentID); + + // We need to ensure that we are not already removing the scene presence before we ask it not to be + // closed. + if (sp != null && sp.IsChildAgent && sp.LifecycleState == ScenePresenceState.Running) { - if (!sp.IsChildAgent) - { - // We have a root agent. Is it in transit? - if (!EntityTransferModule.IsInTransit(sp.UUID)) - { - // 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.WarnFormat( - "[SCENE]: Existing root scene presence detected for {0} {1} in {2} when connecting. Removing existing presence.", - sp.Name, sp.UUID, RegionInfo.RegionName); + m_log.DebugFormat( + "[SCENE]: Reusing existing child scene presence for {0} in {1}", sp.Name, Name); - if (sp.ControllingClient != null) - sp.ControllingClient.Close(true); + // In the case where, for example, an A B C D region layout, an avatar may + // teleport from A -> D, but then -> C before A has asked B to close its old child agent. When C + // renews the lease on the child agent at B, we must make sure that the close from A does not succeed. + if (!acd.ChildrenCapSeeds.ContainsKey(RegionInfo.RegionHandle)) + { + m_log.DebugFormat( + "[SCENE]: Setting DoNotCloseAfterTeleport for child scene presence {0} in {1} because source will attempt close.", + sp.Name, Name); - sp = null; - } - //else - // m_log.WarnFormat("[SCENE]: Existing root scene presence for {0} {1} in {2}, but agent is in trasit", sp.Name, sp.UUID, RegionInfo.RegionName); + sp.DoNotCloseAfterTeleport = true; } - else + } + } + + // Need to poll here in case we are currently deleting an sp. Letting threads run over each other will + // allow unpredictable things to happen. + if (sp != null) + { + const int polls = 10; + const int pollInterval = 1000; + int pollsLeft = polls; + + while (sp.LifecycleState == ScenePresenceState.Removing && pollsLeft-- > 0) + Thread.Sleep(pollInterval); + + if (sp.LifecycleState == ScenePresenceState.Removing) + { + m_log.WarnFormat( + "[SCENE]: Agent {0} in {1} was still being removed after {2}s. Aborting NewUserConnection.", + sp.Name, Name, polls * pollInterval / 1000); + + return false; + } + else if (polls != pollsLeft) + { + m_log.DebugFormat( + "[SCENE]: NewUserConnection for agent {0} in {1} had to wait {2}s for in-progress removal to complete on an old presence.", + sp.Name, Name, polls * pollInterval / 1000); + } + } + + // TODO: can we remove this lock? + lock (acd) + { + if (sp != null && !sp.IsChildAgent) + { + // We have a root agent. Is it in transit? + if (!EntityTransferModule.IsInTransit(sp.UUID)) { - // We have a child agent here - sp.DoNotCloseAfterTeleport = true; - //m_log.WarnFormat("[SCENE]: Existing child scene presence for {0} {1} in {2}", sp.Name, sp.UUID, RegionInfo.RegionName); + // 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.WarnFormat( + "[SCENE]: Existing root scene presence detected for {0} {1} in {2} when connecting. Removing existing presence.", + sp.Name, sp.UUID, RegionInfo.RegionName); + + if (sp.ControllingClient != null) + IncomingCloseAgent(sp.UUID, true); + + sp = null; } + //else + // m_log.WarnFormat("[SCENE]: Existing root scene presence for {0} {1} in {2}, but agent is in trasit", sp.Name, sp.UUID, RegionInfo.RegionName); } // Optimistic: add or update the circuit data with the new agent circuit data and teleport flags. // We need the circuit data here for some of the subsequent checks. (groups, for example) // If the checks fail, we remove the circuit. - agent.teleportFlags = teleportFlags; - m_authenticateHandler.AddNewCircuit(agent.circuitcode, agent); + acd.teleportFlags = teleportFlags; + m_authenticateHandler.AddNewCircuit(acd.circuitcode, acd); - land = LandChannel.GetLandObject(agent.startpos.X, agent.startpos.Y); + land = LandChannel.GetLandObject(acd.startpos.X, acd.startpos.Y); // On login test land permisions if (vialogin) { - if (land != null && !TestLandRestrictions(agent.AgentID, out reason, ref agent.startpos.X, ref agent.startpos.Y)) + if (land != null && !TestLandRestrictions(acd.AgentID, out reason, ref acd.startpos.X, ref acd.startpos.Y)) { - m_authenticateHandler.RemoveCircuit(agent.circuitcode); + m_authenticateHandler.RemoveCircuit(acd.circuitcode); return false; } } @@ -3733,9 +3777,9 @@ namespace OpenSim.Region.Framework.Scenes { try { - if (!VerifyUserPresence(agent, out reason)) + if (!VerifyUserPresence(acd, out reason)) { - m_authenticateHandler.RemoveCircuit(agent.circuitcode); + m_authenticateHandler.RemoveCircuit(acd.circuitcode); return false; } } @@ -3744,16 +3788,16 @@ namespace OpenSim.Region.Framework.Scenes m_log.ErrorFormat( "[SCENE]: Exception verifying presence {0}{1}", e.Message, e.StackTrace); - m_authenticateHandler.RemoveCircuit(agent.circuitcode); + m_authenticateHandler.RemoveCircuit(acd.circuitcode); return false; } } try { - if (!AuthorizeUser(agent, SeeIntoRegion, out reason)) + if (!AuthorizeUser(acd, SeeIntoRegion, out reason)) { - m_authenticateHandler.RemoveCircuit(agent.circuitcode); + m_authenticateHandler.RemoveCircuit(acd.circuitcode); return false; } } @@ -3762,19 +3806,19 @@ namespace OpenSim.Region.Framework.Scenes m_log.ErrorFormat( "[SCENE]: Exception authorizing user {0}{1}", e.Message, e.StackTrace); - m_authenticateHandler.RemoveCircuit(agent.circuitcode); + m_authenticateHandler.RemoveCircuit(acd.circuitcode); return false; } m_log.InfoFormat( "[SCENE]: Region {0} authenticated and authorized incoming {1} agent {2} {3} {4} (circuit code {5})", - RegionInfo.RegionName, (agent.child ? "child" : "root"), agent.firstname, agent.lastname, - agent.AgentID, agent.circuitcode); + Name, (acd.child ? "child" : "root"), acd.firstname, acd.lastname, + acd.AgentID, acd.circuitcode); if (CapsModule != null) { - CapsModule.SetAgentCapsSeeds(agent); - CapsModule.CreateCaps(agent.AgentID); + CapsModule.SetAgentCapsSeeds(acd); + CapsModule.CreateCaps(acd.AgentID); } } else @@ -3787,14 +3831,14 @@ namespace OpenSim.Region.Framework.Scenes { m_log.DebugFormat( "[SCENE]: Adjusting known seeds for existing agent {0} in {1}", - agent.AgentID, RegionInfo.RegionName); + acd.AgentID, RegionInfo.RegionName); sp.AdjustKnownSeeds(); if (CapsModule != null) { - CapsModule.SetAgentCapsSeeds(agent); - CapsModule.CreateCaps(agent.AgentID); + CapsModule.SetAgentCapsSeeds(acd); + CapsModule.CreateCaps(acd.AgentID); } } } @@ -3802,23 +3846,23 @@ namespace OpenSim.Region.Framework.Scenes // Try caching an incoming user name much earlier on to see if this helps with an issue // where HG users are occasionally seen by others as "Unknown User" because their UUIDName // request for the HG avatar appears to trigger before the user name is cached. - CacheUserName(null, agent); + CacheUserName(null, acd); } if (vialogin) { // CleanDroppedAttachments(); - if (TestBorderCross(agent.startpos, Cardinals.E)) + if (TestBorderCross(acd.startpos, Cardinals.E)) { - Border crossedBorder = GetCrossedBorder(agent.startpos, Cardinals.E); - agent.startpos.X = crossedBorder.BorderLine.Z - 1; + Border crossedBorder = GetCrossedBorder(acd.startpos, Cardinals.E); + acd.startpos.X = crossedBorder.BorderLine.Z - 1; } - if (TestBorderCross(agent.startpos, Cardinals.N)) + if (TestBorderCross(acd.startpos, Cardinals.N)) { - Border crossedBorder = GetCrossedBorder(agent.startpos, Cardinals.N); - agent.startpos.Y = crossedBorder.BorderLine.Z - 1; + Border crossedBorder = GetCrossedBorder(acd.startpos, Cardinals.N); + acd.startpos.Y = crossedBorder.BorderLine.Z - 1; } //Mitigate http://opensimulator.org/mantis/view.php?id=3522 @@ -3828,39 +3872,39 @@ namespace OpenSim.Region.Framework.Scenes { lock (EastBorders) { - if (agent.startpos.X > EastBorders[0].BorderLine.Z) + if (acd.startpos.X > EastBorders[0].BorderLine.Z) { m_log.Warn("FIX AGENT POSITION"); - agent.startpos.X = EastBorders[0].BorderLine.Z * 0.5f; - if (agent.startpos.Z > 720) - agent.startpos.Z = 720; + acd.startpos.X = EastBorders[0].BorderLine.Z * 0.5f; + if (acd.startpos.Z > 720) + acd.startpos.Z = 720; } } lock (NorthBorders) { - if (agent.startpos.Y > NorthBorders[0].BorderLine.Z) + if (acd.startpos.Y > NorthBorders[0].BorderLine.Z) { m_log.Warn("FIX Agent POSITION"); - agent.startpos.Y = NorthBorders[0].BorderLine.Z * 0.5f; - if (agent.startpos.Z > 720) - agent.startpos.Z = 720; + acd.startpos.Y = NorthBorders[0].BorderLine.Z * 0.5f; + if (acd.startpos.Z > 720) + acd.startpos.Z = 720; } } } else { - if (agent.startpos.X > EastBorders[0].BorderLine.Z) + if (acd.startpos.X > EastBorders[0].BorderLine.Z) { m_log.Warn("FIX AGENT POSITION"); - agent.startpos.X = EastBorders[0].BorderLine.Z * 0.5f; - if (agent.startpos.Z > 720) - agent.startpos.Z = 720; + acd.startpos.X = EastBorders[0].BorderLine.Z * 0.5f; + if (acd.startpos.Z > 720) + acd.startpos.Z = 720; } - if (agent.startpos.Y > NorthBorders[0].BorderLine.Z) + if (acd.startpos.Y > NorthBorders[0].BorderLine.Z) { m_log.Warn("FIX Agent POSITION"); - agent.startpos.Y = NorthBorders[0].BorderLine.Z * 0.5f; - if (agent.startpos.Z > 720) - agent.startpos.Z = 720; + acd.startpos.Y = NorthBorders[0].BorderLine.Z * 0.5f; + if (acd.startpos.Z > 720) + acd.startpos.Z = 720; } } @@ -3876,12 +3920,12 @@ namespace OpenSim.Region.Framework.Scenes { // We have multiple SpawnPoints, Route the agent to a random or sequential one if (SpawnPointRouting == "random") - agent.startpos = spawnpoints[Util.RandomClass.Next(spawnpoints.Count) - 1].GetLocation( + acd.startpos = spawnpoints[Util.RandomClass.Next(spawnpoints.Count) - 1].GetLocation( telehub.AbsolutePosition, telehub.GroupRotation ); else - agent.startpos = spawnpoints[SpawnPoint()].GetLocation( + acd.startpos = spawnpoints[SpawnPoint()].GetLocation( telehub.AbsolutePosition, telehub.GroupRotation ); @@ -3889,7 +3933,7 @@ namespace OpenSim.Region.Framework.Scenes else { // We have a single SpawnPoint and will route the agent to it - agent.startpos = spawnpoints[0].GetLocation(telehub.AbsolutePosition, telehub.GroupRotation); + acd.startpos = spawnpoints[0].GetLocation(telehub.AbsolutePosition, telehub.GroupRotation); } return true; @@ -3900,7 +3944,7 @@ namespace OpenSim.Region.Framework.Scenes { if (land.LandData.LandingType == (byte)1 && land.LandData.UserLocation != Vector3.Zero) { - agent.startpos = land.LandData.UserLocation; + acd.startpos = land.LandData.UserLocation; } } } @@ -4359,11 +4403,51 @@ namespace OpenSim.Region.Framework.Scenes /// public bool IncomingCloseAgent(UUID agentID, bool force) { - //m_log.DebugFormat("[SCENE]: Processing incoming close agent for {0}", agentID); - ScenePresence presence = m_sceneGraph.GetScenePresence(agentID); - if (presence != null) + ScenePresence sp; + + lock (m_removeClientLock) + { + sp = GetScenePresence(agentID); + + if (sp == null) + { + m_log.DebugFormat( + "[SCENE]: Called RemoveClient() with agent ID {0} but no such presence is in {1}", + agentID, Name); + + return false; + } + + if (sp.LifecycleState != ScenePresenceState.Running) + { + m_log.DebugFormat( + "[SCENE]: Called RemoveClient() for {0} in {1} but presence is already in state {2}", + sp.Name, Name, sp.LifecycleState); + + return false; + } + + // We need to avoid a race condition where in, for example, an A B C D region layout, an avatar may + // teleport from A -> D, but then -> C before A has asked B to close its old child agent. We do not + // want to obey this close since C may have renewed the child agent lease on B. + if (sp.DoNotCloseAfterTeleport) + { + m_log.DebugFormat( + "[SCENE]: Not closing {0} agent {1} in {2} since another simulator has re-established the child connection", + sp.IsChildAgent ? "child" : "root", sp.Name, Name); + + // Need to reset the flag so that a subsequent close after another teleport can succeed. + sp.DoNotCloseAfterTeleport = false; + + return false; + } + + sp.LifecycleState = ScenePresenceState.Removing; + } + + if (sp != null) { - presence.ControllingClient.Close(force); + sp.ControllingClient.Close(force); return true; } diff --git a/OpenSim/Region/Framework/Scenes/ScenePresence.cs b/OpenSim/Region/Framework/Scenes/ScenePresence.cs index 7fd1302..bdcdf03 100644 --- a/OpenSim/Region/Framework/Scenes/ScenePresence.cs +++ b/OpenSim/Region/Framework/Scenes/ScenePresence.cs @@ -74,6 +74,8 @@ namespace OpenSim.Region.Framework.Scenes public class ScenePresence : EntityBase, IScenePresence { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + // ~ScenePresence() // { // m_log.DebugFormat("[SCENE PRESENCE]: Destructor called on {0}", Name); @@ -85,10 +87,27 @@ namespace OpenSim.Region.Framework.Scenes m_scene.EventManager.TriggerScenePresenceUpdated(this); } - private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); - public PresenceType PresenceType { get; private set; } + private ScenePresenceStateMachine m_stateMachine; + + /// + /// The current state of this presence. Governs only the existence lifecycle. See ScenePresenceStateMachine + /// for more details. + /// + public ScenePresenceState LifecycleState + { + get + { + return m_stateMachine.GetState(); + } + + set + { + m_stateMachine.SetState(value); + } + } + // private static readonly byte[] DEFAULT_TEXTURE = AvatarAppearance.GetDefaultTexture().GetBytes(); private static readonly Array DIR_CONTROL_FLAGS = Enum.GetValues(typeof(Dir_ControlFlags)); private static readonly Vector3 HEAD_ADJUSTMENT = new Vector3(0f, 0f, 0.3f); @@ -766,7 +785,7 @@ namespace OpenSim.Region.Framework.Scenes public ScenePresence( IClientAPI client, Scene world, AvatarAppearance appearance, PresenceType type) - { + { AttachmentsSyncLock = new Object(); AllowMovement = true; IsChildAgent = true; @@ -811,6 +830,8 @@ namespace OpenSim.Region.Framework.Scenes SetDirectionVectors(); Appearance = appearance; + + m_stateMachine = new ScenePresenceStateMachine(this); } public void RegisterToEvents() @@ -879,7 +900,7 @@ namespace OpenSim.Region.Framework.Scenes /// public void MakeRootAgent(Vector3 pos, bool isFlying) { - m_log.DebugFormat( + m_log.InfoFormat( "[SCENE]: Upgrading child to root agent for {0} in {1}", Name, m_scene.RegionInfo.RegionName); @@ -887,6 +908,11 @@ namespace OpenSim.Region.Framework.Scenes IsChildAgent = false; + // Must reset this here so that a teleport to a region next to an existing region does not keep the flag + // set and prevent the close of the connection on a subsequent re-teleport. + // Should not be needed if we are not trying to tell this region to close +// DoNotCloseAfterTeleport = false; + IGroupsModule gm = m_scene.RequestModuleInterface(); if (gm != null) Grouptitle = gm.GetGroupTitle(m_uuid); @@ -3738,6 +3764,8 @@ namespace OpenSim.Region.Framework.Scenes // m_reprioritizationTimer.Dispose(); RemoveFromPhysicalScene(); + + LifecycleState = ScenePresenceState.Removed; } public void AddAttachment(SceneObjectGroup gobj) -- cgit v1.1 From 99bce9d87723af1958a76cf8ac5e765ca804549d Mon Sep 17 00:00:00 2001 From: Justin Clark-Casey (justincc) Date: Fri, 9 Aug 2013 00:24:22 +0100 Subject: Fix an issue with an A->C->B->A teleport where these regions are in a row (A,B,C) where the A root agent is still closed, terminating the connection. This was occuring because teleport to B did not set DoNotCloseAfterTeleport on A as it was a neighbour (where it isn't set to avoid the issue where the source region doesn't send Close() to regions that are still neighbours (hence not resetting DoNotCloseAfterTeleport). Fix here is to still set DoNotCloseAfterTeleport if scene presence is still registered as in transit from A --- OpenSim/Region/Framework/Scenes/Scene.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/OpenSim/Region/Framework/Scenes/Scene.cs b/OpenSim/Region/Framework/Scenes/Scene.cs index 5f10869..a3ea7d9 100644 --- a/OpenSim/Region/Framework/Scenes/Scene.cs +++ b/OpenSim/Region/Framework/Scenes/Scene.cs @@ -3699,6 +3699,14 @@ namespace OpenSim.Region.Framework.Scenes sp.DoNotCloseAfterTeleport = true; } + else if (EntityTransferModule.IsInTransit(sp.UUID)) + { + m_log.DebugFormat( + "[SCENE]: Setting DoNotCloseAfterTeleport for child scene presence {0} in {1} because this region will attempt previous end-of-teleport close.", + sp.Name, Name); + + sp.DoNotCloseAfterTeleport = true; + } } } -- cgit v1.1 From 7e01213bf2dba1f771cc49b3d27ad97e93c35961 Mon Sep 17 00:00:00 2001 From: Diva Canto Date: Fri, 9 Aug 2013 08:31:15 -0700 Subject: Go easy on enforcing session ids in position updates --- OpenSim/Region/Framework/Scenes/Scene.cs | 33 ++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/OpenSim/Region/Framework/Scenes/Scene.cs b/OpenSim/Region/Framework/Scenes/Scene.cs index 503b81a..1633e07 100644 --- a/OpenSim/Region/Framework/Scenes/Scene.cs +++ b/OpenSim/Region/Framework/Scenes/Scene.cs @@ -4267,24 +4267,25 @@ namespace OpenSim.Region.Framework.Scenes ScenePresence childAgentUpdate = GetScenePresence(cAgentData.AgentID); if (childAgentUpdate != null) { - if (childAgentUpdate.ControllingClient.SessionId == cAgentData.SessionID) + if (childAgentUpdate.ControllingClient.SessionId != cAgentData.SessionID) + // Only warn for now + m_log.WarnFormat("[SCENE]: Attempt at updating position of agent {0} with invalid session id {1}. Neighbor running older version?", + childAgentUpdate.UUID, cAgentData.SessionID); + + // I can't imagine *yet* why we would get an update if the agent is a root agent.. + // however to avoid a race condition crossing borders.. + if (childAgentUpdate.IsChildAgent) { - // I can't imagine *yet* why we would get an update if the agent is a root agent.. - // however to avoid a race condition crossing borders.. - if (childAgentUpdate.IsChildAgent) - { - uint rRegionX = (uint)(cAgentData.RegionHandle >> 40); - uint rRegionY = (((uint)(cAgentData.RegionHandle)) >> 8); - uint tRegionX = RegionInfo.RegionLocX; - uint tRegionY = RegionInfo.RegionLocY; - //Send Data to ScenePresence - childAgentUpdate.ChildAgentDataUpdate(cAgentData, tRegionX, tRegionY, rRegionX, rRegionY); - // Not Implemented: - //TODO: Do we need to pass the message on to one of our neighbors? - } + uint rRegionX = (uint)(cAgentData.RegionHandle >> 40); + uint rRegionY = (((uint)(cAgentData.RegionHandle)) >> 8); + uint tRegionX = RegionInfo.RegionLocX; + uint tRegionY = RegionInfo.RegionLocY; + //Send Data to ScenePresence + childAgentUpdate.ChildAgentDataUpdate(cAgentData, tRegionX, tRegionY, rRegionX, rRegionY); + // Not Implemented: + //TODO: Do we need to pass the message on to one of our neighbors? } - else - m_log.WarnFormat("[SCENE]: Attempt at updating position of agent {0} with invalid session id {1}", childAgentUpdate.UUID, cAgentData.SessionID); + return true; } -- cgit v1.1 From 6fcbf219da1d5095d6f37e8e44031db9ef108c39 Mon Sep 17 00:00:00 2001 From: Justin Clark-Casey (justincc) Date: Fri, 9 Aug 2013 17:48:35 +0100 Subject: Comment back out seed dump code enabled in b1c26a56. Also adds a few teleport comments. --- .../EntityTransfer/EntityTransferModule.cs | 38 +++++++++++++--------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferModule.cs b/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferModule.cs index 2f74253..87f0264 100644 --- a/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferModule.cs +++ b/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferModule.cs @@ -1067,6 +1067,12 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer // May still need to signal neighbours whether child agents may need closing irrespective of whether this // one needed closing. Neighbour regions also contain logic to prevent a close if a subsequent move or // teleport re-established the child connection. + // + // It may be possible to also close child agents after a pause but one needs to be very careful about + // race conditions between different regions on rapid teleporting (e.g. from A1 to a non-neighbour B, back + // to a neighbour A2 then off to a non-neighbour C. Also, closing child agents early may be more compatible + // with complicated scenarios where there a mixture of V1 and V2 teleports, though this is conjecture. It's + // easier to close immediately and greatly reduce the scope of race conditions if possible. sp.CloseChildAgents(newRegionX, newRegionY); // Finally, let's close this previously-known-as-root agent, when the jump is outside the view zone @@ -1865,10 +1871,10 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer List newRegions = NewNeighbours(neighbourHandles, previousRegionNeighbourHandles); List oldRegions = OldNeighbours(neighbourHandles, previousRegionNeighbourHandles); - Dump("Current Neighbors", neighbourHandles); - Dump("Previous Neighbours", previousRegionNeighbourHandles); - Dump("New Neighbours", newRegions); - Dump("Old Neighbours", oldRegions); +// Dump("Current Neighbors", neighbourHandles); +// Dump("Previous Neighbours", previousRegionNeighbourHandles); +// Dump("New Neighbours", newRegions); +// Dump("Old Neighbours", oldRegions); /// Update the scene presence's known regions here on this region sp.DropOldNeighbours(oldRegions); @@ -2180,18 +2186,18 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer return handles; } - private void Dump(string msg, List handles) - { - m_log.InfoFormat("-------------- HANDLE DUMP ({0}) ---------", msg); - foreach (ulong handle in handles) - { - uint x, y; - Utils.LongToUInts(handle, out x, out y); - x = x / Constants.RegionSize; - y = y / Constants.RegionSize; - m_log.InfoFormat("({0}, {1})", x, y); - } - } +// private void Dump(string msg, List handles) +// { +// m_log.InfoFormat("-------------- HANDLE DUMP ({0}) ---------", msg); +// foreach (ulong handle in handles) +// { +// uint x, y; +// Utils.LongToUInts(handle, out x, out y); +// x = x / Constants.RegionSize; +// y = y / Constants.RegionSize; +// m_log.InfoFormat("({0}, {1})", x, y); +// } +// } #endregion -- cgit v1.1 From aec701972844d4e006bb5d50e7983b818fb39249 Mon Sep 17 00:00:00 2001 From: Justin Clark-Casey (justincc) Date: Fri, 9 Aug 2013 17:57:24 +0100 Subject: Add missing file from b1c26a56 --- .../Framework/Scenes/ScenePresenceStateMachine.cs | 102 +++++++++++++++++++++ 1 file changed, 102 insertions(+) create mode 100644 OpenSim/Region/Framework/Scenes/ScenePresenceStateMachine.cs diff --git a/OpenSim/Region/Framework/Scenes/ScenePresenceStateMachine.cs b/OpenSim/Region/Framework/Scenes/ScenePresenceStateMachine.cs new file mode 100644 index 0000000..dc3a212 --- /dev/null +++ b/OpenSim/Region/Framework/Scenes/ScenePresenceStateMachine.cs @@ -0,0 +1,102 @@ +/* + * 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; + +namespace OpenSim.Region.Framework.Scenes +{ + /// + /// The possible states that a scene presence can be in. This is currently orthagonal to whether a scene presence + /// is root or child. + /// + /// + /// This is a state machine. + /// + /// [Entry] => Running + /// Running => Removing + /// Removing => Removed + /// + /// All other methods should only see the scene presence in running state - this is the normal operational state + /// Removed state occurs when the presence has been removed. This is the end state with no exit. + /// + public enum ScenePresenceState + { + Running, // Normal operation state. The scene presence is available. + Removing, // The presence is in the process of being removed from the scene via Scene.RemoveClient. + Removed, // The presence has been removed from the scene and is effectively dead. + // There is no exit from this state. + } + + internal class ScenePresenceStateMachine + { + private ScenePresence m_sp; + private ScenePresenceState m_state; + + internal ScenePresenceStateMachine(ScenePresence sp) + { + m_sp = sp; + m_state = ScenePresenceState.Running; + } + + internal ScenePresenceState GetState() + { + return m_state; + } + + /// + /// Updates the state of an agent that is already in transit. + /// + /// + /// + /// + /// Illegal transitions will throw an Exception + internal void SetState(ScenePresenceState newState) + { + bool transitionOkay = false; + + lock (this) + { + if (newState == ScenePresenceState.Removing && m_state == ScenePresenceState.Running) + transitionOkay = true; + else if (newState == ScenePresenceState.Removed && m_state == ScenePresenceState.Removing) + transitionOkay = true; + } + + if (!transitionOkay) + { + throw new Exception( + string.Format( + "Scene presence {0} is not allowed to move from state {1} to new state {2} in {3}", + m_sp.Name, m_state, newState, m_sp.Scene.Name)); + } + else + { + m_state = newState; + } + } + } +} \ No newline at end of file -- cgit v1.1 From bfdcdbb2f3f568c3ef829f30b8326dbc8a835f03 Mon Sep 17 00:00:00 2001 From: Justin Clark-Casey (justincc) Date: Fri, 9 Aug 2013 17:59:58 +0100 Subject: Increase wait for source region to sent UpdateAgent to 10 seconds instead of 4. This is giving much better results on teleports between simulators over my lan where for some reason there is a pause before the receiving simulator processes UpdateAgent() At this point, v2 teleports between neighbour and non-neighbour regions on a single simulator and between v2 simulators and between a v1 and v2 simulator are working okay for me in different scenarios (e.g. simple teleport, teleport back to original quickly and re-teleport, teleport back to neighbour and re-teleport. etc.) --- OpenSim/Region/Framework/Scenes/ScenePresence.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OpenSim/Region/Framework/Scenes/ScenePresence.cs b/OpenSim/Region/Framework/Scenes/ScenePresence.cs index aac80f7..69339b7 100644 --- a/OpenSim/Region/Framework/Scenes/ScenePresence.cs +++ b/OpenSim/Region/Framework/Scenes/ScenePresence.cs @@ -1355,7 +1355,7 @@ namespace OpenSim.Region.Framework.Scenes private bool WaitForUpdateAgent(IClientAPI client) { // Before UpdateAgent, m_originRegionID is UUID.Zero; after, it's non-Zero - int count = 20; + int count = 50; while (m_originRegionID.Equals(UUID.Zero) && count-- > 0) { m_log.DebugFormat("[SCENE PRESENCE]: Agent {0} waiting for update in {1}", client.Name, Scene.Name); -- cgit v1.1 From 23ca1f859e20e537572cb774943784d50d3cacb8 Mon Sep 17 00:00:00 2001 From: Justin Clark-Casey (justincc) Date: Fri, 9 Aug 2013 18:27:26 +0100 Subject: minor: Consistently log IOCP for IO completion thread startup log information instead of mixing this with "IO Completion Threads" --- OpenSim/Region/Application/Application.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/OpenSim/Region/Application/Application.cs b/OpenSim/Region/Application/Application.cs index 2e155ec..3a4e5df 100644 --- a/OpenSim/Region/Application/Application.cs +++ b/OpenSim/Region/Application/Application.cs @@ -142,19 +142,19 @@ namespace OpenSim if (iocpThreads < iocpThreadsMin) { iocpThreads = iocpThreadsMin; - m_log.InfoFormat("[OPENSIM MAIN]: Bumping up max IO completion threads to {0}",iocpThreads); + m_log.InfoFormat("[OPENSIM MAIN]: Bumping up max IOCP threads to {0}",iocpThreads); } // Make sure we don't overallocate IOCP threads and thrash system resources if ( iocpThreads > iocpThreadsMax ) { iocpThreads = iocpThreadsMax; - m_log.InfoFormat("[OPENSIM MAIN]: Limiting max IO completion threads to {0}",iocpThreads); + m_log.InfoFormat("[OPENSIM MAIN]: Limiting max IOCP completion threads to {0}",iocpThreads); } // set the resulting worker and IO completion thread counts back to ThreadPool if ( System.Threading.ThreadPool.SetMaxThreads(workerThreads, iocpThreads) ) { m_log.InfoFormat( - "[OPENSIM MAIN]: Threadpool set to {0} max worker threads and {1} max IO completion threads", + "[OPENSIM MAIN]: Threadpool set to {0} max worker threads and {1} max IOCP threads", workerThreads, iocpThreads); } else -- cgit v1.1 From 216f5afe54576c4852974b8479ac95654dc9e08e Mon Sep 17 00:00:00 2001 From: Robert Adams Date: Sat, 10 Aug 2013 09:09:52 -0700 Subject: Stats treaking. Update ToOSDMap for Stat and PercentageStat to return all the various numbers that have been added to the console output. Break out EventHistogram from CounterStat. --- OpenSim/Framework/Monitoring/Stats/CounterStat.cs | 150 ++---------------- .../Framework/Monitoring/Stats/EventHistogram.cs | 173 +++++++++++++++++++++ .../Framework/Monitoring/Stats/PercentageStat.cs | 16 ++ OpenSim/Framework/Monitoring/Stats/Stat.cs | 46 ++++-- 4 files changed, 235 insertions(+), 150 deletions(-) create mode 100755 OpenSim/Framework/Monitoring/Stats/EventHistogram.cs diff --git a/OpenSim/Framework/Monitoring/Stats/CounterStat.cs b/OpenSim/Framework/Monitoring/Stats/CounterStat.cs index 04442c3..318cf1c 100755 --- a/OpenSim/Framework/Monitoring/Stats/CounterStat.cs +++ b/OpenSim/Framework/Monitoring/Stats/CounterStat.cs @@ -34,142 +34,6 @@ using OpenMetaverse.StructuredData; namespace OpenSim.Framework.Monitoring { -// Create a time histogram of events. The histogram is built in a wrap-around -// array of equally distributed buckets. -// For instance, a minute long histogram of second sized buckets would be: -// new EventHistogram(60, 1000) -public class EventHistogram -{ - private int m_timeBase; - private int m_numBuckets; - private int m_bucketMilliseconds; - private int m_lastBucket; - private int m_totalHistogramMilliseconds; - private long[] m_histogram; - private object histoLock = new object(); - - public EventHistogram(int numberOfBuckets, int millisecondsPerBucket) - { - m_numBuckets = numberOfBuckets; - m_bucketMilliseconds = millisecondsPerBucket; - m_totalHistogramMilliseconds = m_numBuckets * m_bucketMilliseconds; - - m_histogram = new long[m_numBuckets]; - Zero(); - m_lastBucket = 0; - m_timeBase = Util.EnvironmentTickCount(); - } - - public void Event() - { - this.Event(1); - } - - // Record an event at time 'now' in the histogram. - public void Event(int cnt) - { - lock (histoLock) - { - // The time as displaced from the base of the histogram - int bucketTime = Util.EnvironmentTickCountSubtract(m_timeBase); - - // If more than the total time of the histogram, we just start over - if (bucketTime > m_totalHistogramMilliseconds) - { - Zero(); - m_lastBucket = 0; - m_timeBase = Util.EnvironmentTickCount(); - } - else - { - // To which bucket should we add this event? - int bucket = bucketTime / m_bucketMilliseconds; - - // Advance m_lastBucket to the new bucket. Zero any buckets skipped over. - while (bucket != m_lastBucket) - { - // Zero from just after the last bucket to the new bucket or the end - for (int jj = m_lastBucket + 1; jj <= Math.Min(bucket, m_numBuckets - 1); jj++) - { - m_histogram[jj] = 0; - } - m_lastBucket = bucket; - // If the new bucket is off the end, wrap around to the beginning - if (bucket > m_numBuckets) - { - bucket -= m_numBuckets; - m_lastBucket = 0; - m_histogram[m_lastBucket] = 0; - m_timeBase += m_totalHistogramMilliseconds; - } - } - } - m_histogram[m_lastBucket] += cnt; - } - } - - // Get a copy of the current histogram - public long[] GetHistogram() - { - long[] ret = new long[m_numBuckets]; - lock (histoLock) - { - int indx = m_lastBucket + 1; - for (int ii = 0; ii < m_numBuckets; ii++, indx++) - { - if (indx >= m_numBuckets) - indx = 0; - ret[ii] = m_histogram[indx]; - } - } - return ret; - } - - public OSDMap GetHistogramAsOSDMap() - { - OSDMap ret = new OSDMap(); - - ret.Add("Buckets", OSD.FromInteger(m_numBuckets)); - ret.Add("BucketMilliseconds", OSD.FromInteger(m_bucketMilliseconds)); - ret.Add("TotalMilliseconds", OSD.FromInteger(m_totalHistogramMilliseconds)); - - // Compute a number for the first bucket in the histogram. - // This will allow readers to know how this histogram relates to any previously read histogram. - int baseBucketNum = (m_timeBase / m_bucketMilliseconds) + m_lastBucket + 1; - ret.Add("BaseNumber", OSD.FromInteger(baseBucketNum)); - - ret.Add("Values", GetHistogramAsOSDArray()); - - return ret; - } - // Get a copy of the current histogram - public OSDArray GetHistogramAsOSDArray() - { - OSDArray ret = new OSDArray(m_numBuckets); - lock (histoLock) - { - int indx = m_lastBucket + 1; - for (int ii = 0; ii < m_numBuckets; ii++, indx++) - { - if (indx >= m_numBuckets) - indx = 0; - ret[ii] = OSD.FromLong(m_histogram[indx]); - } - } - return ret; - } - - // Zero out the histogram - public void Zero() - { - lock (histoLock) - { - for (int ii = 0; ii < m_numBuckets; ii++) - m_histogram[ii] = 0; - } - } -} - // A statistic that wraps a counter. // Built this way mostly so histograms and history can be created. public class CounterStat : Stat @@ -236,12 +100,18 @@ public class CounterStat : Stat // If there are any histograms, add a new field that is an array of histograms as OSDMaps if (m_histograms.Count > 0) { - OSDArray histos = new OSDArray(); - foreach (EventHistogram histo in m_histograms.Values) + lock (counterLock) { - histos.Add(histo.GetHistogramAsOSDMap()); + if (m_histograms.Count > 0) + { + OSDArray histos = new OSDArray(); + foreach (EventHistogram histo in m_histograms.Values) + { + histos.Add(histo.GetHistogramAsOSDMap()); + } + map.Add("Histograms", histos); + } } - map.Add("Histograms", histos); } return map; } diff --git a/OpenSim/Framework/Monitoring/Stats/EventHistogram.cs b/OpenSim/Framework/Monitoring/Stats/EventHistogram.cs new file mode 100755 index 0000000..f51f322 --- /dev/null +++ b/OpenSim/Framework/Monitoring/Stats/EventHistogram.cs @@ -0,0 +1,173 @@ +/* + * 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.Text; + +using OpenMetaverse.StructuredData; + +namespace OpenSim.Framework.Monitoring +{ +// Create a time histogram of events. The histogram is built in a wrap-around +// array of equally distributed buckets. +// For instance, a minute long histogram of second sized buckets would be: +// new EventHistogram(60, 1000) +public class EventHistogram +{ + private int m_timeBase; + private int m_numBuckets; + private int m_bucketMilliseconds; + private int m_lastBucket; + private int m_totalHistogramMilliseconds; + private long[] m_histogram; + private object histoLock = new object(); + + public EventHistogram(int numberOfBuckets, int millisecondsPerBucket) + { + m_numBuckets = numberOfBuckets; + m_bucketMilliseconds = millisecondsPerBucket; + m_totalHistogramMilliseconds = m_numBuckets * m_bucketMilliseconds; + + m_histogram = new long[m_numBuckets]; + Zero(); + m_lastBucket = 0; + m_timeBase = Util.EnvironmentTickCount(); + } + + public void Event() + { + this.Event(1); + } + + // Record an event at time 'now' in the histogram. + public void Event(int cnt) + { + lock (histoLock) + { + // The time as displaced from the base of the histogram + int bucketTime = Util.EnvironmentTickCountSubtract(m_timeBase); + + // If more than the total time of the histogram, we just start over + if (bucketTime > m_totalHistogramMilliseconds) + { + Zero(); + m_lastBucket = 0; + m_timeBase = Util.EnvironmentTickCount(); + } + else + { + // To which bucket should we add this event? + int bucket = bucketTime / m_bucketMilliseconds; + + // Advance m_lastBucket to the new bucket. Zero any buckets skipped over. + while (bucket != m_lastBucket) + { + // Zero from just after the last bucket to the new bucket or the end + for (int jj = m_lastBucket + 1; jj <= Math.Min(bucket, m_numBuckets - 1); jj++) + { + m_histogram[jj] = 0; + } + m_lastBucket = bucket; + // If the new bucket is off the end, wrap around to the beginning + if (bucket > m_numBuckets) + { + bucket -= m_numBuckets; + m_lastBucket = 0; + m_histogram[m_lastBucket] = 0; + m_timeBase += m_totalHistogramMilliseconds; + } + } + } + m_histogram[m_lastBucket] += cnt; + } + } + + // Get a copy of the current histogram + public long[] GetHistogram() + { + long[] ret = new long[m_numBuckets]; + lock (histoLock) + { + int indx = m_lastBucket + 1; + for (int ii = 0; ii < m_numBuckets; ii++, indx++) + { + if (indx >= m_numBuckets) + indx = 0; + ret[ii] = m_histogram[indx]; + } + } + return ret; + } + + public OSDMap GetHistogramAsOSDMap() + { + OSDMap ret = new OSDMap(); + + ret.Add("Buckets", OSD.FromInteger(m_numBuckets)); + ret.Add("BucketMilliseconds", OSD.FromInteger(m_bucketMilliseconds)); + ret.Add("TotalMilliseconds", OSD.FromInteger(m_totalHistogramMilliseconds)); + + // Compute a number for the first bucket in the histogram. + // This will allow readers to know how this histogram relates to any previously read histogram. + int baseBucketNum = (m_timeBase / m_bucketMilliseconds) + m_lastBucket + 1; + ret.Add("BaseNumber", OSD.FromInteger(baseBucketNum)); + + ret.Add("Values", GetHistogramAsOSDArray()); + + return ret; + } + // Get a copy of the current histogram + public OSDArray GetHistogramAsOSDArray() + { + OSDArray ret = new OSDArray(m_numBuckets); + lock (histoLock) + { + int indx = m_lastBucket + 1; + for (int ii = 0; ii < m_numBuckets; ii++, indx++) + { + if (indx >= m_numBuckets) + indx = 0; + ret[ii] = OSD.FromLong(m_histogram[indx]); + } + } + return ret; + } + + // Zero out the histogram + public void Zero() + { + lock (histoLock) + { + for (int ii = 0; ii < m_numBuckets; ii++) + m_histogram[ii] = 0; + } + } +} + +} diff --git a/OpenSim/Framework/Monitoring/Stats/PercentageStat.cs b/OpenSim/Framework/Monitoring/Stats/PercentageStat.cs index 60bed55..55ddf06 100644 --- a/OpenSim/Framework/Monitoring/Stats/PercentageStat.cs +++ b/OpenSim/Framework/Monitoring/Stats/PercentageStat.cs @@ -29,6 +29,8 @@ using System; using System.Collections.Generic; using System.Text; +using OpenMetaverse.StructuredData; + namespace OpenSim.Framework.Monitoring { public class PercentageStat : Stat @@ -84,5 +86,19 @@ namespace OpenSim.Framework.Monitoring return sb.ToString(); } + + // PercentageStat is a basic stat plus percent calc + public override OSDMap ToOSDMap() + { + // Get the foundational instance + OSDMap map = base.ToOSDMap(); + + map["StatType"] = "PercentageStat"; + + map.Add("Antecedent", OSD.FromLong(Antecedent)); + map.Add("Consequent", OSD.FromLong(Consequent)); + + return map; + } } } \ No newline at end of file diff --git a/OpenSim/Framework/Monitoring/Stats/Stat.cs b/OpenSim/Framework/Monitoring/Stats/Stat.cs index ffd5132..2b34493 100644 --- a/OpenSim/Framework/Monitoring/Stats/Stat.cs +++ b/OpenSim/Framework/Monitoring/Stats/Stat.cs @@ -241,6 +241,8 @@ namespace OpenSim.Framework.Monitoring public virtual OSDMap ToOSDMap() { OSDMap ret = new OSDMap(); + ret.Add("StatType", "Stat"); // used by overloading classes to denote type of stat + ret.Add("Category", OSD.FromString(Category)); ret.Add("Container", OSD.FromString(Container)); ret.Add("ShortName", OSD.FromString(ShortName)); @@ -248,26 +250,36 @@ namespace OpenSim.Framework.Monitoring ret.Add("Description", OSD.FromString(Description)); ret.Add("UnitName", OSD.FromString(UnitName)); ret.Add("Value", OSD.FromReal(Value)); - ret.Add("StatType", "Stat"); // used by overloading classes to denote type of stat + + double lastChangeOverTime, averageChangeOverTime; + if (ComputeMeasuresOfInterest(out lastChangeOverTime, out averageChangeOverTime)) + { + ret.Add("LastChangeOverTime", OSD.FromReal(lastChangeOverTime)); + ret.Add("AverageChangeOverTime", OSD.FromReal(averageChangeOverTime)); + } return ret; } - protected void AppendMeasuresOfInterest(StringBuilder sb) + // Compute the averages over time and return same. + // Return 'true' if averages were actually computed. 'false' if no average info. + public bool ComputeMeasuresOfInterest(out double lastChangeOverTime, out double averageChangeOverTime) { - if ((MeasuresOfInterest & MeasuresOfInterest.AverageChangeOverTime) - == MeasuresOfInterest.AverageChangeOverTime) + bool ret = false; + lastChangeOverTime = 0; + averageChangeOverTime = 0; + + if ((MeasuresOfInterest & MeasuresOfInterest.AverageChangeOverTime) == MeasuresOfInterest.AverageChangeOverTime) { double totalChange = 0; - double lastChangeOverTime = 0; double? penultimateSample = null; double? lastSample = null; lock (m_samples) { -// m_log.DebugFormat( -// "[STAT]: Samples for {0} are {1}", -// Name, string.Join(",", m_samples.Select(s => s.ToString()).ToArray())); + // m_log.DebugFormat( + // "[STAT]: Samples for {0} are {1}", + // Name, string.Join(",", m_samples.Select(s => s.ToString()).ToArray())); foreach (double s in m_samples) { @@ -280,13 +292,27 @@ namespace OpenSim.Framework.Monitoring } if (lastSample != null && penultimateSample != null) - lastChangeOverTime + { + lastChangeOverTime = ((double)lastSample - (double)penultimateSample) / (Watchdog.WATCHDOG_INTERVAL_MS / 1000); + } int divisor = m_samples.Count <= 1 ? 1 : m_samples.Count - 1; - double averageChangeOverTime = totalChange / divisor / (Watchdog.WATCHDOG_INTERVAL_MS / 1000); + averageChangeOverTime = totalChange / divisor / (Watchdog.WATCHDOG_INTERVAL_MS / 1000); + ret = true; + } + + return ret; + } + protected void AppendMeasuresOfInterest(StringBuilder sb) + { + double lastChangeOverTime = 0; + double averageChangeOverTime = 0; + + if (ComputeMeasuresOfInterest(out lastChangeOverTime, out averageChangeOverTime)) + { sb.AppendFormat( ", {0:0.##}{1}/s, {2:0.##}{3}/s", lastChangeOverTime, -- cgit v1.1