diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt index a16d17421d..fd00fe8288 100644 --- a/CONTRIBUTORS.txt +++ b/CONTRIBUTORS.txt @@ -16,7 +16,7 @@ people that make the day to day of OpenSim happen. * BlueWall (James Hughes) * Nebadon Izumi (Michael Cerquoni, OSgrid) * Snoopy Pfeffer -* Richard Adams (Intel) +* Robert Adams (Intel) = Core Developers Following the White Rabbit = Core developers who have temporarily (we hope) gone chasing the white rabbit. diff --git a/OpenSim/Capabilities/Handlers/GetTexture/GetTextureHandler.cs b/OpenSim/Capabilities/Handlers/GetTexture/GetTextureHandler.cs index 9b43a8059d..04cc33aa51 100644 --- a/OpenSim/Capabilities/Handlers/GetTexture/GetTextureHandler.cs +++ b/OpenSim/Capabilities/Handlers/GetTexture/GetTextureHandler.cs @@ -223,9 +223,9 @@ namespace OpenSim.Capabilities.Handlers // sending back the last byte instead of an error status if (start >= texture.Data.Length) { - m_log.DebugFormat( - "[GETTEXTURE]: Client requested range for texture {0} starting at {1} but texture has end of {2}", - texture.ID, start, texture.Data.Length); +// m_log.DebugFormat( +// "[GETTEXTURE]: Client requested range for texture {0} starting at {1} but texture has end of {2}", +// texture.ID, start, texture.Data.Length); // Stricly speaking, as per http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html, we should be sending back // Requested Range Not Satisfiable (416) here. However, it appears that at least recent implementations diff --git a/OpenSim/Data/IRegionData.cs b/OpenSim/Data/IRegionData.cs index 546b5e8cba..70e10653de 100644 --- a/OpenSim/Data/IRegionData.cs +++ b/OpenSim/Data/IRegionData.cs @@ -85,21 +85,6 @@ namespace OpenSim.Data List GetHyperlinks(UUID scopeID); } - [Flags] - public enum RegionFlags : int - { - DefaultRegion = 1, // Used for new Rez. Random if multiple defined - FallbackRegion = 2, // Regions we redirect to when the destination is down - RegionOnline = 4, // Set when a region comes online, unset when it unregisters and DeleteOnUnregister is false - NoDirectLogin = 8, // Region unavailable for direct logins (by name) - Persistent = 16, // Don't remove on unregister - LockedOut = 32, // Don't allow registration - NoMove = 64, // Don't allow moving this region - Reservation = 128, // This is an inactive reservation - Authenticate = 256, // Require authentication - Hyperlink = 512 // Record represents a HG link - } - public class RegionDataDistanceCompare : IComparer { private Vector2 m_origin; diff --git a/OpenSim/Data/MSSQL/MSSQLRegionData.cs b/OpenSim/Data/MSSQL/MSSQLRegionData.cs index 3ae87c3804..0d89706566 100644 --- a/OpenSim/Data/MSSQL/MSSQLRegionData.cs +++ b/OpenSim/Data/MSSQL/MSSQLRegionData.cs @@ -37,6 +37,7 @@ using OpenMetaverse; using OpenSim.Framework; using OpenSim.Region.Framework.Interfaces; using OpenSim.Region.Framework.Scenes; +using RegionFlags = OpenSim.Framework.RegionFlags; namespace OpenSim.Data.MSSQL { diff --git a/OpenSim/Data/MySQL/MySQLRegionData.cs b/OpenSim/Data/MySQL/MySQLRegionData.cs index 0614879061..a2d4ae4647 100644 --- a/OpenSim/Data/MySQL/MySQLRegionData.cs +++ b/OpenSim/Data/MySQL/MySQLRegionData.cs @@ -30,11 +30,11 @@ using System.Collections; using System.Collections.Generic; using System.Data; using System.Reflection; - +using MySql.Data.MySqlClient; using OpenMetaverse; using OpenSim.Framework; using OpenSim.Data; -using MySql.Data.MySqlClient; +using RegionFlags = OpenSim.Framework.RegionFlags; namespace OpenSim.Data.MySQL { diff --git a/OpenSim/Data/Null/NullRegionData.cs b/OpenSim/Data/Null/NullRegionData.cs index deb50cb4df..b4d701af50 100644 --- a/OpenSim/Data/Null/NullRegionData.cs +++ b/OpenSim/Data/Null/NullRegionData.cs @@ -33,6 +33,7 @@ using OpenSim.Framework; using OpenSim.Data; using System.Reflection; using log4net; +using RegionFlags = OpenSim.Framework.RegionFlags; namespace OpenSim.Data.Null { diff --git a/OpenSim/Framework/Console/ConsoleUtil.cs b/OpenSim/Framework/Console/ConsoleUtil.cs new file mode 100644 index 0000000000..a7cf0c03b2 --- /dev/null +++ b/OpenSim/Framework/Console/ConsoleUtil.cs @@ -0,0 +1,114 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using log4net; +using OpenMetaverse; + +public class ConsoleUtil +{ +// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + public const string MinRawConsoleVectorValue = "-~"; + public const string MaxRawConsoleVectorValue = "~"; + + public const string VectorSeparator = ","; + public static char[] VectorSeparatorChars = VectorSeparator.ToCharArray(); + + /// + /// Convert a minimum vector input from the console to an OpenMetaverse.Vector3 + /// + /// /param> + /// + /// + public static bool TryParseConsoleMinVector(string rawConsoleVector, out Vector3 vector) + { + return TryParseConsoleVector(rawConsoleVector, c => float.MinValue.ToString(), out vector); + } + + /// + /// Convert a maximum vector input from the console to an OpenMetaverse.Vector3 + /// + /// /param> + /// + /// + public static bool TryParseConsoleMaxVector(string rawConsoleVector, out Vector3 vector) + { + return TryParseConsoleVector(rawConsoleVector, c => float.MaxValue.ToString(), out vector); + } + + /// + /// Convert a vector input from the console to an OpenMetaverse.Vector3 + /// + /// + /// A string in the form ,, where there is no space between values. + /// Any component can be missing (e.g. ,,40). blankComponentFunc is invoked to replace the blank with a suitable value + /// Also, if the blank component is at the end, then the comma can be missed off entirely (e.g. 40,30 or 40) + /// The strings "~" and "-~" are valid in components. The first substitutes float.MaxValue whilst the second is float.MinValue + /// Other than that, component values must be numeric. + /// + /// + /// + /// + public static bool TryParseConsoleVector( + string rawConsoleVector, Func blankComponentFunc, out Vector3 vector) + { + List components = rawConsoleVector.Split(VectorSeparatorChars).ToList(); + + if (components.Count < 1 || components.Count > 3) + { + vector = Vector3.Zero; + return false; + } + + for (int i = components.Count; i < 3; i++) + components.Add(""); + + List semiDigestedComponents + = components.ConvertAll( + c => + { + if (c == "") + return blankComponentFunc.Invoke(c); + else if (c == MaxRawConsoleVectorValue) + return float.MaxValue.ToString(); + else if (c == MinRawConsoleVectorValue) + return float.MinValue.ToString(); + else + return c; + }); + + string semiDigestedConsoleVector = string.Join(VectorSeparator, semiDigestedComponents.ToArray()); + +// m_log.DebugFormat("[CONSOLE UTIL]: Parsing {0} into OpenMetaverse.Vector3", semiDigestedConsoleVector); + + return Vector3.TryParse(semiDigestedConsoleVector, out vector); + } +} \ No newline at end of file diff --git a/OpenSim/Framework/GridInstantMessage.cs b/OpenSim/Framework/GridInstantMessage.cs index a6bf6e3c32..6ae0488fc2 100644 --- a/OpenSim/Framework/GridInstantMessage.cs +++ b/OpenSim/Framework/GridInstantMessage.cs @@ -44,7 +44,6 @@ namespace OpenSim.Framework public Vector3 Position; public byte[] binaryBucket; - public uint ParentEstateID; public Guid RegionID; public uint timestamp; @@ -58,7 +57,7 @@ namespace OpenSim.Framework string _fromAgentName, UUID _toAgentID, byte _dialog, bool _fromGroup, string _message, UUID _imSessionID, bool _offline, Vector3 _position, - byte[] _binaryBucket) + byte[] _binaryBucket, bool addTimestamp) { fromAgentID = _fromAgentID.Guid; fromAgentName = _fromAgentName; @@ -79,7 +78,9 @@ namespace OpenSim.Framework ParentEstateID = scene.RegionInfo.EstateSettings.ParentEstateID; RegionID = scene.RegionInfo.RegionSettings.RegionUUID.Guid; } - timestamp = (uint)Util.UnixTimeSinceEpoch(); + + if (addTimestamp) + timestamp = (uint)Util.UnixTimeSinceEpoch(); } public GridInstantMessage(IScene scene, UUID _fromAgentID, @@ -87,7 +88,7 @@ namespace OpenSim.Framework string _message, bool _offline, Vector3 _position) : this(scene, _fromAgentID, _fromAgentName, _toAgentID, _dialog, false, _message, - _fromAgentID ^ _toAgentID, _offline, _position, new byte[0]) + _fromAgentID ^ _toAgentID, _offline, _position, new byte[0], true) { } } diff --git a/OpenSim/Framework/IClientAPI.cs b/OpenSim/Framework/IClientAPI.cs index 8a63bff9f5..9856978cfd 100644 --- a/OpenSim/Framework/IClientAPI.cs +++ b/OpenSim/Framework/IClientAPI.cs @@ -805,8 +805,23 @@ namespace OpenSim.Framework event Action OnRegionHandShakeReply; event GenericCall1 OnRequestWearables; event Action OnCompleteMovementToRegion; + + /// + /// Called when an AgentUpdate message is received and before OnAgentUpdate. + /// + /// + /// Listeners must not retain a reference to AgentUpdateArgs since this object may be reused for subsequent AgentUpdates. + /// event UpdateAgent OnPreAgentUpdate; + + /// + /// Called when an AgentUpdate message is received and after OnPreAgentUpdate. + /// + /// + /// Listeners must not retain a reference to AgentUpdateArgs since this object may be reused for subsequent AgentUpdates. + /// event UpdateAgent OnAgentUpdate; + event AgentRequestSit OnAgentRequestSit; event AgentSit OnAgentSit; event AvatarPickerRequest OnAvatarPickerRequest; diff --git a/OpenSim/Framework/Monitoring/BaseStatsCollector.cs b/OpenSim/Framework/Monitoring/BaseStatsCollector.cs index 57a63ef4f2..2903b6e7fe 100644 --- a/OpenSim/Framework/Monitoring/BaseStatsCollector.cs +++ b/OpenSim/Framework/Monitoring/BaseStatsCollector.cs @@ -49,7 +49,11 @@ namespace OpenSim.Framework.Monitoring Math.Round(GC.GetTotalMemory(false) / 1024.0 / 1024.0)); sb.AppendFormat( - "OpenSim object memory churn : {0} MB/s\n", + "OpenSim last object memory churn : {0} MB/s\n", + Math.Round((MemoryWatchdog.LastMemoryChurn * 1000) / 1024.0 / 1024, 3)); + + sb.AppendFormat( + "OpenSim average object memory churn : {0} MB/s\n", Math.Round((MemoryWatchdog.AverageMemoryChurn * 1000) / 1024.0 / 1024, 3)); sb.AppendFormat( diff --git a/OpenSim/Framework/Monitoring/MemoryWatchdog.cs b/OpenSim/Framework/Monitoring/MemoryWatchdog.cs index a23cf1fea8..c6010cd092 100644 --- a/OpenSim/Framework/Monitoring/MemoryWatchdog.cs +++ b/OpenSim/Framework/Monitoring/MemoryWatchdog.cs @@ -60,13 +60,21 @@ namespace OpenSim.Framework.Monitoring private static bool m_enabled; /// - /// Average memory churn in bytes per millisecond. + /// Last memory churn in bytes per millisecond. /// public static double AverageMemoryChurn { get { if (m_samples.Count > 0) return m_samples.Average(); else return 0; } } + /// + /// Average memory churn in bytes per millisecond. + /// + public static double LastMemoryChurn + { + get { if (m_samples.Count > 0) return m_samples.Last(); else return 0; } + } + /// /// Maximum number of statistical samples. /// diff --git a/OpenSim/Framework/Monitoring/SimExtraStatsCollector.cs b/OpenSim/Framework/Monitoring/SimExtraStatsCollector.cs index cdd7cc711e..aa862027b8 100644 --- a/OpenSim/Framework/Monitoring/SimExtraStatsCollector.cs +++ b/OpenSim/Framework/Monitoring/SimExtraStatsCollector.cs @@ -355,10 +355,25 @@ Asset service request failures: {3}" + Environment.NewLine, sb.Append(Environment.NewLine); sb.Append( string.Format( - "{0,6:0} {1,6:0} {2,6:0} {3,6:0} {4,6:0} {5,6:0.0} {6,6:0.0} {7,6:0.0} {8,6:0.0} {9,6:0.0} {10,6:0.0}", + "{0,6:0} {1,6:0} {2,6:0} {3,6:0} {4,6:0} {5,6:0.0} {6,6:0.0} {7,6:0.0} {8,6:0.0} {9,6:0.0} {10,6:0.0}\n\n", inPacketsPerSecond, outPacketsPerSecond, pendingDownloads, pendingUploads, unackedBytes, totalFrameTime, netFrameTime, physicsFrameTime, otherFrameTime, agentFrameTime, imageFrameTime)); - sb.Append(Environment.NewLine); + + Dictionary> sceneStats; + + if (StatsManager.TryGetStats("scene", out sceneStats)) + { + foreach (KeyValuePair> kvp in sceneStats) + { + foreach (Stat stat in kvp.Value.Values) + { + if (stat.Verbosity == StatVerbosity.Info) + { + sb.AppendFormat("{0} ({1}): {2}{3}\n", stat.Name, stat.Container, stat.Value, stat.UnitName); + } + } + } + } /* sb.Append(Environment.NewLine); diff --git a/OpenSim/Framework/Monitoring/StatsManager.cs b/OpenSim/Framework/Monitoring/StatsManager.cs index d78fa6a1f0..31989e50ad 100644 --- a/OpenSim/Framework/Monitoring/StatsManager.cs +++ b/OpenSim/Framework/Monitoring/StatsManager.cs @@ -25,6 +25,9 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +using System; +using System.Collections.Generic; + namespace OpenSim.Framework.Monitoring { /// @@ -32,6 +35,24 @@ namespace OpenSim.Framework.Monitoring /// public class StatsManager { + // Subcommand used to list other stats. + public const string AllSubCommand = "all"; + + // Subcommand used to list other stats. + public const string ListSubCommand = "list"; + + // All subcommands + public static HashSet SubCommands = new HashSet { AllSubCommand, ListSubCommand }; + + /// + /// Registered stats categorized by category/container/shortname + /// + /// + /// Do not add or remove directly from this dictionary. + /// + public static Dictionary>> RegisteredStats + = new Dictionary>>(); + private static AssetStatsCollector assetStats; private static UserStatsCollector userStats; private static SimExtraStatsCollector simExtraStats = new SimExtraStatsCollector(); @@ -40,6 +61,75 @@ namespace OpenSim.Framework.Monitoring public static UserStatsCollector UserStats { get { return userStats; } } public static SimExtraStatsCollector SimExtraStats { get { return simExtraStats; } } + public static void RegisterConsoleCommands(ICommandConsole console) + { + console.Commands.AddCommand( + "General", + false, + "show stats", + "show stats [list|all|]", + "Show statistical information for this server", + "If no final argument is specified then legacy statistics information is currently shown.\n" + + "If list is specified then statistic categories are shown.\n" + + "If all is specified then all registered statistics are shown.\n" + + "If a category name is specified then only statistics from that category are shown.\n" + + "THIS STATS FACILITY IS EXPERIMENTAL AND DOES NOT YET CONTAIN ALL STATS", + HandleShowStatsCommand); + } + + public static void HandleShowStatsCommand(string module, string[] cmd) + { + ICommandConsole con = MainConsole.Instance; + + if (cmd.Length > 2) + { + var categoryName = cmd[2]; + + if (categoryName == AllSubCommand) + { + foreach (var category in RegisteredStats.Values) + { + OutputCategoryStatsToConsole(con, category); + } + } + else if (categoryName == ListSubCommand) + { + con.Output("Statistic categories available are:"); + foreach (string category in RegisteredStats.Keys) + con.OutputFormat(" {0}", category); + } + else + { + Dictionary> category; + if (!RegisteredStats.TryGetValue(categoryName, out category)) + { + con.OutputFormat("No such category as {0}", categoryName); + } + else + { + OutputCategoryStatsToConsole(con, category); + } + } + } + else + { + // Legacy + con.Output(SimExtraStats.Report()); + } + } + + private static void OutputCategoryStatsToConsole( + ICommandConsole con, Dictionary> category) + { + foreach (var container in category.Values) + { + foreach (Stat stat in container.Values) + { + con.Output(stat.ToConsoleString()); + } + } + } + /// /// Start collecting statistics related to assets. /// Should only be called once. @@ -61,5 +151,224 @@ namespace OpenSim.Framework.Monitoring return userStats; } + + /// + /// Registers a statistic. + /// + /// + /// + public static bool RegisterStat(Stat stat) + { + Dictionary> category = null, newCategory; + Dictionary container = null, newContainer; + + lock (RegisteredStats) + { + // Stat name is not unique across category/container/shortname key. + // XXX: For now just return false. This is to avoid problems in regression tests where all tests + // in a class are run in the same instance of the VM. + if (TryGetStat(stat, out category, out container)) + return false; + + // We take a copy-on-write approach here of replacing dictionaries when keys are added or removed. + // This means that we don't need to lock or copy them on iteration, which will be a much more + // common operation after startup. + if (container != null) + newContainer = new Dictionary(container); + else + newContainer = new Dictionary(); + + if (category != null) + newCategory = new Dictionary>(category); + else + newCategory = new Dictionary>(); + + newContainer[stat.ShortName] = stat; + newCategory[stat.Container] = newContainer; + RegisteredStats[stat.Category] = newCategory; + } + + return true; + } + + /// + /// Deregister a statistic + /// > + /// + /// > category = null, newCategory; + Dictionary container = null, newContainer; + + lock (RegisteredStats) + { + if (!TryGetStat(stat, out category, out container)) + return false; + + newContainer = new Dictionary(container); + newContainer.Remove(stat.UniqueName); + + newCategory = new Dictionary>(category); + newCategory.Remove(stat.Container); + + newCategory[stat.Container] = newContainer; + RegisteredStats[stat.Category] = newCategory; + + return true; + } + } + + public static bool TryGetStats(string category, out Dictionary> stats) + { + return RegisteredStats.TryGetValue(category, out stats); + } + + public static bool TryGetStat( + Stat stat, + out Dictionary> category, + out Dictionary container) + { + category = null; + container = null; + + lock (RegisteredStats) + { + if (RegisteredStats.TryGetValue(stat.Category, out category)) + { + if (category.TryGetValue(stat.Container, out container)) + { + if (container.ContainsKey(stat.ShortName)) + return true; + } + } + } + + return false; + } + } + + /// + /// Verbosity of stat. + /// + /// + /// Info will always be displayed. + /// + public enum StatVerbosity + { + Debug, + Info + } + + /// + /// Holds individual static details + /// + public class Stat + { + /// + /// Unique stat name used for indexing. Each ShortName in a Category must be unique. + /// + public string UniqueName { get; private set; } + + /// + /// Category of this stat (e.g. cache, scene, etc). + /// + public string Category { get; private set; } + + /// + /// Containing name for this stat. + /// FIXME: In the case of a scene, this is currently the scene name (though this leaves + /// us with a to-be-resolved problem of non-unique region names). + /// + /// + /// The container. + /// + public string Container { get; private set; } + + public StatVerbosity Verbosity { get; private set; } + public string ShortName { get; private set; } + public string Name { get; private set; } + public string Description { get; private set; } + public virtual string UnitName { get; private set; } + + public virtual double Value { get; set; } + + /// + /// Constructor + /// + /// Short name for the stat. Must not contain spaces. e.g. "LongFrames" + /// Human readable name for the stat. e.g. "Long frames" + /// + /// Unit name for the stat. Should be preceeded by a space if the unit name isn't normally appeneded immediately to the value. + /// e.g. " frames" + /// + /// Category under which this stat should appear, e.g. "scene". Do not capitalize. + /// Entity to which this stat relates. e.g. scene name if this is a per scene stat. + /// Verbosity of stat. Controls whether it will appear in short stat display or only full display. + /// Description of stat + public Stat( + string shortName, string name, string unitName, string category, string container, StatVerbosity verbosity, string description) + { + if (StatsManager.SubCommands.Contains(category)) + throw new Exception( + string.Format("Stat cannot be in category '{0}' since this is reserved for a subcommand", category)); + + ShortName = shortName; + Name = name; + UnitName = unitName; + Category = category; + Container = container; + Verbosity = verbosity; + Description = description; + + UniqueName = GenUniqueName(Container, Category, ShortName); + } + + public static string GenUniqueName(string container, string category, string shortName) + { + return string.Format("{0}+{1}+{2}", container, category, shortName); + } + + public virtual string ToConsoleString() + { + return string.Format( + "{0}.{1}.{2} : {3}{4}", Category, Container, ShortName, Value, UnitName); + } + } + + public class PercentageStat : Stat + { + public int Antecedent { get; set; } + public int Consequent { get; set; } + + public override double Value + { + get + { + int c = Consequent; + + // Avoid any chance of a multi-threaded divide-by-zero + if (c == 0) + return 0; + + return (double)Antecedent / c * 100; + } + + set + { + throw new Exception("Cannot set value on a PercentageStat"); + } + } + + public PercentageStat( + string shortName, string name, string category, string container, StatVerbosity verbosity, string description) + : base(shortName, name, "%", category, container, verbosity, description) {} + + public override string ToConsoleString() + { + return string.Format( + "{0}.{1}.{2} : {3:0.##}{4} ({5}/{6})", + Category, Container, ShortName, Value, UnitName, Antecedent, Consequent); + } } } \ No newline at end of file diff --git a/OpenSim/Framework/Monitoring/Watchdog.cs b/OpenSim/Framework/Monitoring/Watchdog.cs index 7964f28944..a20326dbd8 100644 --- a/OpenSim/Framework/Monitoring/Watchdog.cs +++ b/OpenSim/Framework/Monitoring/Watchdog.cs @@ -231,7 +231,25 @@ namespace OpenSim.Framework.Monitoring private static bool RemoveThread(int threadID) { lock (m_threads) - return m_threads.Remove(threadID); + { + ThreadWatchdogInfo twi; + if (m_threads.TryGetValue(threadID, out twi)) + { + m_log.DebugFormat( + "[WATCHDOG]: Removing thread {0}, ID {1}", twi.Thread.Name, twi.Thread.ManagedThreadId); + + m_threads.Remove(threadID); + + return true; + } + else + { + m_log.WarnFormat( + "[WATCHDOG]: Requested to remove thread with ID {0} but this is not being monitored", threadID); + + return false; + } + } } public static bool AbortThread(int threadID) diff --git a/OpenSim/Framework/Pool.cs b/OpenSim/Framework/Pool.cs new file mode 100644 index 0000000000..1ca06c3065 --- /dev/null +++ b/OpenSim/Framework/Pool.cs @@ -0,0 +1,76 @@ +/* + * 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; + +namespace OpenSim.Framework +{ + /// + /// Naive pool implementation. + /// + /// + /// Currently assumes that objects are in a useable state when returned. + /// + public class Pool + { + private Stack m_pool; + + private int m_maxPoolSize; + + private Func m_createFunction; + + public Pool(Func createFunction, int maxSize) + { + m_maxPoolSize = maxSize; + m_createFunction = createFunction; + m_pool = new Stack(m_maxPoolSize); + } + + public T GetObject() + { + lock (m_pool) + { + if (m_pool.Count > 0) + return m_pool.Pop(); + else + return m_createFunction(); + } + } + + public void ReturnObject(T obj) + { + lock (m_pool) + { + if (m_pool.Count >= m_maxPoolSize) + return; + else + m_pool.Push(obj); + } + } + } +} \ No newline at end of file diff --git a/OpenSim/Framework/RegionFlags.cs b/OpenSim/Framework/RegionFlags.cs new file mode 100644 index 0000000000..a3089b077d --- /dev/null +++ b/OpenSim/Framework/RegionFlags.cs @@ -0,0 +1,53 @@ +/* + * 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.Framework +{ + /// + /// Region flags used internally by OpenSimulator to store installation specific information about regions. + /// + /// + /// Don't confuse with OpenMetaverse.RegionFlags which are client facing flags (i.e. they go over the wire). + /// Returned by IGridService.GetRegionFlags() + /// + [Flags] + public enum RegionFlags : int + { + DefaultRegion = 1, // Used for new Rez. Random if multiple defined + FallbackRegion = 2, // Regions we redirect to when the destination is down + RegionOnline = 4, // Set when a region comes online, unset when it unregisters and DeleteOnUnregister is false + NoDirectLogin = 8, // Region unavailable for direct logins (by name) + Persistent = 16, // Don't remove on unregister + LockedOut = 32, // Don't allow registration + NoMove = 64, // Don't allow moving this region + Reservation = 128, // This is an inactive reservation + Authenticate = 256, // Require authentication + Hyperlink = 512 // Record represents a HG link + } +} \ No newline at end of file diff --git a/OpenSim/Framework/Servers/BaseOpenSimServer.cs b/OpenSim/Framework/Servers/BaseOpenSimServer.cs index 7a5c16dd20..aac9c45fb7 100644 --- a/OpenSim/Framework/Servers/BaseOpenSimServer.cs +++ b/OpenSim/Framework/Servers/BaseOpenSimServer.cs @@ -96,11 +96,6 @@ namespace OpenSim.Framework.Servers get { return m_httpServer; } } - /// - /// Holds the non-viewer statistics collection object for this service/server - /// - protected IStatsCollector m_stats; - public BaseOpenSimServer() { m_startuptime = DateTime.Now; @@ -177,10 +172,6 @@ namespace OpenSim.Framework.Servers "show info", "Show general information about the server", HandleShow); - m_console.Commands.AddCommand("General", false, "show stats", - "show stats", - "Show statistics", HandleShow); - m_console.Commands.AddCommand("General", false, "show threads", "show threads", "Show thread status", HandleShow); @@ -226,12 +217,7 @@ namespace OpenSim.Framework.Servers { StringBuilder sb = new StringBuilder("DIAGNOSTICS\n\n"); sb.Append(GetUptimeReport()); - - if (m_stats != null) - { - sb.Append(m_stats.Report()); - } - + sb.Append(StatsManager.SimExtraStats.Report()); sb.Append(Environment.NewLine); sb.Append(GetThreadsReport()); @@ -382,10 +368,6 @@ namespace OpenSim.Framework.Servers { Notice("set log level [level] - change the console logging level only. For example, off or debug."); Notice("show info - show server information (e.g. startup path)."); - - if (m_stats != null) - Notice("show stats - show statistical information for this server"); - Notice("show threads - list tracked threads"); Notice("show uptime - show server startup time and uptime."); Notice("show version - show server version."); @@ -409,11 +391,6 @@ namespace OpenSim.Framework.Servers ShowInfo(); break; - case "stats": - if (m_stats != null) - Notice(m_stats.Report()); - break; - case "threads": Notice(GetThreadsReport()); break; @@ -604,8 +581,7 @@ namespace OpenSim.Framework.Servers public string osSecret { // Secret uuid for the simulator - get { return m_osSecret; } - + get { return m_osSecret; } } public string StatReport(IOSHttpRequest httpRequest) @@ -613,11 +589,11 @@ namespace OpenSim.Framework.Servers // If we catch a request for "callback", wrap the response in the value for jsonp if (httpRequest.Query.ContainsKey("callback")) { - return httpRequest.Query["callback"].ToString() + "(" + m_stats.XReport((DateTime.Now - m_startuptime).ToString() , m_version) + ");"; + return httpRequest.Query["callback"].ToString() + "(" + StatsManager.SimExtraStats.XReport((DateTime.Now - m_startuptime).ToString() , m_version) + ");"; } else { - return m_stats.XReport((DateTime.Now - m_startuptime).ToString() , m_version); + return StatsManager.SimExtraStats.XReport((DateTime.Now - m_startuptime).ToString() , m_version); } } diff --git a/OpenSim/Framework/Servers/HttpServer/BaseHttpServer.cs b/OpenSim/Framework/Servers/HttpServer/BaseHttpServer.cs index d5bc3c3e4d..b018e57c45 100644 --- a/OpenSim/Framework/Servers/HttpServer/BaseHttpServer.cs +++ b/OpenSim/Framework/Servers/HttpServer/BaseHttpServer.cs @@ -648,7 +648,7 @@ namespace OpenSim.Framework.Servers.HttpServer // Every month or so this will wrap and give bad numbers, not really a problem // since its just for reporting int tickdiff = requestEndTick - requestStartTick; - if (tickdiff > 3000 && requestHandler.Name != "GetTexture") + if (tickdiff > 3000 && requestHandler != null && requestHandler.Name != "GetTexture") { m_log.InfoFormat( "[BASE HTTP SERVER]: Slow handling of {0} {1} {2} {3} {4} from {5} took {6}ms", diff --git a/OpenSim/Framework/Util.cs b/OpenSim/Framework/Util.cs index 1b9777f10b..5c7797a95e 100644 --- a/OpenSim/Framework/Util.cs +++ b/OpenSim/Framework/Util.cs @@ -533,6 +533,19 @@ namespace OpenSim.Framework return (x + y - (min >> 1) - (min >> 2) + (min >> 4)); } + /// + /// Determines whether a point is inside a bounding box. + /// + /// /param> + /// + /// + /// + public static bool IsInsideBox(Vector3 v, Vector3 min, Vector3 max) + { + return v.X >= min.X & v.Y >= min.Y && v.Z >= min.Z + && v.X <= max.X && v.Y <= max.Y && v.Z <= max.Z; + } + /// /// Are the co-ordinates of the new region visible from the old region? /// diff --git a/OpenSim/Region/Application/OpenSim.cs b/OpenSim/Region/Application/OpenSim.cs index c3c612fb05..b24641a454 100644 --- a/OpenSim/Region/Application/OpenSim.cs +++ b/OpenSim/Region/Application/OpenSim.cs @@ -254,8 +254,14 @@ namespace OpenSim m_console.Commands.AddCommand("Debug", false, "debug teleport", "debug teleport", "Toggle teleport route debugging", Debug); m_console.Commands.AddCommand("Debug", false, "debug scene", - "debug scene ", - "Turn on scene debugging", Debug); + "debug scene active|collisions|physics|scripting|teleport true|false", + "Turn on scene debugging.", + "If active is false then main scene update and maintenance loops are suspended.\n" + + "If collisions is false then collisions with other objects are turned off.\n" + + "If physics is false then all physics objects are non-physical.\n" + + "If scripting is false then no scripting operations happen.\n" + + "If teleport is true then some extra teleport debug information is logged.", + Debug); m_console.Commands.AddCommand("General", false, "change region", "change region ", @@ -930,7 +936,8 @@ namespace OpenSim } else { - MainConsole.Instance.Output("Usage: debug scene scripting|collisions|physics|teleport true|false"); + MainConsole.Instance.Output( + "Usage: debug scene active|scripting|collisions|physics|teleport true|false"); } break; diff --git a/OpenSim/Region/Application/OpenSimBase.cs b/OpenSim/Region/Application/OpenSimBase.cs index f78439860a..4f1b4392ad 100644 --- a/OpenSim/Region/Application/OpenSimBase.cs +++ b/OpenSim/Region/Application/OpenSimBase.cs @@ -223,8 +223,6 @@ namespace OpenSim base.StartupSpecific(); - m_stats = StatsManager.SimExtraStats; - // Create a ModuleLoader instance m_moduleLoader = new ModuleLoader(m_config.Source); @@ -234,51 +232,51 @@ namespace OpenSim plugin.PostInitialise(); } - AddPluginCommands(); - } - - protected virtual void AddPluginCommands() - { - // If console exists add plugin commands. if (m_console != null) { - List topics = GetHelpTopics(); + StatsManager.RegisterConsoleCommands(m_console); + AddPluginCommands(m_console); + } + } - foreach (string topic in topics) + protected virtual void AddPluginCommands(CommandConsole console) + { + List topics = GetHelpTopics(); + + foreach (string topic in topics) + { + string capitalizedTopic = char.ToUpper(topic[0]) + topic.Substring(1); + + // This is a hack to allow the user to enter the help command in upper or lowercase. This will go + // away at some point. + console.Commands.AddCommand(capitalizedTopic, false, "help " + topic, + "help " + capitalizedTopic, + "Get help on plugin command '" + topic + "'", + HandleCommanderHelp); + console.Commands.AddCommand(capitalizedTopic, false, "help " + capitalizedTopic, + "help " + capitalizedTopic, + "Get help on plugin command '" + topic + "'", + HandleCommanderHelp); + + ICommander commander = null; + + Scene s = SceneManager.CurrentOrFirstScene; + + if (s != null && s.GetCommanders() != null) { - string capitalizedTopic = char.ToUpper(topic[0]) + topic.Substring(1); + if (s.GetCommanders().ContainsKey(topic)) + commander = s.GetCommanders()[topic]; + } - // This is a hack to allow the user to enter the help command in upper or lowercase. This will go - // away at some point. - m_console.Commands.AddCommand(capitalizedTopic, false, "help " + topic, - "help " + capitalizedTopic, - "Get help on plugin command '" + topic + "'", - HandleCommanderHelp); - m_console.Commands.AddCommand(capitalizedTopic, false, "help " + capitalizedTopic, - "help " + capitalizedTopic, - "Get help on plugin command '" + topic + "'", - HandleCommanderHelp); + if (commander == null) + continue; - ICommander commander = null; - - Scene s = SceneManager.CurrentOrFirstScene; - - if (s != null && s.GetCommanders() != null) - { - if (s.GetCommanders().ContainsKey(topic)) - commander = s.GetCommanders()[topic]; - } - - if (commander == null) - continue; - - foreach (string command in commander.Commands.Keys) - { - m_console.Commands.AddCommand(capitalizedTopic, false, - topic + " " + command, - topic + " " + commander.Commands[command].ShortHelp(), - String.Empty, HandleCommanderCommand); - } + foreach (string command in commander.Commands.Keys) + { + console.Commands.AddCommand(capitalizedTopic, false, + topic + " " + command, + topic + " " + commander.Commands[command].ShortHelp(), + String.Empty, HandleCommanderCommand); } } } diff --git a/OpenSim/Region/ClientStack/Linden/Caps/BunchOfCaps/BunchOfCaps.cs b/OpenSim/Region/ClientStack/Linden/Caps/BunchOfCaps/BunchOfCaps.cs index 185f9ce969..cc69645f4b 100644 --- a/OpenSim/Region/ClientStack/Linden/Caps/BunchOfCaps/BunchOfCaps.cs +++ b/OpenSim/Region/ClientStack/Linden/Caps/BunchOfCaps/BunchOfCaps.cs @@ -163,8 +163,8 @@ namespace OpenSim.Region.ClientStack.Linden m_HostCapsObj.RegisterHandler( "SEED", new RestStreamHandler("POST", capsBase + m_requestPath, SeedCapRequest, "SEED", null)); - m_log.DebugFormat( - "[CAPS]: Registered seed capability {0} for {1}", capsBase + m_requestPath, m_HostCapsObj.AgentID); +// m_log.DebugFormat( +// "[CAPS]: Registered seed capability {0} for {1}", capsBase + m_requestPath, m_HostCapsObj.AgentID); //m_capsHandlers["MapLayer"] = // new LLSDStreamhandler("POST", @@ -254,11 +254,12 @@ namespace OpenSim.Region.ClientStack.Linden public string SeedCapRequest(string request, string path, string param, IOSHttpRequest httpRequest, IOSHttpResponse httpResponse) { -// m_log.Debug("[CAPS]: Seed Caps Request in region: " + m_regionName); + m_log.DebugFormat( + "[CAPS]: Received SEED caps request in {0} for agent {1}", m_regionName, m_HostCapsObj.AgentID); if (!m_Scene.CheckClient(m_HostCapsObj.AgentID, httpRequest.RemoteIPEndPoint)) { - m_log.DebugFormat( + m_log.WarnFormat( "[CAPS]: Unauthorized CAPS client {0} from {1}", m_HostCapsObj.AgentID, httpRequest.RemoteIPEndPoint); diff --git a/OpenSim/Region/ClientStack/Linden/Caps/EventQueue/EventQueueGetModule.cs b/OpenSim/Region/ClientStack/Linden/Caps/EventQueue/EventQueueGetModule.cs index 594b229f20..47cb04953f 100644 --- a/OpenSim/Region/ClientStack/Linden/Caps/EventQueue/EventQueueGetModule.cs +++ b/OpenSim/Region/ClientStack/Linden/Caps/EventQueue/EventQueueGetModule.cs @@ -94,7 +94,7 @@ namespace OpenSim.Region.ClientStack.Linden //scene.CommsManager.HttpServer.AddLLSDHandler("/CAPS/EQG/", EventQueueFallBack); - scene.EventManager.OnNewClient += OnNewClient; +// scene.EventManager.OnNewClient += OnNewClient; // TODO: Leaving these open, or closing them when we // become a child is incorrect. It messes up TP in a big @@ -102,6 +102,7 @@ namespace OpenSim.Region.ClientStack.Linden // circuit is there. scene.EventManager.OnClientClosed += ClientClosed; + scene.EventManager.OnMakeChildAgent += MakeChildAgent; scene.EventManager.OnRegisterCaps += OnRegisterCaps; @@ -110,10 +111,10 @@ namespace OpenSim.Region.ClientStack.Linden false, "debug eq", "debug eq [0|1|2]", - "Turn on event queue debugging" - + "<= 0 - turns off all event queue logging" - + ">= 1 - turns on outgoing event logging" - + ">= 2 - turns on poll notification", + "Turn on event queue debugging\n" + + " <= 0 - turns off all event queue logging\n" + + " >= 1 - turns on outgoing event logging\n" + + " >= 2 - turns on poll notification", HandleDebugEq); } else @@ -226,16 +227,6 @@ namespace OpenSim.Region.ClientStack.Linden #endregion - private void OnNewClient(IClientAPI client) - { - //client.OnLogout += ClientClosed; - } - -// private void ClientClosed(IClientAPI client) -// { -// ClientClosed(client.AgentId); -// } - private void ClientClosed(UUID agentID, Scene scene) { // m_log.DebugFormat("[EVENTQUEUE]: Closed client {0} in region {1}", agentID, m_scene.RegionInfo.RegionName); diff --git a/OpenSim/Region/ClientStack/Linden/Caps/RegionConsoleModule.cs b/OpenSim/Region/ClientStack/Linden/Caps/RegionConsoleModule.cs index 36af55f0f3..17c7270479 100644 --- a/OpenSim/Region/ClientStack/Linden/Caps/RegionConsoleModule.cs +++ b/OpenSim/Region/ClientStack/Linden/Caps/RegionConsoleModule.cs @@ -107,7 +107,7 @@ namespace OpenSim.Region.ClientStack.Linden UUID capID = UUID.Random(); - m_log.DebugFormat("[REGION CONSOLE]: /CAPS/{0} in region {1}", capID, m_scene.RegionInfo.RegionName); +// m_log.DebugFormat("[REGION CONSOLE]: /CAPS/{0} in region {1}", capID, m_scene.RegionInfo.RegionName); caps.RegisterHandler( "SimConsoleAsync", new ConsoleHandler("/CAPS/" + capID + "/", "SimConsoleAsync", agentID, this, m_scene)); diff --git a/OpenSim/Region/ClientStack/Linden/UDP/IncomingPacket.cs b/OpenSim/Region/ClientStack/Linden/UDP/IncomingPacket.cs index 1b8535cacb..e22670b3a9 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/IncomingPacket.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/IncomingPacket.cs @@ -45,7 +45,12 @@ namespace OpenSim.Region.ClientStack.LindenUDP public Packet Packet; /// - /// Default constructor + /// No arg constructor. + /// + public IncomingPacket() {} + + /// + /// Constructor /// /// Reference to the client this packet came from /// Packet data diff --git a/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs b/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs index d05ffea1bd..7427c5947a 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs @@ -47,6 +47,7 @@ using OpenSim.Region.Framework.Scenes; using OpenSim.Services.Interfaces; using Timer = System.Timers.Timer; using AssetLandmark = OpenSim.Framework.AssetLandmark; +using RegionFlags = OpenMetaverse.RegionFlags; using Nini.Config; using System.IO; @@ -346,7 +347,17 @@ namespace OpenSim.Region.ClientStack.LindenUDP private int m_moneyBalance; private int m_animationSequenceNumber = 1; private bool m_SendLogoutPacketWhenClosing = true; - private AgentUpdateArgs lastarg; + + /// + /// We retain a single AgentUpdateArgs so that we can constantly reuse it rather than construct a new one for + /// every single incoming AgentUpdate. Every client sends 10 AgentUpdate UDP messages per second, even if it + /// is doing absolutely nothing. + /// + /// + /// This does mean that agent updates must be processed synchronously, at least for each client, and called methods + /// cannot retain a reference to it outside of that method. + /// + private AgentUpdateArgs m_lastAgentUpdateArgs; protected Dictionary m_packetHandlers = new Dictionary(); protected Dictionary m_genericPacketHandlers = new Dictionary(); //PauPaw:Local Generic Message handlers @@ -3921,7 +3932,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP { List blocks = terseAgentUpdateBlocks.Value; - ImprovedTerseObjectUpdatePacket packet = new ImprovedTerseObjectUpdatePacket(); + ImprovedTerseObjectUpdatePacket packet + = (ImprovedTerseObjectUpdatePacket)PacketPool.Instance.GetPacket(PacketType.ImprovedTerseObjectUpdate); + packet.RegionData.RegionHandle = m_scene.RegionInfo.RegionHandle; packet.RegionData.TimeDilation = timeDilation; packet.ObjectData = new ImprovedTerseObjectUpdatePacket.ObjectDataBlock[blocks.Count]; @@ -3966,7 +3979,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP { List blocks = terseUpdateBlocks.Value; - ImprovedTerseObjectUpdatePacket packet = new ImprovedTerseObjectUpdatePacket(); + ImprovedTerseObjectUpdatePacket packet + = (ImprovedTerseObjectUpdatePacket)PacketPool.Instance.GetPacket( + PacketType.ImprovedTerseObjectUpdate); + packet.RegionData.RegionHandle = m_scene.RegionInfo.RegionHandle; packet.RegionData.TimeDilation = timeDilation; packet.ObjectData = new ImprovedTerseObjectUpdatePacket.ObjectDataBlock[blocks.Count]; @@ -4958,7 +4974,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP Utils.UInt16ToBytes(Utils.FloatToUInt16(angularVelocity.Y, -64.0f, 64.0f), data, pos); pos += 2; Utils.UInt16ToBytes(Utils.FloatToUInt16(angularVelocity.Z, -64.0f, 64.0f), data, pos); pos += 2; - ImprovedTerseObjectUpdatePacket.ObjectDataBlock block = new ImprovedTerseObjectUpdatePacket.ObjectDataBlock(); + ImprovedTerseObjectUpdatePacket.ObjectDataBlock block + = PacketPool.Instance.GetDataBlock(); + block.Data = data; if (textureEntry != null && textureEntry.Length > 0) @@ -5190,7 +5208,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP protected virtual void RegisterLocalPacketHandlers() { AddLocalPacketHandler(PacketType.LogoutRequest, HandleLogout); + + // If AgentUpdate is ever handled asynchronously, then we will also need to construct a new AgentUpdateArgs + // for each AgentUpdate packet. AddLocalPacketHandler(PacketType.AgentUpdate, HandleAgentUpdate, false); + AddLocalPacketHandler(PacketType.ViewerEffect, HandleViewerEffect, false); AddLocalPacketHandler(PacketType.AgentCachedTexture, HandleAgentTextureCached, false); AddLocalPacketHandler(PacketType.MultipleObjectUpdate, HandleMultipleObjUpdate, false); @@ -5417,80 +5439,83 @@ namespace OpenSim.Region.ClientStack.LindenUDP #region Scene/Avatar - private bool HandleAgentUpdate(IClientAPI sener, Packet Pack) + private bool HandleAgentUpdate(IClientAPI sener, Packet packet) { if (OnAgentUpdate != null) { - bool update = false; - AgentUpdatePacket agenUpdate = (AgentUpdatePacket)Pack; + AgentUpdatePacket agentUpdate = (AgentUpdatePacket)packet; #region Packet Session and User Check - if (agenUpdate.AgentData.SessionID != SessionId || agenUpdate.AgentData.AgentID != AgentId) + if (agentUpdate.AgentData.SessionID != SessionId || agentUpdate.AgentData.AgentID != AgentId) + { + PacketPool.Instance.ReturnPacket(packet); return false; + } #endregion - AgentUpdatePacket.AgentDataBlock x = agenUpdate.AgentData; + bool update = false; + AgentUpdatePacket.AgentDataBlock x = agentUpdate.AgentData; - // We can only check when we have something to check - // against. - - if (lastarg != null) + if (m_lastAgentUpdateArgs != null) { + // These should be ordered from most-likely to + // least likely to change. I've made an initial + // guess at that. update = ( - (x.BodyRotation != lastarg.BodyRotation) || - (x.CameraAtAxis != lastarg.CameraAtAxis) || - (x.CameraCenter != lastarg.CameraCenter) || - (x.CameraLeftAxis != lastarg.CameraLeftAxis) || - (x.CameraUpAxis != lastarg.CameraUpAxis) || - (x.ControlFlags != lastarg.ControlFlags) || - (x.Far != lastarg.Far) || - (x.Flags != lastarg.Flags) || - (x.State != lastarg.State) || - (x.HeadRotation != lastarg.HeadRotation) || - (x.SessionID != lastarg.SessionID) || - (x.AgentID != lastarg.AgentID) + (x.BodyRotation != m_lastAgentUpdateArgs.BodyRotation) || + (x.CameraAtAxis != m_lastAgentUpdateArgs.CameraAtAxis) || + (x.CameraCenter != m_lastAgentUpdateArgs.CameraCenter) || + (x.CameraLeftAxis != m_lastAgentUpdateArgs.CameraLeftAxis) || + (x.CameraUpAxis != m_lastAgentUpdateArgs.CameraUpAxis) || + (x.ControlFlags != m_lastAgentUpdateArgs.ControlFlags) || + (x.Far != m_lastAgentUpdateArgs.Far) || + (x.Flags != m_lastAgentUpdateArgs.Flags) || + (x.State != m_lastAgentUpdateArgs.State) || + (x.HeadRotation != m_lastAgentUpdateArgs.HeadRotation) || + (x.SessionID != m_lastAgentUpdateArgs.SessionID) || + (x.AgentID != m_lastAgentUpdateArgs.AgentID) ); } else { + m_lastAgentUpdateArgs = new AgentUpdateArgs(); update = true; } - // These should be ordered from most-likely to - // least likely to change. I've made an initial - // guess at that. - if (update) { // m_log.DebugFormat("[LLCLIENTVIEW]: Triggered AgentUpdate for {0}", sener.Name); - AgentUpdateArgs arg = new AgentUpdateArgs(); - arg.AgentID = x.AgentID; - arg.BodyRotation = x.BodyRotation; - arg.CameraAtAxis = x.CameraAtAxis; - arg.CameraCenter = x.CameraCenter; - arg.CameraLeftAxis = x.CameraLeftAxis; - arg.CameraUpAxis = x.CameraUpAxis; - arg.ControlFlags = x.ControlFlags; - arg.Far = x.Far; - arg.Flags = x.Flags; - arg.HeadRotation = x.HeadRotation; - arg.SessionID = x.SessionID; - arg.State = x.State; + m_lastAgentUpdateArgs.AgentID = x.AgentID; + m_lastAgentUpdateArgs.BodyRotation = x.BodyRotation; + m_lastAgentUpdateArgs.CameraAtAxis = x.CameraAtAxis; + m_lastAgentUpdateArgs.CameraCenter = x.CameraCenter; + m_lastAgentUpdateArgs.CameraLeftAxis = x.CameraLeftAxis; + m_lastAgentUpdateArgs.CameraUpAxis = x.CameraUpAxis; + m_lastAgentUpdateArgs.ControlFlags = x.ControlFlags; + m_lastAgentUpdateArgs.Far = x.Far; + m_lastAgentUpdateArgs.Flags = x.Flags; + m_lastAgentUpdateArgs.HeadRotation = x.HeadRotation; + m_lastAgentUpdateArgs.SessionID = x.SessionID; + m_lastAgentUpdateArgs.State = x.State; + UpdateAgent handlerAgentUpdate = OnAgentUpdate; UpdateAgent handlerPreAgentUpdate = OnPreAgentUpdate; - lastarg = arg; // save this set of arguments for nexttime + if (handlerPreAgentUpdate != null) - OnPreAgentUpdate(this, arg); + OnPreAgentUpdate(this, m_lastAgentUpdateArgs); + if (handlerAgentUpdate != null) - OnAgentUpdate(this, arg); + OnAgentUpdate(this, m_lastAgentUpdateArgs); handlerAgentUpdate = null; handlerPreAgentUpdate = null; } } + PacketPool.Instance.ReturnPacket(packet); + return true; } @@ -5862,7 +5887,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP msgpack.MessageBlock.ID, msgpack.MessageBlock.Offline != 0 ? true : false, msgpack.MessageBlock.Position, - msgpack.MessageBlock.BinaryBucket); + msgpack.MessageBlock.BinaryBucket, + true); handlerInstantMessage(this, im); } @@ -9054,7 +9080,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP } #endregion - switch (Utils.BytesToString(messagePacket.MethodData.Method)) + string method = Utils.BytesToString(messagePacket.MethodData.Method); + + switch (method) { case "getinfo": if (((Scene)m_scene).Permissions.CanIssueEstateCommand(AgentId, false)) @@ -9370,7 +9398,17 @@ namespace OpenSim.Region.ClientStack.LindenUDP return true; default: - m_log.Error("EstateOwnerMessage: Unknown method requested\n" + messagePacket); + m_log.WarnFormat( + "[LLCLIENTVIEW]: EstateOwnerMessage: Unknown method {0} requested for {1} in {2}", + method, Name, Scene.Name); + + for (int i = 0; i < messagePacket.ParamList.Length; i++) + { + EstateOwnerMessagePacket.ParamListBlock block = messagePacket.ParamList[i]; + string data = (string)Utils.BytesToString(block.Parameter); + m_log.DebugFormat("[LLCLIENTVIEW]: Param {0}={1}", i, data); + } + return true; } @@ -11756,7 +11794,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP logPacket = false; if (DebugPacketLevel <= 50 - & (packet.Type == PacketType.ImprovedTerseObjectUpdate || packet.Type == PacketType.ObjectUpdate)) + && (packet.Type == PacketType.ImprovedTerseObjectUpdate || packet.Type == PacketType.ObjectUpdate)) logPacket = false; if (DebugPacketLevel <= 25 && packet.Type == PacketType.ObjectPropertiesFamily) @@ -11830,8 +11868,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP if (!ProcessPacketMethod(packet)) m_log.Warn("[CLIENT]: unhandled packet " + packet.Type); - - PacketPool.Instance.ReturnPacket(packet); } private static PrimitiveBaseShape GetShapeFromAddPacket(ObjectAddPacket addPacket) @@ -12284,7 +12320,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP ushort timeDilation = Utils.FloatToUInt16(TIME_DILATION, 0.0f, 1.0f); - ImprovedTerseObjectUpdatePacket packet = new ImprovedTerseObjectUpdatePacket(); + ImprovedTerseObjectUpdatePacket packet + = (ImprovedTerseObjectUpdatePacket)PacketPool.Instance.GetPacket( + PacketType.ImprovedTerseObjectUpdate); + packet.RegionData.RegionHandle = m_scene.RegionInfo.RegionHandle; packet.RegionData.TimeDilation = timeDilation; packet.ObjectData = new ImprovedTerseObjectUpdatePacket.ObjectDataBlock[1]; diff --git a/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs b/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs index 55780d641c..419de66e7b 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs @@ -37,6 +37,7 @@ using log4net; using Nini.Config; using OpenMetaverse.Packets; using OpenSim.Framework; +using OpenSim.Framework.Console; using OpenSim.Framework.Monitoring; using OpenSim.Region.Framework.Scenes; using OpenMetaverse; @@ -100,9 +101,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// The measured resolution of Environment.TickCount public readonly float TickCountResolution; + /// Number of prim updates to put on the queue each time the /// OnQueueEmpty event is triggered for updates public readonly int PrimUpdatesPerCallback; + /// Number of texture packets to put on the queue each time the /// OnQueueEmpty event is triggered for textures public readonly int TextureSendLimit; @@ -111,6 +114,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP //PacketEventDictionary packetEvents = new PacketEventDictionary(); /// Incoming packets that are awaiting handling private OpenMetaverse.BlockingQueue packetInbox = new OpenMetaverse.BlockingQueue(); + /// //private UDPClientCollection m_clients = new UDPClientCollection(); /// Bandwidth throttle for this UDP server @@ -121,28 +125,37 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// Manages authentication for agent circuits private AgentCircuitManager m_circuitManager; + /// Reference to the scene this UDP server is attached to protected Scene m_scene; + /// The X/Y coordinates of the scene this UDP server is attached to private Location m_location; + /// The size of the receive buffer for the UDP socket. This value /// is passed up to the operating system and used in the system networking /// stack. Use zero to leave this value as the default private int m_recvBufferSize; + /// Flag to process packets asynchronously or synchronously private bool m_asyncPacketHandling; + /// Tracks whether or not a packet was sent each round so we know /// whether or not to sleep private bool m_packetSent; /// Environment.TickCount of the last time that packet stats were reported to the scene private int m_elapsedMSSinceLastStatReport = 0; + /// Environment.TickCount of the last time the outgoing packet handler executed private int m_tickLastOutgoingPacketHandler; + /// Keeps track of the number of elapsed milliseconds since the last time the outgoing packet handler looped private int m_elapsedMSOutgoingPacketHandler; + /// Keeps track of the number of 100 millisecond periods elapsed in the outgoing packet handler executed private int m_elapsed100MSOutgoingPacketHandler; + /// Keeps track of the number of 500 millisecond periods elapsed in the outgoing packet handler executed private int m_elapsed500MSOutgoingPacketHandler; @@ -155,6 +168,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// Flag to signal when clients should send pings protected bool m_sendPing; + private Pool m_incomingPacketPool; + private int m_defaultRTO = 0; private int m_maxRTO = 0; private int m_ackTimeout = 0; @@ -175,7 +190,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// private IClientAPI m_currentIncomingClient; - public LLUDPServer(IPAddress listenIP, ref uint port, int proxyPortOffsetParm, bool allow_alternate_port, IConfigSource configSource, AgentCircuitManager circuitManager) + public LLUDPServer( + IPAddress listenIP, ref uint port, int proxyPortOffsetParm, bool allow_alternate_port, + IConfigSource configSource, AgentCircuitManager circuitManager) : base(listenIP, (int)port) { #region Environment.TickCount Measurement @@ -222,6 +239,16 @@ namespace OpenSim.Region.ClientStack.LindenUDP m_pausedAckTimeout = 1000 * 300; // 5 minutes } + // FIXME: This actually only needs to be done once since the PacketPool is shared across all servers. + // However, there is no harm in temporarily doing it multiple times. + IConfig packetConfig = configSource.Configs["PacketPool"]; + if (packetConfig != null) + { + PacketPool.Instance.RecyclePackets = packetConfig.GetBoolean("RecyclePackets", true); + PacketPool.Instance.RecycleDataBlocks = packetConfig.GetBoolean("RecycleDataBlocks", true); + UsePools = packetConfig.GetBoolean("RecycleBaseUDPPackets", false); + } + #region BinaryStats config = configSource.Configs["Statistics.Binary"]; m_shouldCollectStats = false; @@ -249,20 +276,28 @@ namespace OpenSim.Region.ClientStack.LindenUDP m_throttle = new TokenBucket(null, sceneThrottleBps); ThrottleRates = new ThrottleRates(configSource); + + if (UsePools) + m_incomingPacketPool = new Pool(() => new IncomingPacket(), 500); } public void Start() { - if (m_scene == null) - throw new InvalidOperationException("[LLUDPSERVER]: Cannot LLUDPServer.Start() without an IScene reference"); + StartInbound(); + StartOutbound(); + m_elapsedMSSinceLastStatReport = Environment.TickCount; + } + + private void StartInbound() + { m_log.InfoFormat( - "[LLUDPSERVER]: Starting the LLUDP server in {0} mode", - m_asyncPacketHandling ? "asynchronous" : "synchronous"); + "[LLUDPSERVER]: Starting inbound packet processing for the LLUDP server in {0} mode with UsePools = {1}", + m_asyncPacketHandling ? "asynchronous" : "synchronous", UsePools); - base.Start(m_recvBufferSize, m_asyncPacketHandling); + base.StartInbound(m_recvBufferSize, m_asyncPacketHandling); - // Start the packet processing threads + // This thread will process the packets received that are placed on the packetInbox Watchdog.StartThread( IncomingPacketHandler, string.Format("Incoming Packets ({0})", m_scene.RegionInfo.RegionName), @@ -271,6 +306,13 @@ namespace OpenSim.Region.ClientStack.LindenUDP true, GetWatchdogIncomingAlarmData, Watchdog.DEFAULT_WATCHDOG_TIMEOUT_MS); + } + + private new void StartOutbound() + { + m_log.Info("[LLUDPSERVER]: Starting outbound packet processing for the LLUDP server"); + + base.StartOutbound(); Watchdog.StartThread( OutgoingPacketHandler, @@ -280,8 +322,13 @@ namespace OpenSim.Region.ClientStack.LindenUDP true, GetWatchdogOutgoingAlarmData, Watchdog.DEFAULT_WATCHDOG_TIMEOUT_MS); + } - m_elapsedMSSinceLastStatReport = Environment.TickCount; + public void Stop() + { + m_log.Info("[LLUDPSERVER]: Shutting down the LLUDP server for " + m_scene.RegionInfo.RegionName); + base.StopOutbound(); + base.StopInbound(); } /// @@ -306,12 +353,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP m_currentOutgoingClient != null ? m_currentOutgoingClient.Name : "none"); } - public new void Stop() - { - m_log.Info("[LLUDPSERVER]: Shutting down the LLUDP server for " + m_scene.RegionInfo.RegionName); - base.Stop(); - } - public void AddScene(IScene scene) { if (m_scene != null) @@ -328,6 +369,81 @@ namespace OpenSim.Region.ClientStack.LindenUDP m_scene = (Scene)scene; m_location = new Location(m_scene.RegionInfo.RegionHandle); + + MainConsole.Instance.Commands.AddCommand( + "Debug", + false, + "debug lludp start", + "debug lludp start ", + "Control LLUDP packet processing.", + "No effect if packet processing has already started.\n" + + "in - start inbound processing.\n" + + "out - start outbound processing.\n" + + "all - start in and outbound processing.\n", + HandleStartCommand); + + MainConsole.Instance.Commands.AddCommand( + "Debug", + false, + "debug lludp stop", + "debug lludp stop ", + "Stop LLUDP packet processing.", + "No effect if packet processing has already stopped.\n" + + "in - stop inbound processing.\n" + + "out - stop outbound processing.\n" + + "all - stop in and outbound processing.\n", + HandleStopCommand); + + MainConsole.Instance.Commands.AddCommand( + "Debug", + false, + "debug lludp status", + "debug lludp status", + "Return status of LLUDP packet processing.", + HandleStatusCommand); + } + + private void HandleStartCommand(string module, string[] args) + { + if (args.Length != 4) + { + MainConsole.Instance.Output("Usage: debug lludp start "); + return; + } + + string subCommand = args[3]; + + if (subCommand == "in" || subCommand == "all") + StartInbound(); + + if (subCommand == "out" || subCommand == "all") + StartOutbound(); + } + + private void HandleStopCommand(string module, string[] args) + { + if (args.Length != 4) + { + MainConsole.Instance.Output("Usage: debug lludp stop "); + return; + } + + string subCommand = args[3]; + + if (subCommand == "in" || subCommand == "all") + StopInbound(); + + if (subCommand == "out" || subCommand == "all") + StopOutbound(); + } + + private void HandleStatusCommand(string module, string[] args) + { + MainConsole.Instance.OutputFormat( + "IN LLUDP packet processing for {0} is {1}", m_scene.Name, IsRunningInbound ? "enabled" : "disabled"); + + MainConsole.Instance.OutputFormat( + "OUT LLUDP packet processing for {0} is {1}", m_scene.Name, IsRunningOutbound ? "enabled" : "disabled"); } public bool HandlesRegion(Location x) @@ -411,6 +527,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP byte[] data = packet.ToBytes(); SendPacketData(udpClient, data, packet.Type, category, method); } + + PacketPool.Instance.ReturnPacket(packet); } /// @@ -695,7 +813,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP LLUDPClient udpClient = null; Packet packet = null; int packetEnd = buffer.DataLength - 1; - IPEndPoint address = (IPEndPoint)buffer.RemoteEndPoint; + IPEndPoint endPoint = (IPEndPoint)buffer.RemoteEndPoint; #region Decoding @@ -705,7 +823,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP // "[LLUDPSERVER]: Dropping undersized packet with {0} bytes received from {1} in {2}", // buffer.DataLength, buffer.RemoteEndPoint, m_scene.RegionInfo.RegionName); - return; // Drop undersizd packet + return; // Drop undersized packet } int headerLen = 7; @@ -728,7 +846,13 @@ namespace OpenSim.Region.ClientStack.LindenUDP try { - packet = Packet.BuildPacket(buffer.Data, ref packetEnd, +// packet = Packet.BuildPacket(buffer.Data, ref packetEnd, +// // Only allocate a buffer for zerodecoding if the packet is zerocoded +// ((buffer.Data[0] & Helpers.MSG_ZEROCODED) != 0) ? new byte[4096] : null); + // If OpenSimUDPBase.UsePool == true (which is currently separate from the PacketPool) then we + // assume that packet construction does not retain a reference to byte[] buffer.Data (instead, all + // bytes are copied out). + packet = PacketPool.Instance.GetPacket(buffer.Data, ref packetEnd, // Only allocate a buffer for zerodecoding if the packet is zerocoded ((buffer.Data[0] & Helpers.MSG_ZEROCODED) != 0) ? new byte[4096] : null); } @@ -743,11 +867,13 @@ namespace OpenSim.Region.ClientStack.LindenUDP return; // Drop short packet } - catch(Exception e) + catch (Exception e) { if (m_malformedCount < 100) m_log.DebugFormat("[LLUDPSERVER]: Dropped malformed packet: " + e.ToString()); + m_malformedCount++; + if ((m_malformedCount % 100000) == 0) m_log.DebugFormat("[LLUDPSERVER]: Received {0} malformed packets so far, probable network attack.", m_malformedCount); } @@ -768,7 +894,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP // UseCircuitCode handling if (packet.Type == PacketType.UseCircuitCode) { - object[] array = new object[] { buffer, packet }; + // We need to copy the endpoint so that it doesn't get changed when another thread reuses the + // buffer. + object[] array = new object[] { new IPEndPoint(endPoint.Address, endPoint.Port), packet }; Util.FireAndForget(HandleUseCircuitCode, array); @@ -777,7 +905,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP // Determine which agent this packet came from IClientAPI client; - if (!m_scene.TryGetClient(address, out client) || !(client is LLClientView)) + if (!m_scene.TryGetClient(endPoint, out client) || !(client is LLClientView)) { //m_log.Debug("[LLUDPSERVER]: Received a " + packet.Type + " packet from an unrecognized source: " + address + " in " + m_scene.RegionInfo.RegionName); return; @@ -801,6 +929,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP // Handle appended ACKs if (packet.Header.AppendedAcks && packet.Header.AckList != null) { +// m_log.DebugFormat( +// "[LLUDPSERVER]: Handling {0} appended acks from {1} in {2}", +// packet.Header.AckList.Length, client.Name, m_scene.Name); + for (int i = 0; i < packet.Header.AckList.Length; i++) udpClient.NeedAcks.Acknowledge(packet.Header.AckList[i], now, packet.Header.Resent); } @@ -810,6 +942,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP { PacketAckPacket ackPacket = (PacketAckPacket)packet; +// m_log.DebugFormat( +// "[LLUDPSERVER]: Handling {0} packet acks for {1} in {2}", +// ackPacket.Packets.Length, client.Name, m_scene.Name); + for (int i = 0; i < ackPacket.Packets.Length; i++) udpClient.NeedAcks.Acknowledge(ackPacket.Packets[i].ID, now, packet.Header.Resent); @@ -823,6 +959,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP if (packet.Header.Reliable) { +// m_log.DebugFormat( +// "[LLUDPSERVER]: Adding ack request for {0} {1} from {2} in {3}", +// packet.Type, packet.Header.Sequence, client.Name, m_scene.Name); + udpClient.PendingAcks.Enqueue(packet.Header.Sequence); // This is a somewhat odd sequence of steps to pull the client.BytesSinceLastACK value out, @@ -869,6 +1009,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP if (packet.Type == PacketType.StartPingCheck) { +// m_log.DebugFormat("[LLUDPSERVER]: Handling ping from {0} in {1}", client.Name, m_scene.Name); + // We don't need to do anything else with ping checks StartPingCheckPacket startPing = (StartPingCheckPacket)packet; CompletePing(udpClient, startPing.PingID.PingID); @@ -888,8 +1030,21 @@ namespace OpenSim.Region.ClientStack.LindenUDP #endregion Ping Check Handling + IncomingPacket incomingPacket; + // Inbox insertion - packetInbox.Enqueue(new IncomingPacket((LLClientView)client, packet)); + if (UsePools) + { + incomingPacket = m_incomingPacketPool.GetObject(); + incomingPacket.Client = (LLClientView)client; + incomingPacket.Packet = packet; + } + else + { + incomingPacket = new IncomingPacket((LLClientView)client, packet); + } + + packetInbox.Enqueue(incomingPacket); } #region BinaryStats @@ -975,21 +1130,19 @@ namespace OpenSim.Region.ClientStack.LindenUDP private void HandleUseCircuitCode(object o) { - IPEndPoint remoteEndPoint = null; + IPEndPoint endPoint = null; IClientAPI client = null; try { // DateTime startTime = DateTime.Now; object[] array = (object[])o; - UDPPacketBuffer buffer = (UDPPacketBuffer)array[0]; + endPoint = (IPEndPoint)array[0]; UseCircuitCodePacket uccp = (UseCircuitCodePacket)array[1]; m_log.DebugFormat( "[LLUDPSERVER]: Handling UseCircuitCode request for circuit {0} to {1} from IP {2}", - uccp.CircuitCode.Code, m_scene.RegionInfo.RegionName, buffer.RemoteEndPoint); - - remoteEndPoint = (IPEndPoint)buffer.RemoteEndPoint; + uccp.CircuitCode.Code, m_scene.RegionInfo.RegionName, endPoint); AuthenticateResponse sessionInfo; if (IsClientAuthorized(uccp, out sessionInfo)) @@ -1000,13 +1153,13 @@ namespace OpenSim.Region.ClientStack.LindenUDP uccp.CircuitCode.Code, uccp.CircuitCode.ID, uccp.CircuitCode.SessionID, - remoteEndPoint, + endPoint, sessionInfo); // Send ack straight away to let the viewer know that the connection is active. // The client will be null if it already exists (e.g. if on a region crossing the client sends a use // circuit code to the existing child agent. This is not particularly obvious. - SendAckImmediate(remoteEndPoint, uccp.Header.Sequence); + SendAckImmediate(endPoint, uccp.Header.Sequence); // We only want to send initial data to new clients, not ones which are being converted from child to root. if (client != null) @@ -1017,7 +1170,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP // Don't create clients for unauthorized requesters. m_log.WarnFormat( "[LLUDPSERVER]: Ignoring connection request for {0} to {1} with unknown circuit code {2} from IP {3}", - uccp.CircuitCode.ID, m_scene.RegionInfo.RegionName, uccp.CircuitCode.Code, remoteEndPoint); + uccp.CircuitCode.ID, m_scene.RegionInfo.RegionName, uccp.CircuitCode.Code, endPoint); } // m_log.DebugFormat( @@ -1029,7 +1182,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP { m_log.ErrorFormat( "[LLUDPSERVER]: UseCircuitCode handling from endpoint {0}, client {1} {2} failed. Exception {3}{4}", - remoteEndPoint != null ? remoteEndPoint.ToString() : "n/a", + endPoint != null ? endPoint.ToString() : "n/a", client != null ? client.Name : "unknown", client != null ? client.AgentId.ToString() : "unknown", e.Message, @@ -1094,20 +1247,20 @@ namespace OpenSim.Region.ClientStack.LindenUDP { IClientAPI client = null; - // In priciple there shouldn't be more than one thread here, ever. - // But in case that happens, we need to synchronize this piece of code - // because it's too important - lock (this) + // We currently synchronize this code across the whole scene to avoid issues such as + // http://opensimulator.org/mantis/view.php?id=5365 However, once locking per agent circuit can be done + // consistently, this lock could probably be removed. + lock (this) { if (!m_scene.TryGetClient(agentID, out client)) { LLUDPClient udpClient = new LLUDPClient(this, ThrottleRates, m_throttle, circuitCode, agentID, remoteEndPoint, m_defaultRTO, m_maxRTO); - + client = new LLClientView(m_scene, this, udpClient, sessionInfo, agentID, sessionID, circuitCode); client.OnLogout += LogoutHandler; - + ((LLClientView)client).DisableFacelights = m_disableFacelights; - + client.Start(); } } @@ -1146,7 +1299,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP // on to en-US to avoid number parsing issues Culture.SetCurrentCulture(); - while (base.IsRunning) + while (IsRunningInbound) { try { @@ -1161,7 +1314,12 @@ namespace OpenSim.Region.ClientStack.LindenUDP } if (packetInbox.Dequeue(100, ref incomingPacket)) + { ProcessInPacket(incomingPacket);//, incomingPacket); Util.FireAndForget(ProcessInPacket, incomingPacket); + + if (UsePools) + m_incomingPacketPool.ReturnObject(incomingPacket); + } } catch (Exception ex) { @@ -1188,7 +1346,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP // Action generic every round Action clientPacketHandler = ClientOutgoingPacketHandler; - while (base.IsRunning) + while (base.IsRunningOutbound) { try { diff --git a/OpenSim/Region/ClientStack/Linden/UDP/OpenSimUDPBase.cs b/OpenSim/Region/ClientStack/Linden/UDP/OpenSimUDPBase.cs index 039379d4ee..6e6b3ef1b5 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/OpenSimUDPBase.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/OpenSimUDPBase.cs @@ -30,6 +30,7 @@ using System.Net; using System.Net.Sockets; using System.Threading; using log4net; +using OpenSim.Framework; namespace OpenMetaverse { @@ -58,17 +59,29 @@ namespace OpenMetaverse /// Flag to process packets asynchronously or synchronously private bool m_asyncPacketHandling; - /// The all important shutdown flag - private volatile bool m_shutdownFlag = true; + /// + /// Pool to use for handling data. May be null if UsePools = false; + /// + protected OpenSim.Framework.Pool m_pool; - /// Returns true if the server is currently listening, otherwise false - public bool IsRunning { get { return !m_shutdownFlag; } } + /// + /// Are we to use object pool(s) to reduce memory churn when receiving data? + /// + public bool UsePools { get; protected set; } + + /// Returns true if the server is currently listening for inbound packets, otherwise false + public bool IsRunningInbound { get; private set; } + + /// Returns true if the server is currently sending outbound packets, otherwise false + /// If IsRunningOut = false, then any request to send a packet is simply dropped. + public bool IsRunningOutbound { get; private set; } /// /// Default constructor /// /// Local IP address to bind the server to /// Port to listening for incoming UDP packets on + /// /// Are we to use an object pool to get objects for handing inbound data? public OpenSimUDPBase(IPAddress bindAddress, int port) { m_localBindAddress = bindAddress; @@ -76,7 +89,7 @@ namespace OpenMetaverse } /// - /// Start the UDP server + /// Start inbound UDP packet handling. /// /// The size of the receive buffer for /// the UDP socket. This value is passed up to the operating system @@ -91,11 +104,16 @@ namespace OpenMetaverse /// manner (not throwing an exception when the remote side resets the /// connection). This call is ignored on Mono where the flag is not /// necessary - public void Start(int recvBufferSize, bool asyncPacketHandling) + public void StartInbound(int recvBufferSize, bool asyncPacketHandling) { + if (UsePools) + m_pool = new Pool(() => new UDPPacketBuffer(), 500); + else + m_pool = null; + m_asyncPacketHandling = asyncPacketHandling; - if (m_shutdownFlag) + if (!IsRunningInbound) { const int SIO_UDP_CONNRESET = -1744830452; @@ -127,8 +145,7 @@ namespace OpenMetaverse m_udpSocket.Bind(ipep); - // we're not shutting down, we're starting up - m_shutdownFlag = false; + IsRunningInbound = true; // kick off an async receive. The Start() method will return, the // actual receives will occur asynchronously and will be caught in @@ -138,28 +155,41 @@ namespace OpenMetaverse } /// - /// Stops the UDP server + /// Start outbound UDP packet handling. /// - public void Stop() + public void StartOutbound() { - if (!m_shutdownFlag) + IsRunningOutbound = true; + } + + public void StopInbound() + { + if (IsRunningInbound) { // wait indefinitely for a writer lock. Once this is called, the .NET runtime // will deny any more reader locks, in effect blocking all other send/receive - // threads. Once we have the lock, we set shutdownFlag to inform the other + // threads. Once we have the lock, we set IsRunningInbound = false to inform the other // threads that the socket is closed. - m_shutdownFlag = true; + IsRunningInbound = false; m_udpSocket.Close(); } } + public void StopOutbound() + { + IsRunningOutbound = false; + } + private void AsyncBeginReceive() { - // allocate a packet buffer - //WrappedObject wrappedBuffer = Pool.CheckOut(); - UDPPacketBuffer buf = new UDPPacketBuffer(); + UDPPacketBuffer buf; - if (!m_shutdownFlag) + if (UsePools) + buf = m_pool.GetObject(); + else + buf = new UDPPacketBuffer(); + + if (IsRunningInbound) { try { @@ -212,7 +242,7 @@ namespace OpenMetaverse { // Asynchronous receive operations will complete here through the call // to AsyncBeginReceive - if (!m_shutdownFlag) + if (IsRunningInbound) { // Asynchronous mode will start another receive before the // callback for this packet is even fired. Very parallel :-) @@ -221,8 +251,6 @@ namespace OpenMetaverse // get the buffer that was created in AsyncBeginReceive // this is the received data - //WrappedObject wrappedBuffer = (WrappedObject)iar.AsyncState; - //UDPPacketBuffer buffer = wrappedBuffer.Instance; UDPPacketBuffer buffer = (UDPPacketBuffer)iar.AsyncState; try @@ -239,7 +267,8 @@ namespace OpenMetaverse catch (ObjectDisposedException) { } finally { - //wrappedBuffer.Dispose(); + if (UsePools) + m_pool.ReturnObject(buffer); // Synchronous mode waits until the packet callback completes // before starting the receive to fetch another packet @@ -252,7 +281,7 @@ namespace OpenMetaverse public void AsyncBeginSend(UDPPacketBuffer buf) { - if (!m_shutdownFlag) + if (IsRunningOutbound) { try { diff --git a/OpenSim/Framework/PacketPool.cs b/OpenSim/Region/ClientStack/Linden/UDP/PacketPool.cs similarity index 79% rename from OpenSim/Framework/PacketPool.cs rename to OpenSim/Region/ClientStack/Linden/UDP/PacketPool.cs index 41d17c54f2..2a3d14f69f 100644 --- a/OpenSim/Framework/PacketPool.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/PacketPool.cs @@ -31,10 +31,10 @@ using System.Reflection; using OpenMetaverse; using OpenMetaverse.Packets; using log4net; +using OpenSim.Framework.Monitoring; -namespace OpenSim.Framework +namespace OpenSim.Region.ClientStack.LindenUDP { - public sealed class PacketPool { private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); @@ -44,14 +44,28 @@ namespace OpenSim.Framework private bool packetPoolEnabled = true; private bool dataBlockPoolEnabled = true; + private PercentageStat m_packetsReusedStat = new PercentageStat( + "PacketsReused", + "Packets reused", + "clientstack", + "packetpool", + StatVerbosity.Debug, + "Number of packets reused out of all requests to the packet pool"); + + private PercentageStat m_blocksReusedStat = new PercentageStat( + "BlocksReused", + "Blocks reused", + "clientstack", + "packetpool", + StatVerbosity.Debug, + "Number of data blocks reused out of all requests to the packet pool"); + + /// + /// Pool of packets available for reuse. + /// private readonly Dictionary> pool = new Dictionary>(); - private static Dictionary> DataBlocks = - new Dictionary>(); - - static PacketPool() - { - } + private static Dictionary> DataBlocks = new Dictionary>(); public static PacketPool Instance { @@ -70,8 +84,21 @@ namespace OpenSim.Framework get { return dataBlockPoolEnabled; } } + private PacketPool() + { + StatsManager.RegisterStat(m_packetsReusedStat); + StatsManager.RegisterStat(m_blocksReusedStat); + } + + /// + /// Gets a packet of the given type. + /// + /// + /// Guaranteed to always return a packet, whether from the pool or newly constructed. public Packet GetPacket(PacketType type) { + m_packetsReusedStat.Consequent++; + Packet packet; if (!packetPoolEnabled) @@ -81,13 +108,19 @@ namespace OpenSim.Framework { if (!pool.ContainsKey(type) || pool[type] == null || (pool[type]).Count == 0) { +// m_log.DebugFormat("[PACKETPOOL]: Building {0} packet", type); + // Creating a new packet if we cannot reuse an old package packet = Packet.BuildPacket(type); } else { +// m_log.DebugFormat("[PACKETPOOL]: Pulling {0} packet", type); + // Recycle old packages - packet = (pool[type]).Pop(); + m_packetsReusedStat.Antecedent++; + + packet = pool[type].Pop(); } } @@ -136,7 +169,7 @@ namespace OpenSim.Framework { PacketType type = GetType(bytes); - Array.Clear(zeroBuffer, 0, zeroBuffer.Length); +// Array.Clear(zeroBuffer, 0, zeroBuffer.Length); int i = 0; Packet packet = GetPacket(type); @@ -183,6 +216,7 @@ namespace OpenSim.Framework switch (packet.Type) { // List pooling packets here + case PacketType.AgentUpdate: case PacketType.PacketAck: case PacketType.ObjectUpdate: case PacketType.ImprovedTerseObjectUpdate: @@ -197,7 +231,9 @@ namespace OpenSim.Framework if ((pool[type]).Count < 50) { - (pool[type]).Push(packet); +// m_log.DebugFormat("[PACKETPOOL]: Pushing {0} packet", type); + + pool[type].Push(packet); } } break; @@ -209,16 +245,21 @@ namespace OpenSim.Framework } } - public static T GetDataBlock() where T: new() + public T GetDataBlock() where T: new() { lock (DataBlocks) { + m_blocksReusedStat.Consequent++; + Stack s; if (DataBlocks.TryGetValue(typeof(T), out s)) { if (s.Count > 0) + { + m_blocksReusedStat.Antecedent++; return (T)s.Pop(); + } } else { @@ -229,7 +270,7 @@ namespace OpenSim.Framework } } - public static void ReturnDataBlock(T block) where T: new() + public void ReturnDataBlock(T block) where T: new() { if (block == null) return; @@ -244,4 +285,4 @@ namespace OpenSim.Framework } } } -} +} \ No newline at end of file diff --git a/OpenSim/Region/ClientStack/Linden/UDP/Tests/BasicCircuitTests.cs b/OpenSim/Region/ClientStack/Linden/UDP/Tests/BasicCircuitTests.cs index 109a8e1914..556df305ab 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/Tests/BasicCircuitTests.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/Tests/BasicCircuitTests.cs @@ -43,7 +43,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP.Tests /// This will contain basic tests for the LindenUDP client stack /// [TestFixture] - public class BasicCircuitTests + public class BasicCircuitTests : OpenSimTestCase { private Scene m_scene; private TestLLUDPServer m_udpServer; @@ -65,8 +65,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP.Tests } [SetUp] - public void SetUp() + public override void SetUp() { + base.SetUp(); m_scene = new SceneHelpers().SetupScene(); } @@ -143,7 +144,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP.Tests public void TestAddClient() { TestHelpers.InMethod(); -// XmlConfigurator.Configure(); +// TestHelpers.EnableLogging(); AddUdpServer(); diff --git a/OpenSim/Region/CoreModules/Avatar/Attachments/AttachmentsModule.cs b/OpenSim/Region/CoreModules/Avatar/Attachments/AttachmentsModule.cs index 130c869bc7..2a513e91d8 100644 --- a/OpenSim/Region/CoreModules/Avatar/Attachments/AttachmentsModule.cs +++ b/OpenSim/Region/CoreModules/Avatar/Attachments/AttachmentsModule.cs @@ -406,6 +406,11 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments } public void DetachSingleAttachmentToGround(IScenePresence sp, uint soLocalId) + { + DetachSingleAttachmentToGround(sp, soLocalId, sp.AbsolutePosition, Quaternion.Identity); + } + + public void DetachSingleAttachmentToGround(IScenePresence sp, uint soLocalId, Vector3 absolutePos, Quaternion absoluteRot) { if (!Enabled) return; @@ -448,7 +453,11 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments so.FromItemID = UUID.Zero; SceneObjectPart rootPart = so.RootPart; - so.AbsolutePosition = sp.AbsolutePosition; + so.AbsolutePosition = absolutePos; + if (absoluteRot != Quaternion.Identity) + { + so.UpdateGroupRotationR(absoluteRot); + } so.AttachedAvatar = UUID.Zero; rootPart.SetParentLocalId(0); so.ClearPartAttachmentData(); diff --git a/OpenSim/Region/CoreModules/Avatar/AvatarFactory/AvatarFactoryModule.cs b/OpenSim/Region/CoreModules/Avatar/AvatarFactory/AvatarFactoryModule.cs index 1d6e522add..b5b00825f4 100644 --- a/OpenSim/Region/CoreModules/Avatar/AvatarFactory/AvatarFactoryModule.cs +++ b/OpenSim/Region/CoreModules/Avatar/AvatarFactory/AvatarFactoryModule.cs @@ -525,7 +525,7 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory { for (int i = 0; i < AvatarWearable.MAX_WEARABLES; i++) { - for (int j = 0; j < appearance.Wearables[j].Count; j++) + for (int j = 0; j < appearance.Wearables[i].Count; j++) { if (appearance.Wearables[i][j].ItemID == UUID.Zero) continue; @@ -533,6 +533,7 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory // Ignore ruth's assets if (appearance.Wearables[i][j].ItemID == AvatarWearable.DefaultWearables[i][0].ItemID) continue; + InventoryItemBase baseItem = new InventoryItemBase(appearance.Wearables[i][j].ItemID, userID); baseItem = invService.GetItem(baseItem); diff --git a/OpenSim/Region/CoreModules/Avatar/Friends/CallingCardModule.cs b/OpenSim/Region/CoreModules/Avatar/Friends/CallingCardModule.cs index d942e8742a..5ec0ea94b4 100644 --- a/OpenSim/Region/CoreModules/Avatar/Friends/CallingCardModule.cs +++ b/OpenSim/Region/CoreModules/Avatar/Friends/CallingCardModule.cs @@ -141,7 +141,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Friends client.FirstName+" "+client.LastName, destID, (byte)211, false, String.Empty, - transactionID, false, new Vector3(), new byte[0]), + transactionID, false, new Vector3(), new byte[0], true), delegate(bool success) {} ); } } diff --git a/OpenSim/Region/CoreModules/Avatar/Inventory/Transfer/InventoryTransferModule.cs b/OpenSim/Region/CoreModules/Avatar/Inventory/Transfer/InventoryTransferModule.cs index f3af59ab2a..33b48393fb 100644 --- a/OpenSim/Region/CoreModules/Avatar/Inventory/Transfer/InventoryTransferModule.cs +++ b/OpenSim/Region/CoreModules/Avatar/Inventory/Transfer/InventoryTransferModule.cs @@ -297,6 +297,74 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Transfer }); } } + + // XXX: This code was placed here to try and accomdate RLV which moves given folders named #RLV/~ + // to a folder called name in #RLV. However, this approach may not be ultimately correct - from analysis + // of Firestorm 4.2.2 on sending an InventoryOffered instead of TaskInventoryOffered (as was previously + // done), the viewer itself would appear to move and rename the folder, rather than the simulator doing it here. + else if (im.dialog == (byte) InstantMessageDialog.TaskInventoryAccepted) + { + UUID destinationFolderID = UUID.Zero; + + if (im.binaryBucket != null && im.binaryBucket.Length >= 16) + { + destinationFolderID = new UUID(im.binaryBucket, 0); + } + + if (destinationFolderID != UUID.Zero) + { + InventoryFolderBase destinationFolder = new InventoryFolderBase(destinationFolderID, client.AgentId); + if (destinationFolder == null) + { + m_log.WarnFormat( + "[INVENTORY TRANSFER]: TaskInventoryAccepted message from {0} in {1} specified folder {2} which does not exist", + client.Name, scene.Name, destinationFolderID); + + return; + } + + IInventoryService invService = scene.InventoryService; + + UUID inventoryID = new UUID(im.imSessionID); // The inventory item/folder, back from it's trip + + InventoryItemBase item = new InventoryItemBase(inventoryID, client.AgentId); + item = invService.GetItem(item); + InventoryFolderBase folder = null; + UUID? previousParentFolderID = null; + + if (item != null) // It's an item + { + previousParentFolderID = item.Folder; + item.Folder = destinationFolderID; + + invService.DeleteItems(item.Owner, new List() { item.ID }); + scene.AddInventoryItem(client, item); + } + else + { + folder = new InventoryFolderBase(inventoryID, client.AgentId); + folder = invService.GetFolder(folder); + + if (folder != null) // It's a folder + { + previousParentFolderID = folder.ParentID; + folder.ParentID = destinationFolderID; + invService.MoveFolder(folder); + } + } + + // Tell client about updates to original parent and new parent (this should probably be factored with existing move item/folder code). + if (previousParentFolderID != null) + { + InventoryFolderBase previousParentFolder + = new InventoryFolderBase((UUID)previousParentFolderID, client.AgentId); + previousParentFolder = invService.GetFolder(previousParentFolder); + scene.SendInventoryUpdate(client, previousParentFolder, true, true); + + scene.SendInventoryUpdate(client, destinationFolder, true, true); + } + } + } else if ( im.dialog == (byte)InstantMessageDialog.InventoryDeclined || im.dialog == (byte)InstantMessageDialog.TaskInventoryDeclined) @@ -315,9 +383,11 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Transfer InventoryItemBase item = new InventoryItemBase(inventoryID, client.AgentId); item = invService.GetItem(item); InventoryFolderBase folder = null; + UUID? previousParentFolderID = null; if (item != null && trashFolder != null) { + previousParentFolderID = item.Folder; item.Folder = trashFolder.ID; // Diva comment: can't we just update this item??? @@ -333,6 +403,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Transfer if (folder != null & trashFolder != null) { + previousParentFolderID = folder.ParentID; folder.ParentID = trashFolder.ID; invService.MoveFolder(folder); } @@ -352,6 +423,16 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Transfer client.SendAgentAlertMessage("Unable to delete "+ "received inventory" + reason, false); } + // Tell client about updates to original parent and new parent (this should probably be factored with existing move item/folder code). + else if (previousParentFolderID != null) + { + InventoryFolderBase previousParentFolder + = new InventoryFolderBase((UUID)previousParentFolderID, client.AgentId); + previousParentFolder = invService.GetFolder(previousParentFolder); + scene.SendInventoryUpdate(client, previousParentFolder, true, true); + + scene.SendInventoryUpdate(client, trashFolder, true, true); + } ScenePresence user = scene.GetScenePresence(new UUID(im.toAgentID)); diff --git a/OpenSim/Region/CoreModules/Avatar/Lure/HGLureModule.cs b/OpenSim/Region/CoreModules/Avatar/Lure/HGLureModule.cs index 92cf9d1616..9c369f6060 100644 --- a/OpenSim/Region/CoreModules/Avatar/Lure/HGLureModule.cs +++ b/OpenSim/Region/CoreModules/Avatar/Lure/HGLureModule.cs @@ -186,7 +186,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Lure client.FirstName+" "+client.LastName, targetid, (byte)InstantMessageDialog.RequestTeleport, false, message, sessionID, false, presence.AbsolutePosition, - new Byte[0]); + new Byte[0], true); m.RegionID = client.Scene.RegionInfo.RegionID.Guid; m_log.DebugFormat("[HG LURE MODULE]: RequestTeleport sessionID={0}, regionID={1}, message={2}", m.imSessionID, m.RegionID, m.message); diff --git a/OpenSim/Region/CoreModules/Avatar/Lure/LureModule.cs b/OpenSim/Region/CoreModules/Avatar/Lure/LureModule.cs index 2d4cffd224..6ce9556e67 100644 --- a/OpenSim/Region/CoreModules/Avatar/Lure/LureModule.cs +++ b/OpenSim/Region/CoreModules/Avatar/Lure/LureModule.cs @@ -169,7 +169,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Lure client.FirstName+" "+client.LastName, targetid, (byte)InstantMessageDialog.RequestTeleport, false, message, dest, false, presence.AbsolutePosition, - new Byte[0]); + new Byte[0], true); if (m_TransferModule != null) { diff --git a/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferModule.cs b/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferModule.cs index b51570f0cc..90fe43034b 100644 --- a/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferModule.cs +++ b/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferModule.cs @@ -486,6 +486,8 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer if (sp.ParentID != (uint)0) sp.StandUp(); + // At least on LL 3.3.4, this is not strictly necessary - a teleport will succeed without sending this to + // the viewer. However, it might mean that the viewer does not see the black teleport screen (untested). sp.ControllingClient.SendTeleportStart(teleportFlags); // the avatar.Close below will clear the child region list. We need this below for (possibly) @@ -561,8 +563,11 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer // So let's wait Thread.Sleep(200); + // At least on LL 3.3.4 for teleports between different regions on the same simulator this appears + // unnecessary - teleport will succeed and SEED caps will be requested without it (though possibly + // only on TeleportFinish). This is untested for region teleport between different simulators + // though this probably also works. m_eqModule.EstablishAgentCommunication(sp.UUID, endPoint, capsPath); - } else { @@ -1068,6 +1073,7 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer Scene initiatingScene) { Thread.Sleep(10000); + IMessageTransferModule im = initiatingScene.RequestModuleInterface(); if (im != null) { @@ -1080,11 +1086,22 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer (uint)(int)position.X, (uint)(int)position.Y, (uint)(int)position.Z); - GridInstantMessage m = new GridInstantMessage(initiatingScene, UUID.Zero, - "Region", agent.UUID, - (byte)InstantMessageDialog.GodLikeRequestTeleport, false, - "", gotoLocation, false, new Vector3(127, 0, 0), - new Byte[0]); + + GridInstantMessage m + = new GridInstantMessage( + initiatingScene, + UUID.Zero, + "Region", + agent.UUID, + (byte)InstantMessageDialog.GodLikeRequestTeleport, + false, + "", + gotoLocation, + false, + new Vector3(127, 0, 0), + new Byte[0], + false); + im.SendInstantMessage(m, delegate(bool success) { m_log.DebugFormat("[ENTITY TRANSFER MODULE]: Client Initiating Teleport sending IM success = {0}", success); diff --git a/OpenSim/Region/CoreModules/Framework/EntityTransfer/HGEntityTransferModule.cs b/OpenSim/Region/CoreModules/Framework/EntityTransfer/HGEntityTransferModule.cs index aa1e127eb2..679be18121 100644 --- a/OpenSim/Region/CoreModules/Framework/EntityTransfer/HGEntityTransferModule.cs +++ b/OpenSim/Region/CoreModules/Framework/EntityTransfer/HGEntityTransferModule.cs @@ -212,7 +212,7 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer int flags = Scene.GridService.GetRegionFlags(Scene.RegionInfo.ScopeID, region.RegionID); m_log.DebugFormat("[HG ENTITY TRANSFER MODULE]: region {0} flags: {1}", region.RegionID, flags); - if ((flags & (int)OpenSim.Data.RegionFlags.Hyperlink) != 0) + if ((flags & (int)OpenSim.Framework.RegionFlags.Hyperlink) != 0) { m_log.DebugFormat("[HG ENTITY TRANSFER MODULE]: Destination region {0} is hyperlink", region.RegionID); GridRegion real_destination = m_GatekeeperConnector.GetHyperlinkRegion(region, region.RegionID); @@ -232,7 +232,7 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer return true; int flags = Scene.GridService.GetRegionFlags(Scene.RegionInfo.ScopeID, reg.RegionID); - if (flags == -1 /* no region in DB */ || (flags & (int)OpenSim.Data.RegionFlags.Hyperlink) != 0) + if (flags == -1 /* no region in DB */ || (flags & (int)OpenSim.Framework.RegionFlags.Hyperlink) != 0) return true; return false; @@ -256,7 +256,7 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer reason = string.Empty; logout = false; int flags = Scene.GridService.GetRegionFlags(Scene.RegionInfo.ScopeID, reg.RegionID); - if (flags == -1 /* no region in DB */ || (flags & (int)OpenSim.Data.RegionFlags.Hyperlink) != 0) + if (flags == -1 /* no region in DB */ || (flags & (int)OpenSim.Framework.RegionFlags.Hyperlink) != 0) { // this user is going to another grid // check if HyperGrid teleport is allowed, based on user level @@ -298,7 +298,7 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer // The rest is only needed for controlling appearance int flags = Scene.GridService.GetRegionFlags(Scene.RegionInfo.ScopeID, reg.RegionID); - if (flags == -1 /* no region in DB */ || (flags & (int)OpenSim.Data.RegionFlags.Hyperlink) != 0) + if (flags == -1 /* no region in DB */ || (flags & (int)OpenSim.Framework.RegionFlags.Hyperlink) != 0) { // this user is going to another grid if (Scene.UserManagementModule.IsLocalGridUser(sp.UUID)) diff --git a/OpenSim/Region/CoreModules/Framework/Monitoring/MonitorModule.cs b/OpenSim/Region/CoreModules/Framework/Monitoring/MonitorModule.cs index e135c21b79..e4115851af 100644 --- a/OpenSim/Region/CoreModules/Framework/Monitoring/MonitorModule.cs +++ b/OpenSim/Region/CoreModules/Framework/Monitoring/MonitorModule.cs @@ -95,14 +95,14 @@ namespace OpenSim.Region.CoreModules.Framework.Monitoring { foreach (IMonitor monitor in m_staticMonitors) { - m_log.InfoFormat( + MainConsole.Instance.OutputFormat( "[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( + MainConsole.Instance.OutputFormat( "[MONITOR MODULE]: {0} reports {1} = {2}", m_scene.RegionInfo.RegionName, tuple.Key, tuple.Value); } diff --git a/OpenSim/Region/CoreModules/Framework/Statistics/Logging/LogWriter.cs b/OpenSim/Region/CoreModules/Framework/Statistics/Logging/LogWriter.cs index 65e4c90ba5..fd8d5e32bd 100755 --- a/OpenSim/Region/CoreModules/Framework/Statistics/Logging/LogWriter.cs +++ b/OpenSim/Region/CoreModules/Framework/Statistics/Logging/LogWriter.cs @@ -1,161 +1,170 @@ -/* - * Copyright (c) Contributors, http://opensimulator.org/ - * See CONTRIBUTORS.TXT for a full list of copyright holders. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of the OpenSimulator Project nor the - * names of its contributors may be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -using System; -using System.IO; -using System.Text; -using log4net; - -namespace OpenSim.Region.CoreModules.Framework.Statistics.Logging -{ - /// - /// Class for writing a high performance, high volume log file. - /// Sometimes, to debug, one has a high volume logging to do and the regular - /// log file output is not appropriate. - /// Create a new instance with the parameters needed and - /// call Write() to output a line. Call Close() when finished. - /// If created with no parameters, it will not log anything. - /// - public class LogWriter : IDisposable - { - public bool Enabled { get; private set; } - - private string m_logDirectory = "."; - private int m_logMaxFileTimeMin = 5; // 5 minutes - public String LogFileHeader { get; set; } - - private StreamWriter m_logFile = null; - private TimeSpan m_logFileLife; - private DateTime m_logFileEndTime; - private Object m_logFileWriteLock = new Object(); - - // set externally when debugging. If let 'null', this does not write any error messages. - public ILog ErrorLogger = null; - private string LogHeader = "[LOG WRITER]"; - - /// - /// Create a log writer that will not write anything. Good for when not enabled - /// but the write statements are still in the code. - /// - public LogWriter() - { - Enabled = false; - m_logFile = null; - } - - /// - /// Create a log writer instance. - /// - /// The directory to create the log file in. May be 'null' for default. - /// The characters that begin the log file name. May be 'null' for default. - /// Maximum age of a log file in minutes. If zero, will set default. - public LogWriter(string dir, string headr, int maxFileTime) - { - m_logDirectory = dir == null ? "." : dir; - - LogFileHeader = headr == null ? "log-" : headr; - - m_logMaxFileTimeMin = maxFileTime; - if (m_logMaxFileTimeMin < 1) - m_logMaxFileTimeMin = 5; - - m_logFileLife = new TimeSpan(0, m_logMaxFileTimeMin, 0); - m_logFileEndTime = DateTime.Now + m_logFileLife; - - Enabled = true; - } - - public void Dispose() - { - this.Close(); - } - - public void Close() - { - Enabled = false; - if (m_logFile != null) - { - m_logFile.Close(); - m_logFile.Dispose(); - m_logFile = null; - } - } - - public void Write(string line, params object[] args) - { - if (!Enabled) return; - Write(String.Format(line, args)); - } - - public void Write(string line) - { - if (!Enabled) return; - try - { - lock (m_logFileWriteLock) - { - DateTime now = DateTime.Now; - if (m_logFile == null || now > m_logFileEndTime) - { - if (m_logFile != null) - { - m_logFile.Close(); - m_logFile.Dispose(); - m_logFile = null; - } - - // First log file or time has expired, start writing to a new log file - m_logFileEndTime = now + m_logFileLife; - string path = (m_logDirectory.Length > 0 ? m_logDirectory - + System.IO.Path.DirectorySeparatorChar.ToString() : "") - + String.Format("{0}{1}.log", LogFileHeader, now.ToString("yyyyMMddHHmmss")); - m_logFile = new StreamWriter(File.Open(path, FileMode.Append, FileAccess.Write)); - } - if (m_logFile != null) - { - StringBuilder buff = new StringBuilder(line.Length + 25); - buff.Append(now.ToString("yyyyMMddHHmmssfff")); - // buff.Append(now.ToString("yyyyMMddHHmmss")); - buff.Append(","); - buff.Append(line); - buff.Append("\r\n"); - m_logFile.Write(buff.ToString()); - } - } - } - catch (Exception e) - { - if (ErrorLogger != null) - { - ErrorLogger.ErrorFormat("{0}: FAILURE WRITING TO LOGFILE: {1}", LogHeader, e); - } - Enabled = false; - } - return; - } - } -} \ No newline at end of file +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +using System; +using System.IO; +using System.Text; +using log4net; + +namespace OpenSim.Region.CoreModules.Framework.Statistics.Logging +{ + /// + /// Class for writing a high performance, high volume log file. + /// Sometimes, to debug, one has a high volume logging to do and the regular + /// log file output is not appropriate. + /// Create a new instance with the parameters needed and + /// call Write() to output a line. Call Close() when finished. + /// If created with no parameters, it will not log anything. + /// + public class LogWriter : IDisposable + { + public bool Enabled { get; private set; } + + private string m_logDirectory = "."; + private int m_logMaxFileTimeMin = 5; // 5 minutes + public String LogFileHeader { get; set; } + + private StreamWriter m_logFile = null; + private TimeSpan m_logFileLife; + private DateTime m_logFileEndTime; + private Object m_logFileWriteLock = new Object(); + + // set externally when debugging. If let 'null', this does not write any error messages. + public ILog ErrorLogger = null; + private string LogHeader = "[LOG WRITER]"; + + /// + /// Create a log writer that will not write anything. Good for when not enabled + /// but the write statements are still in the code. + /// + public LogWriter() + { + Enabled = false; + m_logFile = null; + } + + /// + /// Create a log writer instance. + /// + /// The directory to create the log file in. May be 'null' for default. + /// The characters that begin the log file name. May be 'null' for default. + /// Maximum age of a log file in minutes. If zero, will set default. + public LogWriter(string dir, string headr, int maxFileTime) + { + m_logDirectory = dir == null ? "." : dir; + + LogFileHeader = headr == null ? "log-" : headr; + + m_logMaxFileTimeMin = maxFileTime; + if (m_logMaxFileTimeMin < 1) + m_logMaxFileTimeMin = 5; + + m_logFileLife = new TimeSpan(0, m_logMaxFileTimeMin, 0); + m_logFileEndTime = DateTime.Now + m_logFileLife; + + Enabled = true; + } + + public void Dispose() + { + this.Close(); + } + + public void Close() + { + Enabled = false; + if (m_logFile != null) + { + m_logFile.Close(); + m_logFile.Dispose(); + m_logFile = null; + } + } + + public void Write(string line, params object[] args) + { + if (!Enabled) return; + Write(String.Format(line, args)); + } + + public void Flush() + { + if (!Enabled) return; + if (m_logFile != null) + { + m_logFile.Flush(); + } + } + + public void Write(string line) + { + if (!Enabled) return; + try + { + lock (m_logFileWriteLock) + { + DateTime now = DateTime.Now; + if (m_logFile == null || now > m_logFileEndTime) + { + if (m_logFile != null) + { + m_logFile.Close(); + m_logFile.Dispose(); + m_logFile = null; + } + + // First log file or time has expired, start writing to a new log file + m_logFileEndTime = now + m_logFileLife; + string path = (m_logDirectory.Length > 0 ? m_logDirectory + + System.IO.Path.DirectorySeparatorChar.ToString() : "") + + String.Format("{0}{1}.log", LogFileHeader, now.ToString("yyyyMMddHHmmss")); + m_logFile = new StreamWriter(File.Open(path, FileMode.Append, FileAccess.Write)); + } + if (m_logFile != null) + { + StringBuilder buff = new StringBuilder(line.Length + 25); + buff.Append(now.ToString("yyyyMMddHHmmssfff")); + // buff.Append(now.ToString("yyyyMMddHHmmss")); + buff.Append(","); + buff.Append(line); + buff.Append("\r\n"); + m_logFile.Write(buff.ToString()); + } + } + } + catch (Exception e) + { + if (ErrorLogger != null) + { + ErrorLogger.ErrorFormat("{0}: FAILURE WRITING TO LOGFILE: {1}", LogHeader, e); + } + Enabled = false; + } + return; + } + } +} diff --git a/OpenSim/Region/CoreModules/Framework/UserManagement/UserManagementModule.cs b/OpenSim/Region/CoreModules/Framework/UserManagement/UserManagementModule.cs index 36c84c7bc6..b4811dadce 100644 --- a/OpenSim/Region/CoreModules/Framework/UserManagement/UserManagementModule.cs +++ b/OpenSim/Region/CoreModules/Framework/UserManagement/UserManagementModule.cs @@ -31,6 +31,7 @@ using System.Reflection; using OpenSim.Framework; using OpenSim.Framework.Console; +using OpenSim.Region.ClientStack.LindenUDP; using OpenSim.Region.Framework; using OpenSim.Region.Framework.Interfaces; using OpenSim.Region.Framework.Scenes; diff --git a/OpenSim/Region/CoreModules/Scripting/DynamicTexture/DynamicTextureModule.cs b/OpenSim/Region/CoreModules/Scripting/DynamicTexture/DynamicTextureModule.cs index 1f340df1b1..93a045eff7 100644 --- a/OpenSim/Region/CoreModules/Scripting/DynamicTexture/DynamicTextureModule.cs +++ b/OpenSim/Region/CoreModules/Scripting/DynamicTexture/DynamicTextureModule.cs @@ -42,7 +42,7 @@ namespace OpenSim.Region.CoreModules.Scripting.DynamicTexture { public class DynamicTextureModule : IRegionModule, IDynamicTextureManager { - private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); +// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); private const int ALL_SIDES = -1; diff --git a/OpenSim/Region/CoreModules/World/Archiver/ArchiveReadRequest.cs b/OpenSim/Region/CoreModules/World/Archiver/ArchiveReadRequest.cs index a6923ef48d..ea806eca55 100644 --- a/OpenSim/Region/CoreModules/World/Archiver/ArchiveReadRequest.cs +++ b/OpenSim/Region/CoreModules/World/Archiver/ArchiveReadRequest.cs @@ -290,7 +290,7 @@ namespace OpenSim.Region.CoreModules.World.Archiver foreach (DearchiveContext sceneContext in sceneContexts.Values) { - m_log.InfoFormat("[ARCHIVER:] Loading region {0}", sceneContext.Scene.RegionInfo.RegionName); + m_log.InfoFormat("[ARCHIVER]: Loading region {0}", sceneContext.Scene.RegionInfo.RegionName); if (!m_merge) { @@ -324,7 +324,7 @@ namespace OpenSim.Region.CoreModules.World.Archiver Util.FireAndForget(delegate(object o) { Thread.Sleep(15000); - m_log.Info("Starting scripts in scene objects"); + m_log.Info("[ARCHIVER]: Starting scripts in scene objects"); foreach (DearchiveContext sceneContext in sceneContexts.Values) { diff --git a/OpenSim/Region/CoreModules/World/Estate/EstateManagementModule.cs b/OpenSim/Region/CoreModules/World/Estate/EstateManagementModule.cs index 58bbd2405a..0e6d663000 100644 --- a/OpenSim/Region/CoreModules/World/Estate/EstateManagementModule.cs +++ b/OpenSim/Region/CoreModules/World/Estate/EstateManagementModule.cs @@ -39,6 +39,7 @@ using OpenMetaverse; using OpenSim.Framework; using OpenSim.Region.Framework.Interfaces; using OpenSim.Region.Framework.Scenes; +using RegionFlags = OpenMetaverse.RegionFlags; namespace OpenSim.Region.CoreModules.World.Estate { diff --git a/OpenSim/Region/CoreModules/World/Land/LandObject.cs b/OpenSim/Region/CoreModules/World/Land/LandObject.cs index 0536f6ec6e..8829f27a97 100644 --- a/OpenSim/Region/CoreModules/World/Land/LandObject.cs +++ b/OpenSim/Region/CoreModules/World/Land/LandObject.cs @@ -33,6 +33,7 @@ using OpenMetaverse; using OpenSim.Framework; using OpenSim.Region.Framework.Interfaces; using OpenSim.Region.Framework.Scenes; +using RegionFlags = OpenMetaverse.RegionFlags; namespace OpenSim.Region.CoreModules.World.Land { diff --git a/OpenSim/Region/CoreModules/World/Objects/Commands/ObjectCommandsModule.cs b/OpenSim/Region/CoreModules/World/Objects/Commands/ObjectCommandsModule.cs index 09f6758ba4..87241e1559 100644 --- a/OpenSim/Region/CoreModules/World/Objects/Commands/ObjectCommandsModule.cs +++ b/OpenSim/Region/CoreModules/World/Objects/Commands/ObjectCommandsModule.cs @@ -27,6 +27,7 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Reflection; using System.Text; using System.Text.RegularExpressions; @@ -83,46 +84,99 @@ namespace OpenSim.Region.CoreModules.World.Objects.Commands m_console.Commands.AddCommand( "Objects", false, "delete object owner", "delete object owner ", - "Delete a scene object by owner", HandleDeleteObject); + "Delete scene objects by owner", + "Command will ask for confirmation before proceeding.", + HandleDeleteObject); m_console.Commands.AddCommand( "Objects", false, "delete object creator", "delete object creator ", - "Delete a scene object by creator", HandleDeleteObject); + "Delete scene objects by creator", + "Command will ask for confirmation before proceeding.", + HandleDeleteObject); m_console.Commands.AddCommand( "Objects", false, "delete object uuid", "delete object uuid ", - "Delete a scene object by uuid", HandleDeleteObject); + "Delete a scene object by uuid", + HandleDeleteObject); m_console.Commands.AddCommand( "Objects", false, "delete object name", "delete object name [--regex] ", "Delete a scene object by name.", - "If --regex is specified then the name is treatead as a regular expression", + "Command will ask for confirmation before proceeding.\n" + + "If --regex is specified then the name is treatead as a regular expression", HandleDeleteObject); m_console.Commands.AddCommand( "Objects", false, "delete object outside", "delete object outside", - "Delete all scene objects outside region boundaries", HandleDeleteObject); + "Delete all scene objects outside region boundaries", + "Command will ask for confirmation before proceeding.", + HandleDeleteObject); + + m_console.Commands.AddCommand( + "Objects", + false, + "delete object pos", + "delete object pos to ", + "Delete scene objects within the given area.", + "Each component of the coord is comma separated. There must be no spaces between the commas.\n" + + "If you don't care about the z component you can simply omit it.\n" + + "If you don't care about the x or y components then you can leave them blank (though a comma is still required)\n" + + "If you want to specify the maxmimum value of a component then you can use ~ instead of a number\n" + + "If you want to specify the minimum value of a component then you can use -~ instead of a number\n" + + "e.g.\n" + + "delete object pos 20,20,20 to 40,40,40\n" + + "delete object pos 20,20 to 40,40\n" + + "delete object pos ,20,20 to ,40,40\n" + + "delete object pos ,,30 to ,,~\n" + + "delete object pos ,,-~ to ,,30", + HandleDeleteObject); m_console.Commands.AddCommand( "Objects", false, "show object uuid", - "show object uuid ", - "Show details of a scene object with the given UUID", HandleShowObjectByUuid); + "show object uuid [--full] ", + "Show details of a scene object with the given UUID", + "The --full option will print out information on all the parts of the object.\n" + + "For yet more detailed part information, use the \"show part\" commands.", + HandleShowObjectByUuid); m_console.Commands.AddCommand( "Objects", false, "show object name", - "show object name [--regex] ", + "show object name [--full] [--regex] ", "Show details of scene objects with the given name.", - "If --regex is specified then the name is treatead as a regular expression", + "The --full option will print out information on all the parts of the object.\n" + + "For yet more detailed part information, use the \"show part\" commands.\n" + + "If --regex is specified then the name is treatead as a regular expression.", HandleShowObjectByName); + m_console.Commands.AddCommand( + "Objects", + false, + "show object pos", + "show object pos [--full] to ", + "Show details of scene objects within the given area.", + "The --full option will print out information on all the parts of the object.\n" + + "For yet more detailed part information, use the \"show part\" commands.\n" + + "Each component of the coord is comma separated. There must be no spaces between the commas.\n" + + "If you don't care about the z component you can simply omit it.\n" + + "If you don't care about the x or y components then you can leave them blank (though a comma is still required)\n" + + "If you want to specify the maxmimum value of a component then you can use ~ instead of a number\n" + + "If you want to specify the minimum value of a component then you can use -~ instead of a number\n" + + "e.g.\n" + + "show object pos 20,20,20 to 40,40,40\n" + + "show object pos 20,20 to 40,40\n" + + "show object pos ,20,20 to ,40,40\n" + + "show object pos ,,30 to ,,~\n" + + "show object pos ,,-~ to ,,30", + HandleShowObjectByPos); + m_console.Commands.AddCommand( "Objects", false, @@ -138,6 +192,25 @@ namespace OpenSim.Region.CoreModules.World.Objects.Commands "Show details of scene object parts with the given name.", "If --regex is specified then the name is treatead as a regular expression", HandleShowPartByName); + + m_console.Commands.AddCommand( + "Objects", + false, + "show part pos", + "show part pos to ", + "Show details of scene object parts within the given area.", + "Each component of the coord is comma separated. There must be no spaces between the commas.\n" + + "If you don't care about the z component you can simply omit it.\n" + + "If you don't care about the x or y components then you can leave them blank (though a comma is still required)\n" + + "If you want to specify the maxmimum value of a component then you can use ~ instead of a number\n" + + "If you want to specify the minimum value of a component then you can use -~ instead of a number\n" + + "e.g.\n" + + "show object pos 20,20,20 to 40,40,40\n" + + "show object pos 20,20 to 40,40\n" + + "show object pos ,20,20 to ,40,40\n" + + "show object pos ,,30 to ,,~\n" + + "show object pos ,,-~ to ,,30", + HandleShowPartByPos); } public void RemoveRegion(Scene scene) @@ -150,21 +223,68 @@ namespace OpenSim.Region.CoreModules.World.Objects.Commands // m_log.DebugFormat("[OBJECTS COMMANDS MODULE]: REGION {0} LOADED", scene.RegionInfo.RegionName); } - private void HandleShowObjectByUuid(string module, string[] cmd) + /// + /// Outputs the sogs to console. + /// + /// + /// If true then output all part details. If false then output summary. + private void OutputSogsToConsole(Predicate searchPredicate, bool showFull) + { + List sceneObjects = m_scene.GetSceneObjectGroups().FindAll(searchPredicate); + + StringBuilder sb = new StringBuilder(); + + foreach (SceneObjectGroup so in sceneObjects) + { + AddSceneObjectReport(sb, so, showFull); + sb.Append("\n"); + } + + sb.AppendFormat("{0} object(s) found in {1}\n", sceneObjects.Count, m_scene.Name); + + m_console.OutputFormat(sb.ToString()); + } + + private void OutputSopsToConsole(Predicate searchPredicate, bool showFull) + { + List sceneObjects = m_scene.GetSceneObjectGroups(); + List parts = new List(); + + sceneObjects.ForEach(so => parts.AddRange(Array.FindAll(so.Parts, searchPredicate))); + + StringBuilder sb = new StringBuilder(); + + foreach (SceneObjectPart part in parts) + { + AddScenePartReport(sb, part, showFull); + sb.Append("\n"); + } + + sb.AppendFormat("{0} parts found in {1}\n", parts.Count, m_scene.Name); + + m_console.OutputFormat(sb.ToString()); + } + + private void HandleShowObjectByUuid(string module, string[] cmdparams) { if (!(m_console.ConsoleScene == null || m_console.ConsoleScene == m_scene)) return; - if (cmd.Length < 4) + bool showFull = false; + OptionSet options = new OptionSet().Add("full", v => showFull = v != null ); + + List mainParams = options.Parse(cmdparams); + + if (mainParams.Count < 4) { m_console.OutputFormat("Usage: show object uuid "); return; } UUID objectUuid; - if (!UUID.TryParse(cmd[3], out objectUuid)) + if (!UUID.TryParse(mainParams[3], out objectUuid)) { - m_console.OutputFormat("{0} is not a valid uuid", cmd[3]); + m_console.OutputFormat("{0} is not a valid uuid", mainParams[3]); return; } @@ -177,7 +297,7 @@ namespace OpenSim.Region.CoreModules.World.Objects.Commands } StringBuilder sb = new StringBuilder(); - AddSceneObjectReport(sb, so); + AddSceneObjectReport(sb, so, showFull); m_console.OutputFormat(sb.ToString()); } @@ -187,66 +307,85 @@ namespace OpenSim.Region.CoreModules.World.Objects.Commands if (!(m_console.ConsoleScene == null || m_console.ConsoleScene == m_scene)) return; + bool showFull = false; bool useRegex = false; - OptionSet options = new OptionSet().Add("regex", v=> useRegex = v != null ); + OptionSet options = new OptionSet(); + options.Add("full", v => showFull = v != null ); + options.Add("regex", v => useRegex = v != null ); List mainParams = options.Parse(cmdparams); if (mainParams.Count < 4) { - m_console.OutputFormat("Usage: show object name [--regex] "); + m_console.OutputFormat("Usage: show object name [--full] [--regex] "); return; } string name = mainParams[3]; - List sceneObjects = new List(); - Action searchAction; + Predicate searchPredicate; if (useRegex) { Regex nameRegex = new Regex(name); - searchAction = so => { if (nameRegex.IsMatch(so.Name)) { sceneObjects.Add(so); }}; + searchPredicate = so => nameRegex.IsMatch(so.Name); } else { - searchAction = so => { if (so.Name == name) { sceneObjects.Add(so); }}; + searchPredicate = so => so.Name == name; } - m_scene.ForEachSOG(searchAction); - - if (sceneObjects.Count == 0) - { - m_console.OutputFormat("No objects with name {0} found in {1}", name, m_scene.RegionInfo.RegionName); - return; - } - - StringBuilder sb = new StringBuilder(); - - foreach (SceneObjectGroup so in sceneObjects) - { - AddSceneObjectReport(sb, so); - sb.Append("\n"); - } - - m_console.OutputFormat(sb.ToString()); + OutputSogsToConsole(searchPredicate, showFull); } - private void HandleShowPartByUuid(string module, string[] cmd) + private void HandleShowObjectByPos(string module, string[] cmdparams) { if (!(m_console.ConsoleScene == null || m_console.ConsoleScene == m_scene)) return; - if (cmd.Length < 4) + bool showFull = false; + OptionSet options = new OptionSet().Add("full", v => showFull = v != null ); + + List mainParams = options.Parse(cmdparams); + + if (mainParams.Count < 5) { - m_console.OutputFormat("Usage: show part uuid "); + m_console.OutputFormat("Usage: show object pos [--full] to "); + return; + } + + Vector3 startVector, endVector; + + if (!TryParseVectorRange(cmdparams.Skip(3).Take(3), out startVector, out endVector)) + return; + + Predicate searchPredicate + = so => Util.IsInsideBox(so.AbsolutePosition, startVector, endVector); + + OutputSogsToConsole(searchPredicate, showFull); + } + + private void HandleShowPartByUuid(string module, string[] cmdparams) + { + if (!(m_console.ConsoleScene == null || m_console.ConsoleScene == m_scene)) + return; + +// bool showFull = false; + OptionSet options = new OptionSet(); +// options.Add("full", v => showFull = v != null ); + + List mainParams = options.Parse(cmdparams); + + if (mainParams.Count < 4) + { + m_console.OutputFormat("Usage: show part uuid [--full] "); return; } UUID objectUuid; - if (!UUID.TryParse(cmd[3], out objectUuid)) + if (!UUID.TryParse(mainParams[3], out objectUuid)) { - m_console.OutputFormat("{0} is not a valid uuid", cmd[3]); + m_console.OutputFormat("{0} is not a valid uuid", mainParams[3]); return; } @@ -259,84 +398,188 @@ namespace OpenSim.Region.CoreModules.World.Objects.Commands } StringBuilder sb = new StringBuilder(); - AddScenePartReport(sb, sop); + AddScenePartReport(sb, sop, true); m_console.OutputFormat(sb.ToString()); } + private void HandleShowPartByPos(string module, string[] cmdparams) + { + if (!(m_console.ConsoleScene == null || m_console.ConsoleScene == m_scene)) + return; + +// bool showFull = false; + OptionSet options = new OptionSet(); +// options.Add("full", v => showFull = v != null ); + + List mainParams = options.Parse(cmdparams); + + if (mainParams.Count < 5) + { + m_console.OutputFormat("Usage: show part pos [--full] to "); + return; + } + + string rawConsoleStartVector = mainParams[3]; + Vector3 startVector; + + if (!ConsoleUtil.TryParseConsoleMinVector(rawConsoleStartVector, out startVector)) + { + m_console.OutputFormat("Error: Start vector {0} does not have a valid format", rawConsoleStartVector); + return; + } + + string rawConsoleEndVector = mainParams[5]; + Vector3 endVector; + + if (!ConsoleUtil.TryParseConsoleMaxVector(rawConsoleEndVector, out endVector)) + { + m_console.OutputFormat("Error: End vector {0} does not have a valid format", rawConsoleEndVector); + return; + } + + OutputSopsToConsole(sop => Util.IsInsideBox(sop.AbsolutePosition, startVector, endVector), true); + } + private void HandleShowPartByName(string module, string[] cmdparams) { if (!(m_console.ConsoleScene == null || m_console.ConsoleScene == m_scene)) return; +// bool showFull = false; bool useRegex = false; - OptionSet options = new OptionSet().Add("regex", v=> useRegex = v != null ); + OptionSet options = new OptionSet(); +// options.Add("full", v => showFull = v != null ); + options.Add("regex", v => useRegex = v != null ); List mainParams = options.Parse(cmdparams); if (mainParams.Count < 4) { - m_console.OutputFormat("Usage: show part name [--regex] "); + m_console.OutputFormat("Usage: show part name [--full] [--regex] "); return; } string name = mainParams[3]; - List parts = new List(); - - Action searchAction; + Predicate searchPredicate; if (useRegex) { Regex nameRegex = new Regex(name); - searchAction = so => so.ForEachPart(sop => { if (nameRegex.IsMatch(sop.Name)) { parts.Add(sop); } }); + searchPredicate = sop => nameRegex.IsMatch(sop.Name); } else { - searchAction = so => so.ForEachPart(sop => { if (sop.Name == name) { parts.Add(sop); } }); + searchPredicate = sop => sop.Name == name; } - m_scene.ForEachSOG(searchAction); - - if (parts.Count == 0) - { - m_console.OutputFormat("No parts with name {0} found in {1}", name, m_scene.RegionInfo.RegionName); - return; - } - - StringBuilder sb = new StringBuilder(); - - foreach (SceneObjectPart part in parts) - { - AddScenePartReport(sb, part); - sb.Append("\n"); - } - - m_console.OutputFormat(sb.ToString()); + OutputSopsToConsole(searchPredicate, true); } - private StringBuilder AddSceneObjectReport(StringBuilder sb, SceneObjectGroup so) + /// + /// Append a scene object report to an input StringBuilder + /// + /// + /// + /// + /// + /// If true then information on all parts of an object is appended. + /// If false then only summary information about an object is appended. + /// + private StringBuilder AddSceneObjectReport(StringBuilder sb, SceneObjectGroup so, bool showFull) { - sb.AppendFormat("Name: {0}\n", so.Name); - sb.AppendFormat("Description: {0}\n", so.Description); - sb.AppendFormat("Location: {0} @ {1}\n", so.AbsolutePosition, so.Scene.RegionInfo.RegionName); - sb.AppendFormat("Parts: {0}\n", so.PrimCount); - sb.AppendFormat("Flags: {0}\n", so.RootPart.Flags); + if (showFull) + { + foreach (SceneObjectPart sop in so.Parts) + { + AddScenePartReport(sb, sop, false); + sb.Append("\n"); + } + } + else + { + AddSummarySceneObjectReport(sb, so); + } return sb; } - private StringBuilder AddScenePartReport(StringBuilder sb, SceneObjectPart sop) + private StringBuilder AddSummarySceneObjectReport(StringBuilder sb, SceneObjectGroup so) { - sb.AppendFormat("Name: {0}\n", sop.Name); - sb.AppendFormat("Description: {0}\n", sop.Description); - sb.AppendFormat("Location: {0} @ {1}\n", sop.AbsolutePosition, sop.ParentGroup.Scene.RegionInfo.RegionName); - sb.AppendFormat("Parent: {0}", - sop.IsRoot ? "Is Root\n" : string.Format("{0} {1}\n", sop.ParentGroup.Name, sop.ParentGroup.UUID)); - sb.AppendFormat("Link number: {0}\n", sop.LinkNum); - sb.AppendFormat("Flags: {0}\n", sop.Flags); + ConsoleDisplayList cdl = new ConsoleDisplayList(); + cdl.AddRow("Name", so.Name); + cdl.AddRow("Descrition", so.Description); + cdl.AddRow("Local ID", so.LocalId); + cdl.AddRow("UUID", so.UUID); + cdl.AddRow("Location", string.Format("{0} @ {1}", so.AbsolutePosition, so.Scene.Name)); + cdl.AddRow("Parts", so.PrimCount); + cdl.AddRow("Flags", so.RootPart.Flags); - return sb; + return sb.Append(cdl.ToString()); + } + + /// + /// Append a scene object part report to an input StringBuilder + /// + /// + /// + /// + /// + /// If true then information on each inventory item will be shown. + /// If false then only summary inventory information is shown. + /// + private StringBuilder AddScenePartReport(StringBuilder sb, SceneObjectPart sop, bool showFull) + { + ConsoleDisplayList cdl = new ConsoleDisplayList(); + cdl.AddRow("Name", sop.Name); + cdl.AddRow("Description", sop.Description); + cdl.AddRow("Local ID", sop.LocalId); + cdl.AddRow("UUID", sop.UUID); + cdl.AddRow("Location", string.Format("{0} @ {1}", sop.AbsolutePosition, sop.ParentGroup.Scene.Name)); + cdl.AddRow( + "Parent", + sop.IsRoot ? "Is Root" : string.Format("{0} {1}", sop.ParentGroup.Name, sop.ParentGroup.UUID)); + cdl.AddRow("Link number", sop.LinkNum); + cdl.AddRow("Flags", sop.Flags); + + object itemsOutput; + if (showFull) + { + StringBuilder itemsSb = new StringBuilder("\n"); + itemsOutput = AddScenePartItemsReport(itemsSb, sop.Inventory).ToString(); + } + else + { + itemsOutput = sop.Inventory.Count; + } + + + cdl.AddRow("Items", itemsOutput); + + return sb.Append(cdl.ToString()); + } + + private StringBuilder AddScenePartItemsReport(StringBuilder sb, IEntityInventory inv) + { + ConsoleDisplayTable cdt = new ConsoleDisplayTable(); + cdt.Indent = 2; + + cdt.AddColumn("Name", 50); + cdt.AddColumn("Type", 12); + cdt.AddColumn("Running", 7); + cdt.AddColumn("Item UUID", 36); + cdt.AddColumn("Asset UUID", 36); + + foreach (TaskInventoryItem item in inv.GetInventoryItems()) + cdt.AddRow( + item.Name, + ((InventoryType)item.InvType).ToString(), + (InventoryType)item.InvType == InventoryType.LSL ? item.ScriptRunning.ToString() : "n/a", + item.ItemID.ToString(), + item.AssetID.ToString()); + + return sb.Append(cdt.ToString()); } private void HandleDeleteObject(string module, string[] cmd) @@ -450,6 +693,10 @@ namespace OpenSim.Region.CoreModules.World.Objects.Commands break; + case "pos": + deletes = GetDeleteCandidatesByPos(module, cmd); + break; + default: m_console.OutputFormat("Unrecognized mode {0}", mode); return; @@ -464,7 +711,7 @@ namespace OpenSim.Region.CoreModules.World.Objects.Commands string.Format( "Are you sure that you want to delete {0} objects from {1}", deletes.Count, m_scene.RegionInfo.RegionName), - "n"); + "y/N"); if (response.ToLower() != "y") { @@ -486,9 +733,6 @@ namespace OpenSim.Region.CoreModules.World.Objects.Commands private List GetDeleteCandidatesByName(string module, string[] cmdparams) { - if (!(m_console.ConsoleScene == null || m_console.ConsoleScene == m_scene)) - return null; - bool useRegex = false; OptionSet options = new OptionSet().Add("regex", v=> useRegex = v != null ); @@ -522,5 +766,52 @@ namespace OpenSim.Region.CoreModules.World.Objects.Commands return sceneObjects; } + + /// + /// Get scene object delete candidates by position + /// + /// + /// + /// null if parsing failed on one of the arguments, otherwise a list of objects to delete. If there + /// are no objects to delete then the list will be empty./returns> + private List GetDeleteCandidatesByPos(string module, string[] cmdparams) + { + if (cmdparams.Length < 5) + { + m_console.OutputFormat("Usage: delete object pos to "); + return null; + } + + Vector3 startVector, endVector; + + if (!TryParseVectorRange(cmdparams.Skip(3).Take(3), out startVector, out endVector)) + return null; + + return m_scene.GetSceneObjectGroups().FindAll( + so => !so.IsAttachment && Util.IsInsideBox(so.AbsolutePosition, startVector, endVector)); + } + + private bool TryParseVectorRange(IEnumerable rawComponents, out Vector3 startVector, out Vector3 endVector) + { + string rawConsoleStartVector = rawComponents.Take(1).Single(); + + if (!ConsoleUtil.TryParseConsoleMinVector(rawConsoleStartVector, out startVector)) + { + m_console.OutputFormat("Error: Start vector {0} does not have a valid format", rawConsoleStartVector); + endVector = Vector3.Zero; + + return false; + } + + string rawConsoleEndVector = rawComponents.Skip(1).Take(1).Single(); + + if (!ConsoleUtil.TryParseConsoleMaxVector(rawConsoleEndVector, out endVector)) + { + m_console.OutputFormat("Error: End vector {0} does not have a valid format", rawConsoleEndVector); + return false; + } + + return true; + } } } \ No newline at end of file diff --git a/OpenSim/Region/Framework/Interfaces/IAttachmentsModule.cs b/OpenSim/Region/Framework/Interfaces/IAttachmentsModule.cs index 8155eabbbe..620ec22cc8 100644 --- a/OpenSim/Region/Framework/Interfaces/IAttachmentsModule.cs +++ b/OpenSim/Region/Framework/Interfaces/IAttachmentsModule.cs @@ -108,6 +108,15 @@ namespace OpenSim.Region.Framework.Interfaces /// void DetachSingleAttachmentToGround(IScenePresence sp, uint objectLocalID); + /// + /// Detach the given item to the ground at the specified coordinates & rotation + /// + /// + /// + /// + /// + void DetachSingleAttachmentToGround(IScenePresence sp, uint objectLocalID, Vector3 absolutePos, Quaternion absoluteRot); + /// /// Detach the given attachment so that it remains in the user's inventory. /// diff --git a/OpenSim/Region/Framework/Interfaces/IEntityInventory.cs b/OpenSim/Region/Framework/Interfaces/IEntityInventory.cs index 8d6284739b..c457b2f2f5 100644 --- a/OpenSim/Region/Framework/Interfaces/IEntityInventory.cs +++ b/OpenSim/Region/Framework/Interfaces/IEntityInventory.cs @@ -267,18 +267,26 @@ namespace OpenSim.Region.Framework.Interfaces void ApplyGodPermissions(uint perms); + /// + /// Number of items in this inventory. + /// + int Count { get; } + /// /// Returns true if this inventory contains any scripts /// bool ContainsScripts(); /// - /// Returns the count of scripts contained - /// + /// Number of scripts in this inventory. + /// + /// + /// Includes both running and non running scripts. + /// int ScriptCount(); /// - /// Returns the count of running scripts contained + /// Number of running scripts in this inventory. /// int RunningScriptCount(); diff --git a/OpenSim/Region/Framework/Interfaces/IUserManagement.cs b/OpenSim/Region/Framework/Interfaces/IUserManagement.cs index 24cd06978e..f8088c3d3c 100644 --- a/OpenSim/Region/Framework/Interfaces/IUserManagement.cs +++ b/OpenSim/Region/Framework/Interfaces/IUserManagement.cs @@ -1,4 +1,31 @@ -using System; +/* + * 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 OpenMetaverse; diff --git a/OpenSim/Region/Framework/Scenes/Animation/AnimationSet.cs b/OpenSim/Region/Framework/Scenes/Animation/AnimationSet.cs index 33041e9ac7..ad421eeedb 100644 --- a/OpenSim/Region/Framework/Scenes/Animation/AnimationSet.cs +++ b/OpenSim/Region/Framework/Scenes/Animation/AnimationSet.cs @@ -87,7 +87,7 @@ namespace OpenSim.Region.Framework.Scenes.Animation { if (m_defaultAnimation.AnimID == animID) { - ResetDefaultAnimation(); + m_defaultAnimation = new OpenSim.Framework.Animation(UUID.Zero, 1, UUID.Zero); } else if (HasAnimation(animID)) { @@ -149,19 +149,26 @@ namespace OpenSim.Region.Framework.Scenes.Animation { lock (m_animations) { - animIDs = new UUID[m_animations.Count + 1]; - sequenceNums = new int[m_animations.Count + 1]; - objectIDs = new UUID[m_animations.Count + 1]; + int defaultSize = 0; + if (m_defaultAnimation.AnimID != UUID.Zero) + defaultSize++; - animIDs[0] = m_defaultAnimation.AnimID; - sequenceNums[0] = m_defaultAnimation.SequenceNum; - objectIDs[0] = m_defaultAnimation.ObjectID; + animIDs = new UUID[m_animations.Count + defaultSize]; + sequenceNums = new int[m_animations.Count + defaultSize]; + objectIDs = new UUID[m_animations.Count + defaultSize]; + + if (m_defaultAnimation.AnimID != UUID.Zero) + { + animIDs[0] = m_defaultAnimation.AnimID; + sequenceNums[0] = m_defaultAnimation.SequenceNum; + objectIDs[0] = m_defaultAnimation.ObjectID; + } for (int i = 0; i < m_animations.Count; ++i) { - animIDs[i + 1] = m_animations[i].AnimID; - sequenceNums[i + 1] = m_animations[i].SequenceNum; - objectIDs[i + 1] = m_animations[i].ObjectID; + animIDs[i + defaultSize] = m_animations[i].AnimID; + sequenceNums[i + defaultSize] = m_animations[i].SequenceNum; + objectIDs[i + defaultSize] = m_animations[i].ObjectID; } } } diff --git a/OpenSim/Region/Framework/Scenes/Animation/ScenePresenceAnimator.cs b/OpenSim/Region/Framework/Scenes/Animation/ScenePresenceAnimator.cs index ff53f45f71..bb33f07b77 100644 --- a/OpenSim/Region/Framework/Scenes/Animation/ScenePresenceAnimator.cs +++ b/OpenSim/Region/Framework/Scenes/Animation/ScenePresenceAnimator.cs @@ -408,13 +408,19 @@ namespace OpenSim.Region.Framework.Scenes.Animation { lock (m_animations) { - CurrentMovementAnimation = DetermineMovementAnimation(); + string newMovementAnimation = DetermineMovementAnimation(); + if (CurrentMovementAnimation != newMovementAnimation) + { + CurrentMovementAnimation = DetermineMovementAnimation(); -// m_log.DebugFormat( -// "[SCENE PRESENCE ANIMATOR]: Determined animation {0} for {1} in UpdateMovementAnimations()", -// CurrentMovementAnimation, m_scenePresence.Name); +// m_log.DebugFormat( +// "[SCENE PRESENCE ANIMATOR]: Determined animation {0} for {1} in UpdateMovementAnimations()", +// CurrentMovementAnimation, m_scenePresence.Name); - TrySetMovementAnimation(CurrentMovementAnimation); + // Only set it if it's actually changed, give a script + // a chance to stop a default animation + TrySetMovementAnimation(CurrentMovementAnimation); + } } } @@ -536,4 +542,4 @@ namespace OpenSim.Region.Framework.Scenes.Animation SendAnimPack(animIDs, sequenceNums, objectIDs); } } -} \ No newline at end of file +} diff --git a/OpenSim/Region/Framework/Scenes/EventManager.cs b/OpenSim/Region/Framework/Scenes/EventManager.cs index e25fa94191..4c49b7168d 100644 --- a/OpenSim/Region/Framework/Scenes/EventManager.cs +++ b/OpenSim/Region/Framework/Scenes/EventManager.cs @@ -121,13 +121,21 @@ namespace OpenSim.Region.Framework.Scenes /// /// /// This is triggered for both child and root agent client connections. + /// /// Triggered before OnClientLogin. + /// + /// This is triggered under per-agent lock. So if you want to perform any long-running operations, please + /// do this on a separate thread. /// public event OnNewClientDelegate OnNewClient; /// /// Fired if the client entering this sim is doing so as a new login /// + /// + /// This is triggered under per-agent lock. So if you want to perform any long-running operations, please + /// do this on a separate thread. + /// public event Action OnClientLogin; public delegate void OnNewPresenceDelegate(ScenePresence presence); @@ -149,6 +157,9 @@ namespace OpenSim.Region.Framework.Scenes /// /// Triggered in which is used by both /// users and NPCs + /// + /// Triggered under per-agent lock. So if you want to perform any long-running operations, please + /// do this on a separate thread. /// public event OnRemovePresenceDelegate OnRemovePresence; @@ -425,6 +436,9 @@ namespace OpenSim.Region.Framework.Scenes /// /// /// At the point of firing, the scene still contains the client's scene presence. + /// + /// This is triggered under per-agent lock. So if you want to perform any long-running operations, please + /// do this on a separate thread. /// public event ClientClosed OnClientClosed; @@ -913,7 +927,7 @@ namespace OpenSim.Region.Framework.Scenes public event SceneObjectPartCopyDelegate OnSceneObjectPartCopy; public delegate void SceneObjectPartCopyDelegate(SceneObjectPart copy, SceneObjectPart original, bool userExposed); - public delegate void SceneObjectPartUpdated(SceneObjectPart sop); + public delegate void SceneObjectPartUpdated(SceneObjectPart sop, bool full); public event SceneObjectPartUpdated OnSceneObjectPartUpdated; public delegate void ScenePresenceUpdated(ScenePresence sp); @@ -2837,7 +2851,7 @@ namespace OpenSim.Region.Framework.Scenes } } - public void TriggerSceneObjectPartUpdated(SceneObjectPart sop) + public void TriggerSceneObjectPartUpdated(SceneObjectPart sop, bool full) { SceneObjectPartUpdated handler = OnSceneObjectPartUpdated; if (handler != null) @@ -2846,7 +2860,7 @@ namespace OpenSim.Region.Framework.Scenes { try { - d(sop); + d(sop, full); } catch (Exception e) { diff --git a/OpenSim/Region/Framework/Scenes/Scene.Inventory.cs b/OpenSim/Region/Framework/Scenes/Scene.Inventory.cs index 3b59dc4866..b23ddb4330 100644 --- a/OpenSim/Region/Framework/Scenes/Scene.Inventory.cs +++ b/OpenSim/Region/Framework/Scenes/Scene.Inventory.cs @@ -1424,7 +1424,7 @@ namespace OpenSim.Region.Framework.Scenes return newFolderID; } - private void SendInventoryUpdate(IClientAPI client, InventoryFolderBase folder, bool fetchFolders, bool fetchItems) + public void SendInventoryUpdate(IClientAPI client, InventoryFolderBase folder, bool fetchFolders, bool fetchItems) { if (folder == null) return; diff --git a/OpenSim/Region/Framework/Scenes/Scene.cs b/OpenSim/Region/Framework/Scenes/Scene.cs index 2e03874c20..5f45529ca8 100644 --- a/OpenSim/Region/Framework/Scenes/Scene.cs +++ b/OpenSim/Region/Framework/Scenes/Scene.cs @@ -79,6 +79,11 @@ namespace OpenSim.Region.Framework.Scenes public SynchronizeSceneHandler SynchronizeScene; + /// + /// Used to prevent simultaneous calls to RemoveClient() for the same agent from interfering with each other. + /// + private object m_removeClientLock = new object(); + /// /// Statistical information for this scene. /// @@ -301,6 +306,31 @@ namespace OpenSim.Region.Framework.Scenes } private volatile bool m_shuttingDown; + /// + /// Is the scene active? + /// + /// + /// If false, maintenance and update loops are not being run. Updates can still be triggered manually if + /// the scene is not active. + /// + public bool Active + { + get { return m_active; } + set + { + if (value) + { + if (!m_active) + Start(); + } + else + { + m_active = false; + } + } + } + private volatile bool m_active; + // private int m_lastUpdate; // private bool m_firstHeartbeat = true; @@ -801,13 +831,6 @@ namespace OpenSim.Region.Framework.Scenes SpawnPointRouting = startupConfig.GetString("SpawnPointRouting", "closest"); TelehubAllowLandmarks = startupConfig.GetBoolean("TelehubAllowLandmark", false); - IConfig packetConfig = m_config.Configs["PacketPool"]; - if (packetConfig != null) - { - PacketPool.Instance.RecyclePackets = packetConfig.GetBoolean("RecyclePackets", true); - PacketPool.Instance.RecycleDataBlocks = packetConfig.GetBoolean("RecycleDataBlocks", true); - } - m_strictAccessControl = startupConfig.GetBoolean("StrictAccessControl", m_strictAccessControl); m_generateMaptiles = startupConfig.GetBoolean("GenerateMaptiles", true); @@ -1161,6 +1184,14 @@ namespace OpenSim.Region.Framework.Scenes public void SetSceneCoreDebug(Dictionary options) { + if (options.ContainsKey("active")) + { + bool active; + + if (bool.TryParse(options["active"], out active)) + Active = active; + } + if (options.ContainsKey("scripting")) { bool enableScripts = true; @@ -1300,6 +1331,8 @@ namespace OpenSim.Region.Framework.Scenes /// public void Start() { + m_active = true; + // m_log.DebugFormat("[SCENE]: Starting Heartbeat timer for {0}", RegionInfo.RegionName); //m_heartbeatTimer.Enabled = true; @@ -1341,7 +1374,7 @@ namespace OpenSim.Region.Framework.Scenes #region Update Methods /// - /// Performs per-frame updates regularly + /// Activate the various loops necessary to continually update the scene. /// private void Heartbeat() { @@ -1398,7 +1431,7 @@ namespace OpenSim.Region.Framework.Scenes List coarseLocations; List avatarUUIDs; - while (!m_shuttingDown && (endRun == null || MaintenanceRun < endRun)) + while (!m_shuttingDown && ((endRun == null && Active) || MaintenanceRun < endRun)) { runtc = Util.EnvironmentTickCount(); ++MaintenanceRun; @@ -1457,7 +1490,7 @@ namespace OpenSim.Region.Framework.Scenes int previousFrameTick, tmpMS; int maintc = Util.EnvironmentTickCount(); - while (!m_shuttingDown && (endFrame == null || Frame < endFrame)) + while (!m_shuttingDown && ((endFrame == null && Active) || Frame < endFrame)) { ++Frame; @@ -2146,7 +2179,14 @@ namespace OpenSim.Region.Framework.Scenes public bool AddRestoredSceneObject( SceneObjectGroup sceneObject, bool attachToBackup, bool alreadyPersisted, bool sendClientUpdates) { - return m_sceneGraph.AddRestoredSceneObject(sceneObject, attachToBackup, alreadyPersisted, sendClientUpdates); + if (m_sceneGraph.AddRestoredSceneObject(sceneObject, attachToBackup, alreadyPersisted, sendClientUpdates)) + { + EventManager.TriggerObjectAddedToScene(sceneObject); + return true; + } + + return false; + } /// @@ -2709,69 +2749,89 @@ namespace OpenSim.Region.Framework.Scenes public override ISceneAgent AddNewClient(IClientAPI client, PresenceType type) { + ScenePresence sp; + bool vialogin; + // Validation occurs in LLUDPServer + // + // XXX: A race condition exists here where two simultaneous calls to AddNewClient can interfere with + // each other. In practice, this does not currently occur in the code. AgentCircuitData aCircuit = m_authenticateHandler.GetAgentCircuitData(client.CircuitCode); - bool vialogin - = (aCircuit.teleportFlags & (uint)Constants.TeleportFlags.ViaHGLogin) != 0 - || (aCircuit.teleportFlags & (uint)Constants.TeleportFlags.ViaLogin) != 0; - -// CheckHeartbeat(); - - ScenePresence sp = GetScenePresence(client.AgentId); - - // XXX: Not sure how good it is to add a new client if a scene presence already exists. Possibly this - // could occur if a viewer crashes and relogs before the old client is kicked out. But this could cause - // other problems, and possible the code calling AddNewClient() should ensure that no client is already - // connected. - if (sp == null) + // We lock here on AgentCircuitData to prevent a race condition between the thread adding a new connection + // and a simultaneous one that removes it (as can happen if the client is closed at a particular point + // whilst connecting). + // + // It would be easier to lock across all NewUserConnection(), AddNewClient() and + // RemoveClient() calls for all agents, but this would allow a slow call (e.g. because of slow service + // response in some module listening to AddNewClient()) from holding up unrelated agent calls. + // + // In practice, the lock (this) in LLUDPServer.AddNewClient() currently lock across all + // AddNewClient() operations (though not other ops). + // In the future this can be relieved once locking per agent (not necessarily on AgentCircuitData) is improved. + lock (aCircuit) { - m_log.DebugFormat( - "[SCENE]: Adding new child scene presence {0} {1} to scene {2} at pos {3}", - client.Name, client.AgentId, RegionInfo.RegionName, client.StartPos); + vialogin + = (aCircuit.teleportFlags & (uint)Constants.TeleportFlags.ViaHGLogin) != 0 + || (aCircuit.teleportFlags & (uint)Constants.TeleportFlags.ViaLogin) != 0; + + // CheckHeartbeat(); + + sp = GetScenePresence(client.AgentId); - m_clientManager.Add(client); - SubscribeToClientEvents(client); - - sp = m_sceneGraph.CreateAndAddChildScenePresence(client, aCircuit.Appearance, type); - m_eventManager.TriggerOnNewPresence(sp); - - sp.TeleportFlags = (TPFlags)aCircuit.teleportFlags; - - // The first agent upon login is a root agent by design. - // For this agent we will have to rez the attachments. - // All other AddNewClient calls find aCircuit.child to be true. - if (aCircuit.child == false) + // XXX: Not sure how good it is to add a new client if a scene presence already exists. Possibly this + // could occur if a viewer crashes and relogs before the old client is kicked out. But this could cause + // other problems, and possible the code calling AddNewClient() should ensure that no client is already + // connected. + if (sp == null) { - // We have to set SP to be a root agent here so that SP.MakeRootAgent() will later not try to - // start the scripts again (since this is done in RezAttachments()). - // XXX: This is convoluted. - sp.IsChildAgent = false; - - if (AttachmentsModule != null) - Util.FireAndForget(delegate(object o) { AttachmentsModule.RezAttachments(sp); }); + m_log.DebugFormat( + "[SCENE]: Adding new child scene presence {0} {1} to scene {2} at pos {3}", + client.Name, client.AgentId, RegionInfo.RegionName, client.StartPos); + + m_clientManager.Add(client); + SubscribeToClientEvents(client); + + sp = m_sceneGraph.CreateAndAddChildScenePresence(client, aCircuit.Appearance, type); + m_eventManager.TriggerOnNewPresence(sp); + + sp.TeleportFlags = (TPFlags)aCircuit.teleportFlags; + + // The first agent upon login is a root agent by design. + // For this agent we will have to rez the attachments. + // All other AddNewClient calls find aCircuit.child to be true. + if (aCircuit.child == false) + { + // We have to set SP to be a root agent here so that SP.MakeRootAgent() will later not try to + // start the scripts again (since this is done in RezAttachments()). + // XXX: This is convoluted. + sp.IsChildAgent = false; + + if (AttachmentsModule != null) + Util.FireAndForget(delegate(object o) { AttachmentsModule.RezAttachments(sp); }); + } } - } - else - { - m_log.WarnFormat( - "[SCENE]: Already found {0} scene presence for {1} in {2} when asked to add new scene presence", - sp.IsChildAgent ? "child" : "root", sp.Name, RegionInfo.RegionName); - } + else + { + m_log.WarnFormat( + "[SCENE]: Already found {0} scene presence for {1} in {2} when asked to add new scene presence", + sp.IsChildAgent ? "child" : "root", sp.Name, RegionInfo.RegionName); + } + + // We must set this here so that TriggerOnNewClient and TriggerOnClientLogin can determine whether the + // client is for a root or child agent. + client.SceneAgent = sp; - // We must set this here so that TriggerOnNewClient and TriggerOnClientLogin can determine whether the - // client is for a root or child agent. - client.SceneAgent = sp; + // Cache the user's name + CacheUserName(sp, aCircuit); + + EventManager.TriggerOnNewClient(client); + if (vialogin) + EventManager.TriggerOnClientLogin(client); + } m_LastLogin = Util.EnvironmentTickCount(); - // Cache the user's name - CacheUserName(sp, aCircuit); - - EventManager.TriggerOnNewClient(client); - if (vialogin) - EventManager.TriggerOnClientLogin(client); - return sp; } @@ -3300,109 +3360,129 @@ namespace OpenSim.Region.Framework.Scenes { // CheckHeartbeat(); bool isChildAgent = false; - ScenePresence avatar = GetScenePresence(agentID); + AgentCircuitData acd; - if (avatar == null) + lock (m_removeClientLock) { - m_log.WarnFormat( - "[SCENE]: Called RemoveClient() with agent ID {0} but no such presence is in the scene.", agentID); + acd = m_authenticateHandler.GetAgentCircuitData(agentID); - return; + 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 raec conditions if two RemoveClient() calls occurred + // simultaneously. + m_authenticateHandler.RemoveCircuit(acd.circuitcode); + } } - try + lock (acd) { - isChildAgent = avatar.IsChildAgent; - - m_log.DebugFormat( - "[SCENE]: Removing {0} agent {1} {2} from {3}", - (isChildAgent ? "child" : "root"), avatar.Name, agentID, RegionInfo.RegionName); - - // Don't do this to root agents, it's not nice for the viewer - if (closeChildAgents && isChildAgent) + ScenePresence avatar = GetScenePresence(agentID); + + if (avatar == null) { - // Tell a single agent to disconnect from the region. - IEventQueue eq = RequestModuleInterface(); - if (eq != null) - { - eq.DisableSimulator(RegionInfo.RegionHandle, avatar.UUID); - } - else - { - avatar.ControllingClient.SendShutdownConnectionNotice(); - } + m_log.WarnFormat( + "[SCENE]: Called RemoveClient() with agent ID {0} but no such presence is in the scene.", agentID); + + return; } - - // Only applies to root agents. - if (avatar.ParentID != 0) - { - avatar.StandUp(); - } - - m_sceneGraph.removeUserCount(!isChildAgent); - - // TODO: We shouldn't use closeChildAgents here - it's being used by the NPC module to stop - // unnecessary operations. This should go away once NPCs have no accompanying IClientAPI - if (closeChildAgents && CapsModule != null) - CapsModule.RemoveCaps(agentID); - - // REFACTORING PROBLEM -- well not really a problem, but just to point out that whatever - // this method is doing is HORRIBLE!!! - avatar.Scene.NeedSceneCacheClear(avatar.UUID); - - if (closeChildAgents && !isChildAgent) - { - List regions = avatar.KnownRegionHandles; - regions.Remove(RegionInfo.RegionHandle); - m_sceneGridService.SendCloseChildAgentConnections(agentID, regions); - } - - m_eventManager.TriggerClientClosed(agentID, this); - m_eventManager.TriggerOnRemovePresence(agentID); - - if (!isChildAgent) - { - if (AttachmentsModule != null) - { - AttachmentsModule.DeRezAttachments(avatar); - } - - ForEachClient( - delegate(IClientAPI client) - { - //We can safely ignore null reference exceptions. It means the avatar is dead and cleaned up anyway - try { client.SendKillObject(avatar.RegionHandle, new List { avatar.LocalId }); } - catch (NullReferenceException) { } - }); - } - - // It's possible for child agents to have transactions if changes are being made cross-border. - if (AgentTransactionsModule != null) - AgentTransactionsModule.RemoveAgentAssetTransactions(agentID); - - m_authenticateHandler.RemoveCircuit(avatar.ControllingClient.CircuitCode); - } - catch (Exception e) - { - m_log.Error( - string.Format("[SCENE]: Exception removing {0} from {1}. Cleaning up. Exception ", avatar.Name, Name), e); - } - finally - { + try { - // Always clean these structures up so that any failure above doesn't cause them to remain in the - // scene with possibly bad effects (e.g. continually timing out on unacked packets and triggering - // the same cleanup exception continually. - m_sceneGraph.RemoveScenePresence(agentID); - m_clientManager.Remove(agentID); + isChildAgent = avatar.IsChildAgent; + + m_log.DebugFormat( + "[SCENE]: Removing {0} agent {1} {2} from {3}", + (isChildAgent ? "child" : "root"), avatar.Name, agentID, RegionInfo.RegionName); - avatar.Close(); + // Don't do this to root agents, it's not nice for the viewer + if (closeChildAgents && isChildAgent) + { + // Tell a single agent to disconnect from the region. + IEventQueue eq = RequestModuleInterface(); + if (eq != null) + { + eq.DisableSimulator(RegionInfo.RegionHandle, avatar.UUID); + } + else + { + avatar.ControllingClient.SendShutdownConnectionNotice(); + } + } + + // Only applies to root agents. + if (avatar.ParentID != 0) + { + avatar.StandUp(); + } + + m_sceneGraph.removeUserCount(!isChildAgent); + + // TODO: We shouldn't use closeChildAgents here - it's being used by the NPC module to stop + // unnecessary operations. This should go away once NPCs have no accompanying IClientAPI + if (closeChildAgents && CapsModule != null) + CapsModule.RemoveCaps(agentID); + + // REFACTORING PROBLEM -- well not really a problem, but just to point out that whatever + // this method is doing is HORRIBLE!!! + avatar.Scene.NeedSceneCacheClear(avatar.UUID); + + if (closeChildAgents && !isChildAgent) + { + List regions = avatar.KnownRegionHandles; + regions.Remove(RegionInfo.RegionHandle); + m_sceneGridService.SendCloseChildAgentConnections(agentID, regions); + } + + m_eventManager.TriggerClientClosed(agentID, this); + m_eventManager.TriggerOnRemovePresence(agentID); + + if (!isChildAgent) + { + if (AttachmentsModule != null) + { + AttachmentsModule.DeRezAttachments(avatar); + } + + ForEachClient( + delegate(IClientAPI client) + { + //We can safely ignore null reference exceptions. It means the avatar is dead and cleaned up anyway + try { client.SendKillObject(avatar.RegionHandle, new List { avatar.LocalId }); } + catch (NullReferenceException) { } + }); + } + + // It's possible for child agents to have transactions if changes are being made cross-border. + if (AgentTransactionsModule != null) + AgentTransactionsModule.RemoveAgentAssetTransactions(agentID); } catch (Exception e) { m_log.Error( - string.Format("[SCENE]: Exception in final clean up of {0} in {1}. Exception ", avatar.Name, Name), e); + string.Format("[SCENE]: Exception removing {0} from {1}. Cleaning up. Exception ", avatar.Name, Name), e); + } + finally + { + try + { + // Always clean these structures up so that any failure above doesn't cause them to remain in the + // scene with possibly bad effects (e.g. continually timing out on unacked packets and triggering + // the same cleanup exception continually. + m_sceneGraph.RemoveScenePresence(agentID); + m_clientManager.Remove(agentID); + + avatar.Close(); + } + catch (Exception e) + { + m_log.Error( + string.Format("[SCENE]: Exception in final clean up of {0} in {1}. Exception ", avatar.Name, Name), e); + } } } @@ -3461,11 +3541,9 @@ namespace OpenSim.Region.Framework.Scenes /// /// Do the work necessary to initiate a new user connection for a particular scene. - /// At the moment, this consists of setting up the caps infrastructure - /// The return bool should allow for connections to be refused, but as not all calling paths - /// take proper notice of it let, we allowed banned users in still. /// /// CircuitData of the agent who is connecting + /// /// Outputs the reason for the false response on this string /// True if the region accepts this agent. False if it does not. False will /// also return a reason. @@ -3476,10 +3554,20 @@ namespace OpenSim.Region.Framework.Scenes /// /// Do the work necessary to initiate a new user connection for a particular scene. - /// At the moment, this consists of setting up the caps infrastructure + /// + /// + /// The return bool should allow for connections to be refused, but as not all calling paths + /// take proper notice of it yet, we still allowed banned users in. + /// + /// At the moment this method consists of setting up the caps infrastructure /// The return bool should allow for connections to be refused, but as not all calling paths /// take proper notice of it let, we allowed banned users in still. - /// + /// + /// This method is called by the login service (in the case of login) or another simulator (in the case of region + /// cross or teleport) to initiate the connection. It is not triggered by the viewer itself - the connection + /// 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 /// Outputs the reason for the false response on this string /// True for normal presence. False for NPC @@ -3564,88 +3652,97 @@ namespace OpenSim.Region.Framework.Scenes agent.firstname, agent.lastname, agent.Viewer); reason = "Access denied, your viewer is banned by the region owner"; return false; - } - - - ScenePresence sp = GetScenePresence(agent.AgentID); - - if (sp != null && !sp.IsChildAgent) - { - // 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); - - sp.ControllingClient.Close(true); - sp = null; } - ILandObject land = LandChannel.GetLandObject(agent.startpos.X, agent.startpos.Y); + ILandObject land; - //On login test land permisions - if (vialogin) + lock (agent) { - if (land != null && !TestLandRestrictions(agent, land, out reason)) + ScenePresence sp = GetScenePresence(agent.AgentID); + + if (sp != null && !sp.IsChildAgent) { - return false; + // 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); + + sp.ControllingClient.Close(true); + sp = null; } - } - - if (sp == null) // We don't have an [child] agent here already - { - if (requirePresenceLookup) + + land = LandChannel.GetLandObject(agent.startpos.X, agent.startpos.Y); + + //On login test land permisions + if (vialogin) { - try + if (land != null && !TestLandRestrictions(agent, land, out reason)) { - if (!VerifyUserPresence(agent, out reason)) - return false; - } catch (Exception e) - { - m_log.ErrorFormat( - "[SCENE]: Exception verifying presence {0}{1}", e.Message, e.StackTrace); return false; } } - - try + + if (sp == null) // We don't have an [child] agent here already { - if (!AuthorizeUser(agent, out reason)) + if (requirePresenceLookup) + { + try + { + if (!VerifyUserPresence(agent, out reason)) + return false; + } + catch (Exception e) + { + m_log.ErrorFormat( + "[SCENE]: Exception verifying presence {0}{1}", e.Message, e.StackTrace); + + return false; + } + } + + try + { + if (!AuthorizeUser(agent, out reason)) + return false; + } + catch (Exception e) + { + m_log.ErrorFormat( + "[SCENE]: Exception authorizing user {0}{1}", e.Message, e.StackTrace); + return false; - } catch (Exception e) - { - m_log.ErrorFormat( - "[SCENE]: Exception authorizing user {0}{1}", e.Message, e.StackTrace); - 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); - - if (CapsModule != null) - { - CapsModule.SetAgentCapsSeeds(agent); - CapsModule.CreateCaps(agent.AgentID); - } - } else - { - // Let the SP know how we got here. This has a lot of interesting - // uses down the line. - sp.TeleportFlags = (TPFlags)teleportFlags; - - if (sp.IsChildAgent) - { - m_log.DebugFormat( - "[SCENE]: Adjusting known seeds for existing agent {0} in {1}", - agent.AgentID, RegionInfo.RegionName); - - sp.AdjustKnownSeeds(); - + } + + 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); + if (CapsModule != null) + { CapsModule.SetAgentCapsSeeds(agent); + CapsModule.CreateCaps(agent.AgentID); + } + } + else + { + // Let the SP know how we got here. This has a lot of interesting + // uses down the line. + sp.TeleportFlags = (TPFlags)teleportFlags; + + if (sp.IsChildAgent) + { + m_log.DebugFormat( + "[SCENE]: Adjusting known seeds for existing agent {0} in {1}", + agent.AgentID, RegionInfo.RegionName); + + sp.AdjustKnownSeeds(); + + if (CapsModule != null) + CapsModule.SetAgentCapsSeeds(agent); + } } } @@ -4047,8 +4144,9 @@ namespace OpenSim.Region.Framework.Scenes return false; } - // We have to wait until the viewer contacts this region after receiving EAC. - // That calls AddNewClient, which finally creates the ScenePresence + // We have to wait until the viewer contacts this region + // after receiving the EnableSimulator HTTP Event Queue message. This triggers the viewer to send + // a UseCircuitCode packet which in turn calls AddNewClient which finally creates the ScenePresence. ScenePresence childAgentUpdate = WaitGetScenePresence(cAgentData.AgentID); if (childAgentUpdate != null) @@ -5545,6 +5643,9 @@ namespace OpenSim.Region.Framework.Scenes public void StoreExtraSetting(string name, string val) { + if (m_extraSettings == null) + return; + string oldVal; if (m_extraSettings.TryGetValue(name, out oldVal)) @@ -5562,6 +5663,9 @@ namespace OpenSim.Region.Framework.Scenes public void RemoveExtraSetting(string name) { + if (m_extraSettings == null) + return; + if (!m_extraSettings.ContainsKey(name)) return; diff --git a/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs b/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs index 45bbbda50a..1fa6a759e3 100644 --- a/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs +++ b/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs @@ -3432,6 +3432,7 @@ namespace OpenSim.Region.Framework.Scenes /// /// When the physics engine has finished with it, the sculpt data is discarded to save memory. /// +/* public void CheckSculptAndLoad() { if (IsDeleted) @@ -3447,7 +3448,7 @@ namespace OpenSim.Region.Framework.Scenes for (int i = 0; i < parts.Length; i++) parts[i].CheckSculptAndLoad(); } - +*/ /// /// Set the user group to which this scene object belongs. /// diff --git a/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs b/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs index 3f10b34d7b..27ef4c9b11 100644 --- a/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs +++ b/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs @@ -1014,9 +1014,9 @@ namespace OpenSim.Region.Framework.Scenes { actor.Size = m_shape.Scale; - if (Shape.SculptEntry) - CheckSculptAndLoad(); - else +// if (Shape.SculptEntry) +// CheckSculptAndLoad(); +// else ParentGroup.Scene.PhysicsScene.AddPhysicsActorTaint(actor); } } @@ -1620,12 +1620,13 @@ namespace OpenSim.Region.Framework.Scenes if (userExposed) { +/* if (dupe.m_shape.SculptEntry && dupe.m_shape.SculptTexture != UUID.Zero) { ParentGroup.Scene.AssetService.Get( dupe.m_shape.SculptTexture.ToString(), dupe, dupe.AssetReceived); } - +*/ bool UsePhysics = ((dupe.Flags & PrimFlags.Physics) != 0); dupe.DoPhysicsPropertyUpdate(UsePhysics, true); } @@ -1643,6 +1644,7 @@ namespace OpenSim.Region.Framework.Scenes /// ID of asset received /// Register /// +/* protected void AssetReceived(string id, Object sender, AssetBase asset) { if (asset != null) @@ -1652,7 +1654,7 @@ namespace OpenSim.Region.Framework.Scenes "[SCENE OBJECT PART]: Part {0} {1} requested mesh/sculpt data for asset id {2} from asset service but received no data", Name, UUID, id); } - +*/ /// /// Do a physics property update for a NINJA joint. /// @@ -1833,9 +1835,9 @@ namespace OpenSim.Region.Framework.Scenes // If this part is a sculpt then delay the physics update until we've asynchronously loaded the // mesh data. - if (Shape.SculptEntry) - CheckSculptAndLoad(); - else +// if (Shape.SculptEntry) +// CheckSculptAndLoad(); +// else ParentGroup.Scene.PhysicsScene.AddPhysicsActorTaint(pa); } } @@ -2465,7 +2467,7 @@ namespace OpenSim.Region.Framework.Scenes // UUID, Name, TimeStampFull); if (ParentGroup.Scene != null) - ParentGroup.Scene.EventManager.TriggerSceneObjectPartUpdated(this); + ParentGroup.Scene.EventManager.TriggerSceneObjectPartUpdated(this, true); } /// @@ -2499,7 +2501,7 @@ namespace OpenSim.Region.Framework.Scenes } if (ParentGroup.Scene != null) - ParentGroup.Scene.EventManager.TriggerSceneObjectPartUpdated(this); + ParentGroup.Scene.EventManager.TriggerSceneObjectPartUpdated(this, false); } public void ScriptSetPhysicsStatus(bool UsePhysics) @@ -2511,6 +2513,7 @@ namespace OpenSim.Region.Framework.Scenes /// Set sculpt and mesh data, and tell the physics engine to process the change. /// /// The mesh itself. +/* public void SculptTextureCallback(AssetBase texture) { if (m_shape.SculptEntry) @@ -2538,7 +2541,7 @@ namespace OpenSim.Region.Framework.Scenes } } } - +*/ /// /// Send a full update to the client for the given part /// @@ -3783,7 +3786,7 @@ namespace OpenSim.Region.Framework.Scenes public void UpdateExtraParam(ushort type, bool inUse, byte[] data) { m_shape.ReadInUpdateExtraParam(type, inUse, data); - +/* if (type == 0x30) { if (m_shape.SculptEntry && m_shape.SculptTexture != UUID.Zero) @@ -3791,7 +3794,7 @@ namespace OpenSim.Region.Framework.Scenes ParentGroup.Scene.AssetService.Get(m_shape.SculptTexture.ToString(), this, AssetReceived); } } - +*/ if (ParentGroup != null) { ParentGroup.HasGroupChanged = true; @@ -4025,14 +4028,6 @@ namespace OpenSim.Region.Framework.Scenes if (!wasUsingPhysics) { DoPhysicsPropertyUpdate(UsePhysics, false); - - if (!ParentGroup.IsDeleted) - { - if (LocalId == ParentGroup.RootPart.LocalId) - { - ParentGroup.CheckSculptAndLoad(); - } - } } } else @@ -4072,14 +4067,6 @@ namespace OpenSim.Region.Framework.Scenes pa.SetMaterial(Material); DoPhysicsPropertyUpdate(UsePhysics, true); - if (!ParentGroup.IsDeleted) - { - if (LocalId == ParentGroup.RootPart.LocalId) - { - ParentGroup.CheckSculptAndLoad(); - } - } - if ( ((AggregateScriptEvents & scriptEvents.collision) != 0) || ((AggregateScriptEvents & scriptEvents.collision_end) != 0) || @@ -4104,14 +4091,6 @@ namespace OpenSim.Region.Framework.Scenes else // it already has a physical representation { DoPhysicsPropertyUpdate(UsePhysics, false); // Update physical status. If it's phantom this will remove the prim - - if (!ParentGroup.IsDeleted) - { - if (LocalId == ParentGroup.RootPart.LocalId) - { - ParentGroup.CheckSculptAndLoad(); - } - } } } @@ -4341,6 +4320,7 @@ namespace OpenSim.Region.Framework.Scenes /// /// When the physics engine has finished with it, the sculpt data is discarded to save memory. /// +/* public void CheckSculptAndLoad() { // m_log.DebugFormat("Processing CheckSculptAndLoad for {0} {1}", Name, LocalId); @@ -4366,7 +4346,7 @@ namespace OpenSim.Region.Framework.Scenes } } } - +*/ /// /// Update the texture entry for this part. /// @@ -4604,6 +4584,7 @@ namespace OpenSim.Region.Framework.Scenes } Quaternion rot = Quaternion.Slerp(RotationOffset,APIDTarget,1.0f/(float)m_APIDIterations); + rot.Normalize(); UpdateRotation(rot); m_APIDIterations--; diff --git a/OpenSim/Region/Framework/Scenes/SceneObjectPartInventory.cs b/OpenSim/Region/Framework/Scenes/SceneObjectPartInventory.cs index 821fd81d1f..bdb044692b 100644 --- a/OpenSim/Region/Framework/Scenes/SceneObjectPartInventory.cs +++ b/OpenSim/Region/Framework/Scenes/SceneObjectPartInventory.cs @@ -92,6 +92,15 @@ namespace OpenSim.Region.Framework.Scenes QueryScriptStates(); } } + + public int Count + { + get + { + lock (m_items) + return m_items.Count; + } + } /// /// Constructor diff --git a/OpenSim/Region/Framework/Scenes/SimStatsReporter.cs b/OpenSim/Region/Framework/Scenes/SimStatsReporter.cs index 96317c3de3..2addb5bab8 100644 --- a/OpenSim/Region/Framework/Scenes/SimStatsReporter.cs +++ b/OpenSim/Region/Framework/Scenes/SimStatsReporter.cs @@ -47,6 +47,7 @@ namespace OpenSim.Region.Framework.Scenes = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); public const string LastReportedObjectUpdateStatName = "LastReportedObjectUpdates"; + public const string SlowFramesStatName = "SlowFrames"; public delegate void SendStatResult(SimStats stats); @@ -128,6 +129,16 @@ namespace OpenSim.Region.Framework.Scenes get { return lastReportedSimStats; } } + /// + /// Number of frames that have taken longer to process than Scene.MIN_FRAME_TIME + /// + public Stat SlowFramesStat { get; private set; } + + /// + /// The threshold at which we log a slow frame. + /// + public int SlowFramesStatReportThreshold { get; private set; } + /// /// Extra sim statistics that are used by monitors but not sent to the client. /// @@ -225,6 +236,22 @@ namespace OpenSim.Region.Framework.Scenes if (StatsManager.SimExtraStats != null) OnSendStatsResult += StatsManager.SimExtraStats.ReceiveClassicSimStatsPacket; + + /// At the moment, we'll only report if a frame is over 120% of target, since commonly frames are a bit + /// longer than ideal (which in itself is a concern). + SlowFramesStatReportThreshold = (int)Math.Ceiling(m_scene.MinFrameTime * 1000 * 1.2); + + SlowFramesStat + = new Stat( + "SlowFrames", + "Slow Frames", + " frames", + "scene", + m_scene.Name, + StatVerbosity.Info, + "Number of frames where frame time has been significantly longer than the desired frame time."); + + StatsManager.RegisterStat(SlowFramesStat); } public void Close() @@ -418,6 +445,7 @@ namespace OpenSim.Region.Framework.Scenes lock (m_lastReportedExtraSimStats) { m_lastReportedExtraSimStats[LastReportedObjectUpdateStatName] = m_objectUpdates / m_statsUpdateFactor; + m_lastReportedExtraSimStats[SlowFramesStat.ShortName] = (float)SlowFramesStat.Value; Dictionary physicsStats = m_scene.PhysicsScene.GetStats(); @@ -535,6 +563,11 @@ namespace OpenSim.Region.Framework.Scenes public void addFrameMS(int ms) { m_frameMS += ms; + + // At the moment, we'll only report if a frame is over 120% of target, since commonly frames are a bit + // longer than ideal due to the inaccuracy of the Sleep in Scene.Update() (which in itself is a concern). + if (ms > SlowFramesStatReportThreshold) + SlowFramesStat.Value++; } public void AddSpareMS(int ms) diff --git a/OpenSim/Region/OptionalModules/Avatar/Attachments/AttachmentsCommandModule.cs b/OpenSim/Region/OptionalModules/Avatar/Attachments/AttachmentsCommandModule.cs index d68aabc5ca..68bcb4abff 100644 --- a/OpenSim/Region/OptionalModules/Avatar/Attachments/AttachmentsCommandModule.cs +++ b/OpenSim/Region/OptionalModules/Avatar/Attachments/AttachmentsCommandModule.cs @@ -146,7 +146,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.Attachments sb.AppendFormat("Attachments for {0}\n", sp.Name); ConsoleDisplayTable ct = new ConsoleDisplayTable() { Indent = 2 }; - ct.Columns.Add(new ConsoleDisplayTableColumn("Attachment Name", 36)); + ct.Columns.Add(new ConsoleDisplayTableColumn("Attachment Name", 50)); ct.Columns.Add(new ConsoleDisplayTableColumn("Local ID", 10)); ct.Columns.Add(new ConsoleDisplayTableColumn("Item ID", 36)); ct.Columns.Add(new ConsoleDisplayTableColumn("Attach Point", 14)); diff --git a/OpenSim/Region/OptionalModules/World/NPC/NPCAvatar.cs b/OpenSim/Region/OptionalModules/World/NPC/NPCAvatar.cs index a8e4d9060d..ffd4222935 100644 --- a/OpenSim/Region/OptionalModules/World/NPC/NPCAvatar.cs +++ b/OpenSim/Region/OptionalModules/World/NPC/NPCAvatar.cs @@ -148,7 +148,7 @@ namespace OpenSim.Region.OptionalModules.World.NPC OnInstantMessage(this, new GridInstantMessage(m_scene, m_uuid, m_firstname + " " + m_lastname, target, 0, false, message, - UUID.Zero, false, Position, new byte[0])); + UUID.Zero, false, Position, new byte[0], true)); } public void SendAgentOffline(UUID[] agentIDs) diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs b/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs index b88ec3cf9d..2e6b2dad90 100644 --- a/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs @@ -218,6 +218,18 @@ public class BSCharacter : BSPhysObject }); } } + public override OMV.Vector3 ForcePosition { + get { + _position = BulletSimAPI.GetPosition2(BSBody.ptr); + return _position; + } + set { + _position = value; + PositionSanityCheck(); + BulletSimAPI.SetTranslation2(BSBody.ptr, _position, _orientation); + } + } + // Check that the current position is sane and, if not, modify the position to make it so. // Check for being below terrain and being out of bounds. @@ -234,6 +246,15 @@ public class BSCharacter : BSPhysObject _position.Z = terrainHeight + 2.0f; ret = true; } + if ((CurrentCollisionFlags & CollisionFlags.BS_FLOATS_ON_WATER) != 0) + { + float waterHeight = PhysicsScene.GetWaterLevelAtXYZ(_position); + if (Position.Z < waterHeight) + { + _position.Z = waterHeight; + ret = true; + } + } // TODO: check for out of bounds return ret; @@ -242,18 +263,22 @@ public class BSCharacter : BSPhysObject // A version of the sanity check that also makes sure a new position value is // pushed back to the physics engine. This routine would be used by anyone // who is not already pushing the value. - private bool PositionSanityCheck2() + private bool PositionSanityCheck2(bool inTaintTime) { bool ret = false; if (PositionSanityCheck()) { // The new position value must be pushed into the physics engine but we can't // just assign to "Position" because of potential call loops. - PhysicsScene.TaintedObject("BSCharacter.PositionSanityCheck", delegate() + BSScene.TaintCallback sanityOperation = delegate() { DetailLog("{0},BSCharacter.PositionSanityCheck,taint,pos={1},orient={2}", LocalID, _position, _orientation); BulletSimAPI.SetObjectTranslation(PhysicsScene.WorldID, LocalID, _position, _orientation); - }); + }; + if (inTaintTime) + sanityOperation(); + else + PhysicsScene.TaintedObject("BSCharacter.PositionSanityCheck", sanityOperation); ret = true; } return ret; @@ -307,6 +332,13 @@ public class BSCharacter : BSPhysObject }); } } + public override OMV.Vector3 ForceVelocity { + get { return _velocity; } + set { + _velocity = value; + BulletSimAPI.SetObjectVelocity(PhysicsScene.WorldID, LocalID, _velocity); + } + } public override OMV.Vector3 Torque { get { return _torque; } set { _torque = value; @@ -333,6 +365,20 @@ public class BSCharacter : BSPhysObject }); } } + // Go directly to Bullet to get/set the value. + public override OMV.Quaternion ForceOrientation + { + get + { + _orientation = BulletSimAPI.GetOrientation2(BSBody.ptr); + return _orientation; + } + set + { + _orientation = value; + BulletSimAPI.SetTranslation2(BSBody.ptr, _position, _orientation); + } + } public override int PhysicsActorType { get { return _physicsActorType; } set { _physicsActorType = value; @@ -378,12 +424,25 @@ public class BSCharacter : BSPhysObject set { _collidingObj = value; } } public override bool FloatOnWater { - set { _floatOnWater = value; } + set { + _floatOnWater = value; + PhysicsScene.TaintedObject("BSCharacter.setFloatOnWater", delegate() + { + if (_floatOnWater) + CurrentCollisionFlags = BulletSimAPI.AddToCollisionFlags2(BSBody.ptr, CollisionFlags.BS_FLOATS_ON_WATER); + else + CurrentCollisionFlags = BulletSimAPI.RemoveFromCollisionFlags2(BSBody.ptr, CollisionFlags.BS_FLOATS_ON_WATER); + }); + } } public override OMV.Vector3 RotationalVelocity { get { return _rotationalVelocity; } set { _rotationalVelocity = value; } } + public override OMV.Vector3 ForceRotationalVelocity { + get { return _rotationalVelocity; } + set { _rotationalVelocity = value; } + } public override bool Kinematic { get { return _kinematic; } set { _kinematic = value; } @@ -493,15 +552,14 @@ public class BSCharacter : BSPhysObject _velocity = entprop.Velocity; _acceleration = entprop.Acceleration; _rotationalVelocity = entprop.RotationalVelocity; + // Do some sanity checking for the avatar. Make sure it's above ground and inbounds. + PositionSanityCheck2(true); + // Avatars don't report their changes the usual way. Changes are checked for in the heartbeat loop. // base.RequestPhysicsterseUpdate(); - // Do some sanity checking for the avatar. Make sure it's above ground and inbounds. - PositionSanityCheck2(); - - float heightHere = PhysicsScene.TerrainManager.GetTerrainHeightAtXYZ(_position); // only for debug - DetailLog("{0},BSCharacter.UpdateProperties,call,pos={1},orient={2},vel={3},accel={4},rotVel={5},terrain={6}", - LocalID, _position, _orientation, _velocity, _acceleration, _rotationalVelocity, heightHere); + DetailLog("{0},BSCharacter.UpdateProperties,call,pos={1},orient={2},vel={3},accel={4},rotVel={5}", + LocalID, _position, _orientation, _velocity, _acceleration, _rotationalVelocity); } } } diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSConstraint.cs b/OpenSim/Region/Physics/BulletSPlugin/BSConstraint.cs index 63a4127075..a20be3a285 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSConstraint.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSConstraint.cs @@ -49,9 +49,16 @@ public abstract class BSConstraint : IDisposable if (m_enabled) { m_enabled = false; - bool success = BulletSimAPI.DestroyConstraint2(m_world.ptr, m_constraint.ptr); - m_world.physicsScene.DetailLog("{0},BSConstraint.Dispose,taint,body1={1},body2={2},success={3}", BSScene.DetailLogZero, m_body1.ID, m_body2.ID, success); - m_constraint.ptr = System.IntPtr.Zero; + if (m_constraint.ptr != IntPtr.Zero) + { + bool success = BulletSimAPI.DestroyConstraint2(m_world.ptr, m_constraint.ptr); + m_world.physicsScene.DetailLog("{0},BSConstraint.Dispose,taint,id1={1},body1={2},id2={3},body2={4},success={5}", + BSScene.DetailLogZero, + m_body1.ID, m_body1.ptr.ToString("X"), + m_body2.ID, m_body2.ptr.ToString("X"), + success); + m_constraint.ptr = System.IntPtr.Zero; + } } } diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs b/OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs index 65b38d696a..56342b88f8 100644 --- a/OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs @@ -92,7 +92,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin private float m_angularMotorDecayTimescale = 0; // motor angular velocity decay rate private Vector3 m_angularFrictionTimescale = Vector3.Zero; // body angular velocity decay rate private Vector3 m_lastAngularVelocity = Vector3.Zero; // what was last applied to body - // private Vector3 m_lastVertAttractor = Vector3.Zero; // what VA was last applied to body + private Vector3 m_lastVertAttractor = Vector3.Zero; // what VA was last applied to body //Deflection properties // private float m_angularDeflectionEfficiency = 0; @@ -138,74 +138,55 @@ namespace OpenSim.Region.Physics.BulletSPlugin switch (pParam) { case Vehicle.ANGULAR_DEFLECTION_EFFICIENCY: - if (pValue < 0.01f) pValue = 0.01f; - // m_angularDeflectionEfficiency = pValue; + // m_angularDeflectionEfficiency = Math.Max(pValue, 0.01f); break; case Vehicle.ANGULAR_DEFLECTION_TIMESCALE: - if (pValue < 0.01f) pValue = 0.01f; - // m_angularDeflectionTimescale = pValue; + // m_angularDeflectionTimescale = Math.Max(pValue, 0.01f); break; case Vehicle.ANGULAR_MOTOR_DECAY_TIMESCALE: - if (pValue < 0.01f) pValue = 0.01f; - m_angularMotorDecayTimescale = pValue; + m_angularMotorDecayTimescale = Math.Max(pValue, 0.01f); break; case Vehicle.ANGULAR_MOTOR_TIMESCALE: - if (pValue < 0.01f) pValue = 0.01f; - m_angularMotorTimescale = pValue; + m_angularMotorTimescale = Math.Max(pValue, 0.01f); break; case Vehicle.BANKING_EFFICIENCY: - if (pValue < 0.01f) pValue = 0.01f; - // m_bankingEfficiency = pValue; + // m_bankingEfficiency = Math.Max(pValue, 0.01f); break; case Vehicle.BANKING_MIX: - if (pValue < 0.01f) pValue = 0.01f; - // m_bankingMix = pValue; + // m_bankingMix = Math.Max(pValue, 0.01f); break; case Vehicle.BANKING_TIMESCALE: - if (pValue < 0.01f) pValue = 0.01f; - // m_bankingTimescale = pValue; + // m_bankingTimescale = Math.Max(pValue, 0.01f); break; case Vehicle.BUOYANCY: - if (pValue < -1f) pValue = -1f; - if (pValue > 1f) pValue = 1f; - m_VehicleBuoyancy = pValue; + m_VehicleBuoyancy = Math.Max(-1f, Math.Min(pValue, 1f)); break; // case Vehicle.HOVER_EFFICIENCY: -// if (pValue < 0f) pValue = 0f; -// if (pValue > 1f) pValue = 1f; -// m_VhoverEfficiency = pValue; +// m_VhoverEfficiency = Math.Max(0f, Math.Min(pValue, 1f)); // break; case Vehicle.HOVER_HEIGHT: m_VhoverHeight = pValue; break; case Vehicle.HOVER_TIMESCALE: - if (pValue < 0.01f) pValue = 0.01f; - m_VhoverTimescale = pValue; + m_VhoverTimescale = Math.Max(pValue, 0.01f); break; case Vehicle.LINEAR_DEFLECTION_EFFICIENCY: - if (pValue < 0.01f) pValue = 0.01f; - // m_linearDeflectionEfficiency = pValue; + // m_linearDeflectionEfficiency = Math.Max(pValue, 0.01f); break; case Vehicle.LINEAR_DEFLECTION_TIMESCALE: - if (pValue < 0.01f) pValue = 0.01f; - // m_linearDeflectionTimescale = pValue; + // m_linearDeflectionTimescale = Math.Max(pValue, 0.01f); break; case Vehicle.LINEAR_MOTOR_DECAY_TIMESCALE: - if (pValue < 0.01f) pValue = 0.01f; - m_linearMotorDecayTimescale = pValue; + m_linearMotorDecayTimescale = Math.Max(pValue, 0.01f); break; case Vehicle.LINEAR_MOTOR_TIMESCALE: - if (pValue < 0.01f) pValue = 0.01f; - m_linearMotorTimescale = pValue; + m_linearMotorTimescale = Math.Max(pValue, 0.01f); break; case Vehicle.VERTICAL_ATTRACTION_EFFICIENCY: - if (pValue < 0.1f) pValue = 0.1f; // Less goes unstable - if (pValue > 1.0f) pValue = 1.0f; - m_verticalAttractionEfficiency = pValue; + m_verticalAttractionEfficiency = Math.Max(0.1f, Math.Min(pValue, 1f)); break; case Vehicle.VERTICAL_ATTRACTION_TIMESCALE: - if (pValue < 0.01f) pValue = 0.01f; - m_verticalAttractionTimescale = pValue; + m_verticalAttractionTimescale = Math.Max(pValue, 0.01f); break; // These are vector properties but the engine lets you use a single float value to @@ -371,8 +352,9 @@ namespace OpenSim.Region.Physics.BulletSPlugin // m_bankingMix = 1; // m_bankingTimescale = 1; // m_referenceFrame = Quaternion.Identity; - m_flags |= (VehicleFlag.NO_DEFLECTION_UP | VehicleFlag.LIMIT_ROLL_ONLY | - VehicleFlag.LIMIT_MOTOR_UP); + m_flags |= (VehicleFlag.NO_DEFLECTION_UP + | VehicleFlag.LIMIT_ROLL_ONLY + | VehicleFlag.LIMIT_MOTOR_UP); m_flags &= ~(VehicleFlag.HOVER_WATER_ONLY | VehicleFlag.HOVER_TERRAIN_ONLY | VehicleFlag.HOVER_GLOBAL_HEIGHT); m_flags |= (VehicleFlag.HOVER_UP_ONLY); break; @@ -399,12 +381,13 @@ namespace OpenSim.Region.Physics.BulletSPlugin // m_bankingMix = 0.8f; // m_bankingTimescale = 1; // m_referenceFrame = Quaternion.Identity; - m_flags &= ~(VehicleFlag.HOVER_TERRAIN_ONLY | - VehicleFlag.HOVER_GLOBAL_HEIGHT | VehicleFlag.HOVER_UP_ONLY); - m_flags &= ~(VehicleFlag.LIMIT_ROLL_ONLY); - m_flags |= (VehicleFlag.NO_DEFLECTION_UP | - VehicleFlag.LIMIT_MOTOR_UP); - m_flags |= (VehicleFlag.HOVER_WATER_ONLY); + m_flags &= ~(VehicleFlag.HOVER_TERRAIN_ONLY + | VehicleFlag.HOVER_GLOBAL_HEIGHT + | VehicleFlag.LIMIT_ROLL_ONLY + | VehicleFlag.HOVER_UP_ONLY); + m_flags |= (VehicleFlag.NO_DEFLECTION_UP + | VehicleFlag.LIMIT_MOTOR_UP + | VehicleFlag.HOVER_WATER_ONLY); break; case Vehicle.TYPE_AIRPLANE: m_linearFrictionTimescale = new Vector3(200, 10, 5); @@ -429,9 +412,12 @@ namespace OpenSim.Region.Physics.BulletSPlugin // m_bankingMix = 0.7f; // m_bankingTimescale = 2; // m_referenceFrame = Quaternion.Identity; - m_flags &= ~(VehicleFlag.HOVER_WATER_ONLY | VehicleFlag.HOVER_TERRAIN_ONLY | - VehicleFlag.HOVER_GLOBAL_HEIGHT | VehicleFlag.HOVER_UP_ONLY); - m_flags &= ~(VehicleFlag.NO_DEFLECTION_UP | VehicleFlag.LIMIT_MOTOR_UP); + m_flags &= ~(VehicleFlag.HOVER_WATER_ONLY + | VehicleFlag.HOVER_TERRAIN_ONLY + | VehicleFlag.HOVER_GLOBAL_HEIGHT + | VehicleFlag.HOVER_UP_ONLY + | VehicleFlag.NO_DEFLECTION_UP + | VehicleFlag.LIMIT_MOTOR_UP); m_flags |= (VehicleFlag.LIMIT_ROLL_ONLY); break; case Vehicle.TYPE_BALLOON: @@ -457,11 +443,13 @@ namespace OpenSim.Region.Physics.BulletSPlugin // m_bankingMix = 0.7f; // m_bankingTimescale = 5; // m_referenceFrame = Quaternion.Identity; - m_flags &= ~(VehicleFlag.HOVER_WATER_ONLY | VehicleFlag.HOVER_TERRAIN_ONLY | - VehicleFlag.HOVER_UP_ONLY); - m_flags &= ~(VehicleFlag.NO_DEFLECTION_UP | VehicleFlag.LIMIT_MOTOR_UP); - m_flags |= (VehicleFlag.LIMIT_ROLL_ONLY); - m_flags |= (VehicleFlag.HOVER_GLOBAL_HEIGHT); + m_flags &= ~(VehicleFlag.HOVER_WATER_ONLY + | VehicleFlag.HOVER_TERRAIN_ONLY + | VehicleFlag.HOVER_UP_ONLY + | VehicleFlag.NO_DEFLECTION_UP + | VehicleFlag.LIMIT_MOTOR_UP); + m_flags |= (VehicleFlag.LIMIT_ROLL_ONLY + | VehicleFlag.HOVER_GLOBAL_HEIGHT); break; } }//end SetDefaultsForType @@ -470,10 +458,11 @@ namespace OpenSim.Region.Physics.BulletSPlugin // Do any updating needed for a vehicle public void Refresh() { - if (Type == Vehicle.TYPE_NONE) return; + if (!IsActive) + return; // Set the prim's inertia to zero. The vehicle code handles that and this - // removes the torque action introduced by Bullet. + // removes the motion and torque actions introduced by Bullet. Vector3 inertia = Vector3.Zero; BulletSimAPI.SetMassProps2(Prim.BSBody.ptr, Prim.MassRaw, inertia); BulletSimAPI.UpdateInertiaTensor2(Prim.BSBody.ptr); @@ -489,10 +478,10 @@ namespace OpenSim.Region.Physics.BulletSPlugin LimitRotation(pTimestep); // remember the position so next step we can limit absolute movement effects - m_lastPositionVector = Prim.Position; + m_lastPositionVector = Prim.ForcePosition; VDetailLog("{0},BSDynamics.Step,done,pos={1},force={2},velocity={3},angvel={4}", - Prim.LocalID, Prim.Position, Prim.Force, Prim.Velocity, Prim.RotationalVelocity); + Prim.LocalID, Prim.ForcePosition, Prim.Force, Prim.ForceVelocity, Prim.RotationalVelocity); }// end Step // Apply the effect of the linear motor. @@ -543,7 +532,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin } // convert requested object velocity to object relative vector - Quaternion rotq = Prim.Orientation; + Quaternion rotq = Prim.ForceOrientation; m_newVelocity = m_lastLinearVelocityVector * rotq; // Add the various forces into m_dir which will be our new direction vector (velocity) @@ -551,7 +540,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin // add Gravity and Buoyancy // There is some gravity, make a gravity force vector that is applied after object velocity. // m_VehicleBuoyancy: -1=2g; 0=1g; 1=0g; - Vector3 grav = Prim.PhysicsScene.DefaultGravity * (Prim.Mass * (1f - m_VehicleBuoyancy)); + Vector3 grav = Prim.PhysicsScene.DefaultGravity * (Prim.Linkset.LinksetMass * (1f - m_VehicleBuoyancy)); /* * RA: Not sure why one would do this @@ -560,19 +549,19 @@ namespace OpenSim.Region.Physics.BulletSPlugin m_dir.Z = vel_now.Z; // Preserve the accumulated falling velocity */ - Vector3 pos = Prim.Position; + Vector3 pos = Prim.ForcePosition; // Vector3 accel = new Vector3(-(m_dir.X - m_lastLinearVelocityVector.X / 0.1f), -(m_dir.Y - m_lastLinearVelocityVector.Y / 0.1f), m_dir.Z - m_lastLinearVelocityVector.Z / 0.1f); // If below the terrain, move us above the ground a little. float terrainHeight = Prim.PhysicsScene.TerrainManager.GetTerrainHeightAtXYZ(pos); // Taking the rotated size doesn't work here because m_prim.Size is the size of the root prim and not the linkset. // Need to add a m_prim.LinkSet.Size similar to m_prim.LinkSet.Mass. - // Vector3 rotatedSize = m_prim.Size * m_prim.Orientation; + // Vector3 rotatedSize = m_prim.Size * m_prim.ForceOrientation; // if (rotatedSize.Z < terrainHeight) if (pos.Z < terrainHeight) { pos.Z = terrainHeight + 2; - Prim.Position = pos; + Prim.ForcePosition = pos; VDetailLog("{0},MoveLinear,terrainHeight,terrainHeight={1},pos={2}", Prim.LocalID, terrainHeight, pos); } @@ -602,7 +591,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin { if ((pos.Z - m_VhoverTargetHeight) > .2 || (pos.Z - m_VhoverTargetHeight) < -.2) { - Prim.Position = pos; + Prim.ForcePosition = pos; } } else @@ -654,12 +643,13 @@ namespace OpenSim.Region.Physics.BulletSPlugin } if (changed) { - Prim.Position = pos; + Prim.ForcePosition = pos; VDetailLog("{0},MoveLinear,blockingEndPoint,block={1},origPos={2},pos={3}", Prim.LocalID, m_BlockingEndPoint, posChange, pos); } } + // Limit absolute vertical change float Zchange = Math.Abs(posChange.Z); if ((m_flags & (VehicleFlag.LIMIT_MOTOR_UP)) != 0) { @@ -678,6 +668,8 @@ namespace OpenSim.Region.Physics.BulletSPlugin grav.Z = (float)(grav.Z * 1.037125); VDetailLog("{0},MoveLinear,limitMotorUp,grav={1}", Prim.LocalID, grav); } + + // If not changing some axis, reduce out velocity if ((m_flags & (VehicleFlag.NO_X)) != 0) m_newVelocity.X = 0; if ((m_flags & (VehicleFlag.NO_Y)) != 0) @@ -686,10 +678,10 @@ namespace OpenSim.Region.Physics.BulletSPlugin m_newVelocity.Z = 0; // Apply velocity - Prim.Velocity = m_newVelocity; + Prim.ForceVelocity = m_newVelocity; // apply gravity force // Why is this set here? The physics engine already does gravity. - // m_prim.AddForce(grav, false); + Prim.AddForce(grav, false, true); // Apply friction Vector3 keepFraction = Vector3.One - (Vector3.One / (m_linearFrictionTimescale / pTimestep)); @@ -712,7 +704,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin // m_lastAngularVelocity // what was last applied to body // Get what the body is doing, this includes 'external' influences - Vector3 angularVelocity = Prim.RotationalVelocity; + Vector3 angularVelocity = Prim.ForceRotationalVelocity; if (m_angularMotorApply > 0) { @@ -720,19 +712,19 @@ namespace OpenSim.Region.Physics.BulletSPlugin // a newly set velocity, this routine steps the value from the previous // value (m_angularMotorVelocity) to the requested value (m_angularMotorDirection). // There are m_angularMotorApply steps. - Vector3 origAngularVelocity = m_angularMotorVelocity; + Vector3 origVel = m_angularMotorVelocity; + Vector3 origDir = m_angularMotorDirection; + // ramp up to new value - // current velocity += error / ( time to get there / step interval) + // new velocity += error / ( time to get there / step interval) // requested speed - last motor speed m_angularMotorVelocity.X += (m_angularMotorDirection.X - m_angularMotorVelocity.X) / (m_angularMotorTimescale / pTimestep); m_angularMotorVelocity.Y += (m_angularMotorDirection.Y - m_angularMotorVelocity.Y) / (m_angularMotorTimescale / pTimestep); m_angularMotorVelocity.Z += (m_angularMotorDirection.Z - m_angularMotorVelocity.Z) / (m_angularMotorTimescale / pTimestep); - VDetailLog("{0},MoveAngular,angularMotorApply,apply={1},angTScale={2},timeStep={3},origvel={4},dir={5},vel={6}", - Prim.LocalID, m_angularMotorApply, m_angularMotorTimescale, pTimestep, origAngularVelocity, m_angularMotorDirection, m_angularMotorVelocity); + VDetailLog("{0},MoveAngular,angularMotorApply,apply={1},angTScale={2},timeStep={3},origvel={4},origDir={5},vel={6}", + Prim.LocalID, m_angularMotorApply, m_angularMotorTimescale, pTimestep, origVel, origDir, m_angularMotorVelocity); - // This is done so that if script request rate is less than phys frame rate the expected - // velocity may still be acheived. m_angularMotorApply--; } else @@ -746,25 +738,32 @@ namespace OpenSim.Region.Physics.BulletSPlugin // Vertical attractor section Vector3 vertattr = Vector3.Zero; - if (m_verticalAttractionTimescale < 300) + Vector3 deflection = Vector3.Zero; + Vector3 banking = Vector3.Zero; + + if (m_verticalAttractionTimescale < 300 && m_lastAngularVelocity != Vector3.Zero) { float VAservo = 0.2f / (m_verticalAttractionTimescale / pTimestep); + VAservo *= (m_verticalAttractionEfficiency * m_verticalAttractionEfficiency); + // get present body rotation - Quaternion rotq = Prim.Orientation; - // make a vector pointing up + Quaternion rotq = Prim.ForceOrientation; + // vector pointing up Vector3 verterr = Vector3.Zero; verterr.Z = 1.0f; + // rotate it to Body Angle verterr = verterr * rotq; - // verterr.X and .Y are the World error ammounts. They are 0 when there is no error (Vehicle Body is 'vertical'), and .Z will be 1. + // verterr.X and .Y are the World error amounts. They are 0 when there is no error (Vehicle Body is 'vertical'), and .Z will be 1. // As the body leans to its side |.X| will increase to 1 and .Z fall to 0. As body inverts |.X| will fall and .Z will go // negative. Similar for tilt and |.Y|. .X and .Y must be modulated to prevent a stable inverted body. + + // Error is 0 (no error) to +/- 2 (max error) if (verterr.Z < 0.0f) { verterr.X = 2.0f - verterr.X; verterr.Y = 2.0f - verterr.Y; } - // Error is 0 (no error) to +/- 2 (max error) // scale it by VAservo verterr = verterr * VAservo; @@ -784,7 +783,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin } // else vertical attractor is off - // m_lastVertAttractor = vertattr; + m_lastVertAttractor = vertattr; // Bank section tba @@ -811,14 +810,14 @@ namespace OpenSim.Region.Physics.BulletSPlugin m_lastAngularVelocity -= m_lastAngularVelocity * decayamount; // Apply to the body - Prim.RotationalVelocity = m_lastAngularVelocity; + Prim.ForceRotationalVelocity = m_lastAngularVelocity; VDetailLog("{0},MoveAngular,done,decay={1},lastAngular={2}", Prim.LocalID, decayamount, m_lastAngularVelocity); } //end MoveAngular internal void LimitRotation(float timestep) { - Quaternion rotq = Prim.Orientation; + Quaternion rotq = Prim.ForceOrientation; Quaternion m_rot = rotq; bool changed = false; if (m_RollreferenceFrame != Quaternion.Identity) @@ -853,7 +852,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin } if (changed) { - Prim.Orientation = m_rot; + Prim.ForceOrientation = m_rot; VDetailLog("{0},LimitRotation,done,orig={1},new={2}", Prim.LocalID, rotq, m_rot); } @@ -863,7 +862,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin private void VDetailLog(string msg, params Object[] args) { if (Prim.PhysicsScene.VehicleLoggingEnabled) - Prim.PhysicsScene.PhysicsLogging.Write(msg, args); + Prim.PhysicsScene.DetailLog(msg, args); } } } diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSLinkset.cs b/OpenSim/Region/Physics/BulletSPlugin/BSLinkset.cs index 4f225ae0f0..43b12623cc 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSLinkset.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSLinkset.cs @@ -34,7 +34,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin { public class BSLinkset { - private static string LogHeader = "[BULLETSIM LINKSET]"; + // private static string LogHeader = "[BULLETSIM LINKSET]"; public BSPhysObject LinksetRoot { get; protected set; } @@ -52,8 +52,8 @@ public class BSLinkset // the physical 'taint' children separately. // After taint processing and before the simulation step, these // two lists must be the same. - private List m_children; - private List m_taintChildren; + private HashSet m_children; + private HashSet m_taintChildren; // We lock the diddling of linkset classes to prevent any badness. // This locks the modification of the instances of this class. Changes @@ -90,8 +90,8 @@ public class BSLinkset m_nextLinksetID = 1; PhysicsScene = scene; LinksetRoot = parent; - m_children = new List(); - m_taintChildren = new List(); + m_children = new HashSet(); + m_taintChildren = new HashSet(); m_mass = parent.MassRaw; } @@ -160,6 +160,28 @@ public class BSLinkset return ret; } + // When physical properties are changed the linkset needs to recalculate + // its internal properties. + // May be called at runtime or taint-time (just pass the appropriate flag). + public void Refresh(BSPhysObject requestor, bool inTaintTime) + { + // If there are no children, not physical or not root, I am not the one that recomputes the constraints + // (For the moment, static linksets do create constraints so remove the test for physical.) + if (!HasAnyChildren || /*!requestor.IsPhysical ||*/ !IsRoot(requestor)) + return; + + BSScene.TaintCallback refreshOperation = delegate() + { + RecomputeLinksetConstraintVariables(); + DetailLog("{0},BSLinkset.Refresh,complete,rBody={1}", + LinksetRoot.LocalID, LinksetRoot.BSBody.ptr.ToString("X")); + }; + if (inTaintTime) + refreshOperation(); + else + PhysicsScene.TaintedObject("BSLinkSet.Refresh", refreshOperation); + } + // The object is going dynamic (physical). Do any setup necessary // for a dynamic linkset. // Only the state of the passed object can be modified. The rest of the linkset @@ -182,22 +204,19 @@ public class BSLinkset return false; } - // When physical properties are changed the linkset needs to recalculate - // its internal properties. - // Called at runtime. - public void Refresh(BSPhysObject requestor) + // If the software is handling the movement of all the objects in a linkset + // (like if one doesn't use constraints for static linksets), this is called + // when an update for the root of the linkset is received. + // Called at taint-time!! + public void UpdateProperties(BSPhysObject physObject) { - // If there are no children, there can't be any constraints to recompute - if (!HasAnyChildren) - return; - - // Only the root does the recomputation - if (IsRoot(requestor)) + // The root local properties have been updated. Apply to the children if appropriate. + if (IsRoot(physObject) && HasAnyChildren) { - PhysicsScene.TaintedObject("BSLinkSet.Refresh", delegate() + if (!physObject.IsPhysical) { - RecomputeLinksetConstraintVariables(); - }); + // TODO: implement software linkset update for static object linksets + } } } @@ -215,13 +234,10 @@ public class BSLinkset if (IsRoot(child)) { // If the one with the dependency is root, must undo all children - DetailLog("{0},BSLinkset.RemoveBodyDependencies,removeChildrenForRoot,rID={1},numChild={2}", - child.LocalID, LinksetRoot.LocalID, m_taintChildren.Count); - foreach (BSPhysObject bpo in m_taintChildren) - { - PhysicallyUnlinkAChildFromRoot(LinksetRoot, LinksetRoot.BSBody, bpo, bpo.BSBody); - ret = true; - } + DetailLog("{0},BSLinkset.RemoveBodyDependencies,removeChildrenForRoot,rID={1},rBody={2}", + child.LocalID, LinksetRoot.LocalID, LinksetRoot.BSBody.ptr.ToString("X")); + + ret = PhysicallyUnlinkAllChildrenFromRoot(LinksetRoot); } else { @@ -229,20 +245,16 @@ public class BSLinkset child.LocalID, LinksetRoot.LocalID, LinksetRoot.BSBody.ptr.ToString("X"), child.LocalID, child.BSBody.ptr.ToString("X")); - // Remove the dependency on the body of this one - if (m_taintChildren.Contains(child)) - { - PhysicallyUnlinkAChildFromRoot(LinksetRoot, LinksetRoot.BSBody, child, child.BSBody); - ret = true; - } + // ret = PhysicallyUnlinkAChildFromRoot(LinksetRoot, child); + // Despite the function name, this removes any link to the specified object. + ret = PhysicallyUnlinkAllChildrenFromRoot(child); } } return ret; } - // Routine used when rebuilding the body of the root of the linkset - // This is called after RemoveAllLinksToRoot() to restore all the constraints. - // This is called when the root body has been changed. + // Companion to RemoveBodyDependencies(). If RemoveBodyDependencies() returns 'true', + // this routine will restore the removed constraints. // Called at taint-time!! public void RestoreBodyDependencies(BSPrim child) { @@ -254,7 +266,7 @@ public class BSLinkset child.LocalID, LinksetRoot.LocalID, m_taintChildren.Count); foreach (BSPhysObject bpo in m_taintChildren) { - PhysicallyLinkAChildToRoot(LinksetRoot, LinksetRoot.BSBody, bpo, bpo.BSBody); + PhysicallyLinkAChildToRoot(LinksetRoot, bpo); } } else @@ -263,7 +275,7 @@ public class BSLinkset LinksetRoot.LocalID, LinksetRoot.LocalID, LinksetRoot.BSBody.ptr.ToString("X"), child.LocalID, child.BSBody.ptr.ToString("X")); - PhysicallyLinkAChildToRoot(LinksetRoot, LinksetRoot.BSBody, child, child.BSBody); + PhysicallyLinkAChildToRoot(LinksetRoot, child); } } } @@ -330,22 +342,22 @@ public class BSLinkset { m_children.Add(child); - BSPhysObject rootx = LinksetRoot; // capture the root and body as of now - BulletBody rootBodyx = LinksetRoot.BSBody; + BSPhysObject rootx = LinksetRoot; // capture the root as of now BSPhysObject childx = child; - BulletBody childBodyx = child.BSBody; - DetailLog("{0},AddChildToLinkset,call,rID={1},rBody={2},cID={3},cBody={4}", - rootx.LocalID, - rootx.LocalID, rootBodyx.ptr.ToString("X"), - childx.LocalID, childBodyx.ptr.ToString("X")); + DetailLog("{0},AddChildToLinkset,call,child={1}", LinksetRoot.LocalID, child.LocalID); PhysicsScene.TaintedObject("AddChildToLinkset", delegate() { - DetailLog("{0},AddChildToLinkset,taint,child={1}", LinksetRoot.LocalID, child.LocalID); - // build the physical binding between me and the child - m_taintChildren.Add(childx); - PhysicallyLinkAChildToRoot(rootx, rootBodyx, childx, childBodyx); + DetailLog("{0},AddChildToLinkset,taint,rID={1},rBody={2},cID={3},cBody={4}", + rootx.LocalID, + rootx.LocalID, rootx.BSBody.ptr.ToString("X"), + childx.LocalID, childx.BSBody.ptr.ToString("X")); + // Since this is taint-time, the body and shape could have changed for the child + rootx.ForcePosition = rootx.Position; // DEBUG + childx.ForcePosition = childx.Position; // DEBUG + PhysicallyLinkAChildToRoot(rootx, childx); + m_taintChildren.Add(child); }); } return; @@ -369,21 +381,17 @@ public class BSLinkset if (m_children.Remove(child)) { BSPhysObject rootx = LinksetRoot; // capture the root and body as of now - BulletBody rootBodyx = LinksetRoot.BSBody; BSPhysObject childx = child; - BulletBody childBodyx = child.BSBody; DetailLog("{0},RemoveChildFromLinkset,call,rID={1},rBody={2},cID={3},cBody={4}", childx.LocalID, - rootx.LocalID, rootBodyx.ptr.ToString("X"), - childx.LocalID, childBodyx.ptr.ToString("X")); + rootx.LocalID, rootx.BSBody.ptr.ToString("X"), + childx.LocalID, childx.BSBody.ptr.ToString("X")); PhysicsScene.TaintedObject("RemoveChildFromLinkset", delegate() { - if (m_taintChildren.Contains(childx)) - m_taintChildren.Remove(childx); - - PhysicallyUnlinkAChildFromRoot(rootx, rootBodyx, childx, childBodyx); + m_taintChildren.Remove(child); + PhysicallyUnlinkAChildFromRoot(rootx, childx); RecomputeLinksetConstraintVariables(); }); @@ -398,8 +406,7 @@ public class BSLinkset // Create a constraint between me (root of linkset) and the passed prim (the child). // Called at taint time! - private void PhysicallyLinkAChildToRoot(BSPhysObject rootPrim, BulletBody rootBody, - BSPhysObject childPrim, BulletBody childBody) + private void PhysicallyLinkAChildToRoot(BSPhysObject rootPrim, BSPhysObject childPrim) { // Zero motion for children so they don't interpolate childPrim.ZeroMotion(); @@ -411,33 +418,17 @@ public class BSLinkset // real world coordinate of midpoint between the two objects OMV.Vector3 midPoint = rootPrim.Position + (childRelativePosition / 2); - DetailLog("{0},PhysicallyLinkAChildToRoot,taint,root={1},rBody={2},child={3},cBody={4},rLoc={5},cLoc={6},midLoc={7}", + DetailLog("{0},BSLinkset.PhysicallyLinkAChildToRoot,taint,root={1},rBody={2},child={3},cBody={4},rLoc={5},cLoc={6},midLoc={7}", rootPrim.LocalID, - rootPrim.LocalID, rootBody.ptr.ToString("X"), - childPrim.LocalID, childBody.ptr.ToString("X"), + rootPrim.LocalID, rootPrim.BSBody.ptr.ToString("X"), + childPrim.LocalID, childPrim.BSBody.ptr.ToString("X"), rootPrim.Position, childPrim.Position, midPoint); // create a constraint that allows no freedom of movement between the two objects // http://bulletphysics.org/Bullet/phpBB3/viewtopic.php?t=4818 - // There is great subtlty in these paramters. Notice the check for a ptr of zero. - // We pass the BulletBody structure into the taint in order to capture the pointer - // of the body at the time of constraint creation. This doesn't work for the very first - // construction because there is no body yet. The body - // is constructed later at taint time. Thus we use the body address at time of the - // taint creation but, if it is zero, use what's in the prim at the moment. - // There is a possible race condition since shape can change without a taint call - // (like changing to a mesh that is already constructed). The fix for that would be - // to only change BSShape at taint time thus syncronizing these operations at - // the cost of efficiency and lag. BS6DofConstraint constrain = new BS6DofConstraint( - PhysicsScene.World, - rootBody.ptr == IntPtr.Zero ? rootPrim.BSBody : rootBody, - childBody.ptr == IntPtr.Zero ? childPrim.BSBody : childBody, - midPoint, - true, - true - ); + PhysicsScene.World, rootPrim.BSBody, childPrim.BSBody, midPoint, true, true ); /* NOTE: below is an attempt to build constraint with full frame computation, etc. * Using the midpoint is easier since it lets the Bullet code manipulate the transforms @@ -454,7 +445,7 @@ public class BSLinkset // create a constraint that allows no freedom of movement between the two objects // http://bulletphysics.org/Bullet/phpBB3/viewtopic.php?t=4818 - DetailLog("{0},PhysicallyLinkAChildToRoot,taint,root={1},child={2}", rootPrim.LocalID, rootPrim.LocalID, childPrim.LocalID); + DetailLog("{0},BSLinkset.PhysicallyLinkAChildToRoot,taint,root={1},child={2}", rootPrim.LocalID, rootPrim.LocalID, childPrim.LocalID); BS6DofConstraint constrain = new BS6DofConstraint( PhysicsScene.World, rootPrim.Body, childPrim.Body, OMV.Vector3.Zero, @@ -488,39 +479,44 @@ public class BSLinkset { constrain.SetSolverIterations(PhysicsScene.Params.linkConstraintSolverIterations); } - - RecomputeLinksetConstraintVariables(); } // Remove linkage between myself and a particular child // The root and child bodies are passed in because we need to remove the constraint between // the bodies that were at unlink time. // Called at taint time! - private void PhysicallyUnlinkAChildFromRoot(BSPhysObject rootPrim, BulletBody rootBody, - BSPhysObject childPrim, BulletBody childBody) + private bool PhysicallyUnlinkAChildFromRoot(BSPhysObject rootPrim, BSPhysObject childPrim) { - DetailLog("{0},PhysicallyUnlinkAChildFromRoot,taint,root={1},rBody={2},child={3},cBody={4}", + bool ret = false; + DetailLog("{0},BSLinkset.PhysicallyUnlinkAChildFromRoot,taint,root={1},rBody={2},child={3},cBody={4}", rootPrim.LocalID, - rootPrim.LocalID, rootBody.ptr.ToString("X"), - childPrim.LocalID, childBody.ptr.ToString("X")); + rootPrim.LocalID, rootPrim.BSBody.ptr.ToString("X"), + childPrim.LocalID, childPrim.BSBody.ptr.ToString("X")); // Find the constraint for this link and get rid of it from the overall collection and from my list - PhysicsScene.Constraints.RemoveAndDestroyConstraint(rootBody, childBody); + if (PhysicsScene.Constraints.RemoveAndDestroyConstraint(rootPrim.BSBody, childPrim.BSBody)) + { + // Make the child refresh its location + BulletSimAPI.PushUpdate2(childPrim.BSBody.ptr); + ret = true; + } - // Make the child refresh its location - BulletSimAPI.PushUpdate2(childPrim.BSBody.ptr); + return ret; } - /* // Remove linkage between myself and any possible children I might have. // Called at taint time! - private void PhysicallyUnlinkAllChildrenFromRoot(BSPhysObject rootPrim) + private bool PhysicallyUnlinkAllChildrenFromRoot(BSPhysObject rootPrim) { - DetailLog("{0},PhysicallyUnlinkAllChildren,taint", rootPrim.LocalID); + DetailLog("{0},BSLinkset.PhysicallyUnlinkAllChildren,taint", rootPrim.LocalID); + bool ret = false; - PhysicsScene.Constraints.RemoveAndDestroyConstraint(rootPrim.BSBody); + if (PhysicsScene.Constraints.RemoveAndDestroyConstraint(rootPrim.BSBody)) + { + ret = true; + } + return ret; } - */ // Call each of the constraints that make up this linkset and recompute the // various transforms and variables. Used when objects are added or removed @@ -552,11 +548,17 @@ public class BSLinkset { // If this is a multiple object linkset, set everybody's center of mass to the set's center of mass OMV.Vector3 centerOfMass = ComputeLinksetCenterOfMass(); - BulletSimAPI.SetCenterOfMassByPosRot2(LinksetRoot.BSBody.ptr, centerOfMass, OMV.Quaternion.Identity); + BulletSimAPI.SetCenterOfMassByPosRot2(LinksetRoot.BSBody.ptr, + centerOfMass, OMV.Quaternion.Identity); + DetailLog("{0},BSLinkset.RecomputeLinksetConstraintVariables,setCenterOfMass,COM={1},rBody={2}", + LinksetRoot.LocalID, centerOfMass, LinksetRoot.BSBody.ptr.ToString("X")); foreach (BSPhysObject child in m_taintChildren) { - BulletSimAPI.SetCenterOfMassByPosRot2(child.BSBody.ptr, centerOfMass, OMV.Quaternion.Identity); + BulletSimAPI.SetCenterOfMassByPosRot2(child.BSBody.ptr, + centerOfMass, OMV.Quaternion.Identity); } + + // BulletSimAPI.DumpAllInfo2(PhysicsScene.World.ptr); // DEBUG DEBUG DEBUG } return; } @@ -565,7 +567,8 @@ public class BSLinkset // Invoke the detailed logger and output something if it's enabled. private void DetailLog(string msg, params Object[] args) { - PhysicsScene.PhysicsLogging.Write(msg, args); + if (PhysicsScene.PhysicsLogging.Enabled) + PhysicsScene.DetailLog(msg, args); } } diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSPhysObject.cs b/OpenSim/Region/Physics/BulletSPlugin/BSPhysObject.cs index d9b738b865..cae599c96f 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSPhysObject.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSPhysObject.cs @@ -81,6 +81,14 @@ public abstract class BSPhysObject : PhysicsActor // Tell the object to clean up. public abstract void Destroy(); + public abstract OMV.Vector3 ForcePosition { get; set; } + + public abstract OMV.Quaternion ForceOrientation { get; set; } + + public abstract OMV.Vector3 ForceVelocity { get; set; } + + public abstract OMV.Vector3 ForceRotationalVelocity { get; set; } + #region Collisions // Requested number of milliseconds between collision events. Zero means disabled. @@ -203,7 +211,8 @@ public abstract class BSPhysObject : PhysicsActor // High performance detailed logging routine used by the physical objects. protected void DetailLog(string msg, params Object[] args) { - PhysicsScene.PhysicsLogging.Write(msg, args); + if (PhysicsScene.PhysicsLogging.Enabled) + PhysicsScene.DetailLog(msg, args); } } } diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs b/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs index a0e627ec66..6a4365c84a 100644 --- a/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs @@ -46,19 +46,13 @@ public sealed class BSPrim : BSPhysObject private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); private static readonly string LogHeader = "[BULLETS PRIM]"; - private IMesh _mesh; private PrimitiveBaseShape _pbs; - private ShapeData.PhysicsShapeType _shapeType; - private ulong _meshKey; - private ulong _hullKey; - private List _hulls; // _size is what the user passed. _scale is what we pass to the physics engine with the mesh. // Often _scale is unity because the meshmerizer will apply _size when creating the mesh. private OMV.Vector3 _size; // the multiplier for each mesh dimension as passed by the user private OMV.Vector3 _scale; // the multiplier for each mesh dimension for the mesh as created by the meshmerizer - private bool _stopped; private bool _grabbed; private bool _isSelected; private bool _isVolumeDetect; @@ -109,8 +103,6 @@ public sealed class BSPrim : BSPhysObject _buoyancy = 1f; _velocity = OMV.Vector3.Zero; _rotationalVelocity = OMV.Vector3.Zero; - _hullKey = 0; - _meshKey = 0; _pbs = pbs; _isPhysical = pisPhysical; _isVolumeDetect = false; @@ -160,8 +152,9 @@ public sealed class BSPrim : BSPhysObject }); } + // No one uses this property. public override bool Stopped { - get { return _stopped; } + get { return false; } } public override OMV.Vector3 Size { get { return _size; } @@ -203,7 +196,7 @@ public sealed class BSPrim : BSPhysObject _isSelected = value; PhysicsScene.TaintedObject("BSPrim.setSelected", delegate() { - // DetailLog("{0},BSPrim.selected,taint,selected={1}", LocalID, _isSelected); + DetailLog("{0},BSPrim.selected,taint,selected={1}", LocalID, _isSelected); SetObjectDynamic(false); }); } @@ -272,8 +265,14 @@ public sealed class BSPrim : BSPhysObject return _position; } set { + // If you must push the position into the physics engine, use ForcePosition. + if (_position == value) + { + return; + } _position = value; // TODO: what does it mean to set the position of a child prim?? Rebuild the constraint? + PositionSanityCheck(); PhysicsScene.TaintedObject("BSPrim.setPosition", delegate() { // DetailLog("{0},BSPrim.SetPosition,taint,pos={1},orient={2}", LocalID, _position, _orientation); @@ -281,6 +280,74 @@ public sealed class BSPrim : BSPhysObject }); } } + public override OMV.Vector3 ForcePosition { + get { + _position = BulletSimAPI.GetPosition2(BSBody.ptr); + return _position; + } + set { + _position = value; + PositionSanityCheck(); + BulletSimAPI.SetTranslation2(BSBody.ptr, _position, _orientation); + } + } + + // Check that the current position is sane and, if not, modify the position to make it so. + // Check for being below terrain and being out of bounds. + // Returns 'true' of the position was made sane by some action. + private bool PositionSanityCheck() + { + bool ret = false; + + // If totally below the ground, move the prim up + // TODO: figure out the right solution for this... only for dynamic objects? + /* + float terrainHeight = PhysicsScene.TerrainManager.GetTerrainHeightAtXYZ(_position); + if (Position.Z < terrainHeight) + { + DetailLog("{0},BSPrim.PositionAdjustUnderGround,call,pos={1},terrain={2}", LocalID, _position, terrainHeight); + _position.Z = terrainHeight + 2.0f; + ret = true; + } + */ + if ((CurrentCollisionFlags & CollisionFlags.BS_FLOATS_ON_WATER) != 0) + { + float waterHeight = PhysicsScene.GetWaterLevelAtXYZ(_position); + if (Position.Z < waterHeight) + { + _position.Z = waterHeight; + ret = true; + } + } + + // TODO: check for out of bounds + return ret; + } + + // A version of the sanity check that also makes sure a new position value is + // pushed back to the physics engine. This routine would be used by anyone + // who is not already pushing the value. + private bool PositionSanityCheck2(bool inTaintTime) + { + bool ret = false; + if (PositionSanityCheck()) + { + // The new position value must be pushed into the physics engine but we can't + // just assign to "Position" because of potential call loops. + BSScene.TaintCallback sanityOperation = delegate() + { + DetailLog("{0},BSPrim.PositionSanityCheck,taint,pos={1},orient={2}", LocalID, _position, _orientation); + BulletSimAPI.SetObjectTranslation(PhysicsScene.WorldID, LocalID, _position, _orientation); + }; + if (inTaintTime) + sanityOperation(); + else + PhysicsScene.TaintedObject("BSPrim.PositionSanityCheck", sanityOperation); + + ret = true; + } + return ret; + } // Return the effective mass of the object. // If there are multiple items in the linkset, add them together for the root @@ -326,14 +393,15 @@ public sealed class BSPrim : BSPhysObject } set { Vehicle type = (Vehicle)value; - BSPrim vehiclePrim = this; + + // Tell the scene about the vehicle so it will get processing each frame. + PhysicsScene.VehicleInSceneTypeChanged(this, type); + PhysicsScene.TaintedObject("setVehicleType", delegate() { // Done at taint time so we're sure the physics engine is not using the variables // Vehicle code changes the parameters for this vehicle type. - _vehicle.ProcessTypeChange(type); - // Tell the scene about the vehicle so it will get processing each frame. - PhysicsScene.VehicleInSceneTypeChanged(this, type); + this._vehicle.ProcessTypeChange(type); }); } } @@ -371,7 +439,9 @@ public sealed class BSPrim : BSPhysObject public override void StepVehicle(float timeStep) { if (IsPhysical) + { _vehicle.Step(timeStep); + } } // Allows the detection of collisions with inherently non-physical prims. see llVolumeDetect for more @@ -388,7 +458,6 @@ public sealed class BSPrim : BSPhysObject } return; } - public override OMV.Vector3 Velocity { get { return _velocity; } set { @@ -400,6 +469,13 @@ public sealed class BSPrim : BSPhysObject }); } } + public override OMV.Vector3 ForceVelocity { + get { return _velocity; } + set { + _velocity = value; + BulletSimAPI.SetLinearVelocity2(BSBody.ptr, _velocity); + } + } public override OMV.Vector3 Torque { get { return _torque; } set { _torque = value; @@ -425,6 +501,8 @@ public sealed class BSPrim : BSPhysObject return _orientation; } set { + if (_orientation == value) + return; _orientation = value; // TODO: what does it mean if a child in a linkset changes its orientation? Rebuild the constraint? PhysicsScene.TaintedObject("BSPrim.setOrientation", delegate() @@ -435,6 +513,20 @@ public sealed class BSPrim : BSPhysObject }); } } + // Go directly to Bullet to get/set the value. + public override OMV.Quaternion ForceOrientation + { + get + { + _orientation = BulletSimAPI.GetOrientation2(BSBody.ptr); + return _orientation; + } + set + { + _orientation = value; + BulletSimAPI.SetTranslation2(BSBody.ptr, _position, _orientation); + } + } public override int PhysicsActorType { get { return _physicsActorType; } set { _physicsActorType = value; } @@ -488,11 +580,10 @@ public sealed class BSPrim : BSPhysObject // This is a NOOP if the object is not in the world (BulletSim and Bullet ignore objects not found). BulletSimAPI.RemoveObjectFromWorld2(PhysicsScene.World.ptr, BSBody.ptr); - // Set up the object physicalness (does gravity and collisions move this object) MakeDynamic(IsStatic); - // Do any vehicle stuff + // Update vehicle specific parameters (after MakeDynamic() so can change physical parameters) _vehicle.Refresh(); // Arrange for collision events if the simulator wants them @@ -515,7 +606,7 @@ public sealed class BSPrim : BSPhysObject // Recompute any linkset parameters. // When going from non-physical to physical, this re-enables the constraints that // had been automatically disabled when the mass was set to zero. - Linkset.Refresh(this); + Linkset.Refresh(this, true); DetailLog("{0},BSPrim.UpdatePhysicalParameters,exit,static={1},solid={2},mass={3},collide={4},cf={5:X},body={6},shape={7}", LocalID, IsStatic, IsSolid, _mass, SubscribedEvents(), CurrentCollisionFlags, BSBody, BSShape); @@ -542,8 +633,10 @@ public sealed class BSPrim : BSPhysObject BulletSimAPI.UpdateInertiaTensor2(BSBody.ptr); // There can be special things needed for implementing linksets Linkset.MakeStatic(this); - // The activation state is 'disabled' so Bullet will not try to act on it + // The activation state is 'disabled' so Bullet will not try to act on it. BulletSimAPI.ForceActivationState2(BSBody.ptr, ActivationState.DISABLE_SIMULATION); + // Start it out sleeping and physical actions could wake it up. + // BulletSimAPI.ForceActivationState2(BSBody.ptr, ActivationState.ISLAND_SLEEPING); BSBody.collisionFilter = CollisionFilterGroups.StaticObjectFilter; BSBody.collisionMask = CollisionFilterGroups.StaticObjectMask; @@ -560,10 +653,12 @@ public sealed class BSPrim : BSPhysObject // per http://www.bulletphysics.org/Bullet/phpBB3/viewtopic.php?t=3382 BulletSimAPI.ClearAllForces2(BSBody.ptr); + // For good measure, make sure the transform is set through to the motion state + BulletSimAPI.SetTranslation2(BSBody.ptr, _position, _orientation); + // A dynamic object has mass IntPtr collisionShapePtr = BulletSimAPI.GetCollisionShape2(BSBody.ptr); OMV.Vector3 inertia = BulletSimAPI.CalculateLocalInertia2(collisionShapePtr, Mass); - // OMV.Vector3 inertia = OMV.Vector3.Zero; BulletSimAPI.SetMassProps2(BSBody.ptr, _mass, inertia); BulletSimAPI.UpdateInertiaTensor2(BSBody.ptr); @@ -573,13 +668,13 @@ public sealed class BSPrim : BSPhysObject BulletSimAPI.SetSleepingThresholds2(BSBody.ptr, PhysicsScene.Params.linearSleepingThreshold, PhysicsScene.Params.angularSleepingThreshold); BulletSimAPI.SetContactProcessingThreshold2(BSBody.ptr, PhysicsScene.Params.contactProcessingThreshold); - // There can be special things needed for implementing linksets. + // There might be special things needed for implementing linksets. Linkset.MakeDynamic(this); // Force activation of the object so Bullet will act on it. // Must do the ForceActivationState2() to overcome the DISABLE_SIMULATION from static objects. - BulletSimAPI.ForceActivationState2(BSBody.ptr, ActivationState.ISLAND_SLEEPING); - BulletSimAPI.Activate2(BSBody.ptr, true); + BulletSimAPI.ForceActivationState2(BSBody.ptr, ActivationState.ACTIVE_TAG); + // BulletSimAPI.Activate2(BSBody.ptr, true); BSBody.collisionFilter = CollisionFilterGroups.ObjectFilter; BSBody.collisionMask = CollisionFilterGroups.ObjectMask; @@ -663,7 +758,16 @@ public sealed class BSPrim : BSPhysObject } } public override bool FloatOnWater { - set { _floatOnWater = value; } + set { + _floatOnWater = value; + PhysicsScene.TaintedObject("BSPrim.setFloatOnWater", delegate() + { + if (_floatOnWater) + CurrentCollisionFlags = BulletSimAPI.AddToCollisionFlags2(BSBody.ptr, CollisionFlags.BS_FLOATS_ON_WATER); + else + CurrentCollisionFlags = BulletSimAPI.RemoveFromCollisionFlags2(BSBody.ptr, CollisionFlags.BS_FLOATS_ON_WATER); + }); + } } public override OMV.Vector3 RotationalVelocity { get { @@ -688,6 +792,15 @@ public sealed class BSPrim : BSPhysObject }); } } + public override OMV.Vector3 ForceRotationalVelocity { + get { + return _rotationalVelocity; + } + set { + _rotationalVelocity = value; + BulletSimAPI.SetAngularVelocity2(BSBody.ptr, _rotationalVelocity); + } + } public override bool Kinematic { get { return _kinematic; } set { _kinematic = value; @@ -742,6 +855,9 @@ public sealed class BSPrim : BSPhysObject private List m_accumulatedForces = new List(); public override void AddForce(OMV.Vector3 force, bool pushforce) { + AddForce(force, pushforce, false); + } + public void AddForce(OMV.Vector3 force, bool pushforce, bool inTaintTime) { // for an object, doesn't matter if force is a pushforce or not if (force.IsFinite()) { @@ -754,11 +870,12 @@ public sealed class BSPrim : BSPhysObject m_log.WarnFormat("{0}: Got a NaN force applied to a prim. LocalID={1}", LogHeader, LocalID); return; } - PhysicsScene.TaintedObject("BSPrim.AddForce", delegate() + BSScene.TaintCallback addForceOperation = delegate() { OMV.Vector3 fSum = OMV.Vector3.Zero; lock (m_accumulatedForces) { + // Sum the accumulated additional forces for one big force to apply once. foreach (OMV.Vector3 v in m_accumulatedForces) { fSum += v; @@ -768,7 +885,11 @@ public sealed class BSPrim : BSPhysObject // DetailLog("{0},BSPrim.AddObjectForce,taint,force={1}", LocalID, fSum); // For unknown reasons, "ApplyCentralForce" adds this force to the total force on the object. BulletSimAPI.ApplyCentralForce2(BSBody.ptr, fSum); - }); + }; + if (inTaintTime) + addForceOperation(); + else + PhysicsScene.TaintedObject("BSPrim.AddForce", addForceOperation); } public override void AddAngularForce(OMV.Vector3 force, bool pushforce) { @@ -1082,15 +1203,15 @@ public sealed class BSPrim : BSPhysObject public void FillShapeInfo(out ShapeData shape) { shape.ID = LocalID; - shape.Type = _shapeType; + shape.Type = ShapeData.PhysicsShapeType.SHAPE_UNKNOWN; shape.Position = _position; shape.Rotation = _orientation; shape.Velocity = _velocity; shape.Scale = _scale; shape.Mass = _isPhysical ? _mass : 0f; shape.Buoyancy = _buoyancy; - shape.HullKey = _hullKey; - shape.MeshKey = _meshKey; + shape.HullKey = 0; + shape.MeshKey = 0; shape.Friction = _friction; shape.Restitution = _restitution; shape.Collidable = (!IsPhantom) ? ShapeData.numericTrue : ShapeData.numericFalse; @@ -1112,11 +1233,13 @@ public sealed class BSPrim : BSPhysObject // Create the correct physical representation for this type of object. // Updates BSBody and BSShape with the new information. - PhysicsScene.Shapes.GetBodyAndShape(forceRebuild, PhysicsScene.World, this, shapeData, _pbs, + // Ignore 'forceRebuild'. This routine makes the right choices and changes of necessary. + PhysicsScene.Shapes.GetBodyAndShape(false, PhysicsScene.World, this, shapeData, _pbs, null, delegate(BulletBody dBody) { // Called if the current prim body is about to be destroyed. // Remove all the physical dependencies on the old body. + // (Maybe someday make the changing of BSShape an event handled by BSLinkset.) needToRestoreLinkset = Linkset.RemoveBodyDependencies(this); }); @@ -1205,6 +1328,10 @@ public sealed class BSPrim : BSPhysObject _acceleration = entprop.Acceleration; _rotationalVelocity = entprop.RotationalVelocity; + PositionSanityCheck2(true); + + Linkset.UpdateProperties(this); + DetailLog("{0},BSPrim.UpdateProperties,call,pos={1},orient={2},vel={3},accel={4},rotVel={5}", LocalID, _position, _orientation, _velocity, _acceleration, _rotationalVelocity); @@ -1215,7 +1342,7 @@ public sealed class BSPrim : BSPhysObject /* else { - // For debugging, we can also report the movement of children + // For debugging, report the movement of children DetailLog("{0},BSPrim.UpdateProperties,child,pos={1},orient={2},vel={3},accel={4},rotVel={5}", LocalID, entprop.Position, entprop.Rotation, entprop.Velocity, entprop.Acceleration, entprop.RotationalVelocity); diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs b/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs index 0cf8c91a2d..2c3c481aea 100644 --- a/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs @@ -39,7 +39,6 @@ using log4net; using OpenMetaverse; // TODOs for BulletSim (for BSScene, BSPrim, BSCharacter and BulletSim) -// Move all logic out of the C++ code and into the C# code for easier future modifications. // Test sculpties (verified that they don't work) // Compute physics FPS reasonably // Based on material, set density and friction @@ -90,10 +89,6 @@ public class BSScene : PhysicsScene, IPhysicsParameters // let my minuions use my logger public ILog Logger { get { return m_log; } } - // If non-zero, the number of simulation steps between calls to the physics - // engine to output detailed physics stats. Debug logging level must be on also. - private int m_detailedStatsStep = 0; - public IMesher mesher; // Level of Detail values kept as float because that's what the Meshmerizer wants public float MeshLOD { get; private set; } @@ -112,6 +107,7 @@ public class BSScene : PhysicsScene, IPhysicsParameters private float m_fixedTimeStep; private long m_simulationStep = 0; public long SimulationStep { get { return m_simulationStep; } } + private int m_taintsToProcessPerStep; // A value of the time now so all the collision and update routines do not have to get their own // Set to 'now' just before all the prims and actors are called for collisions and updates @@ -131,6 +127,7 @@ public class BSScene : PhysicsScene, IPhysicsParameters public bool ShouldMeshSculptedPrim { get; private set; } // cause scuplted prims to get meshed public bool ShouldForceSimplePrimMeshing { get; private set; } // if a cube or sphere, let Bullet do internal shapes + public bool ShouldUseHullsForPhysicalObjects { get; private set; } // 'true' if should create hulls for physical objects public float PID_D { get; private set; } // derivative public float PID_P { get; private set; } // proportional @@ -254,7 +251,7 @@ public class BSScene : PhysicsScene, IPhysicsParameters // The bounding box for the simulated world. The origin is 0,0,0 unless we're // a child in a mega-region. - // Turns out that Bullet really doesn't care about the extents of the simulated + // Bullet actually doesn't care about the extents of the simulated // area. It tracks active objects no matter where they are. Vector3 worldExtent = new Vector3(Constants.RegionSize, Constants.RegionSize, Constants.RegionHeight); @@ -331,7 +328,7 @@ public class BSScene : PhysicsScene, IPhysicsParameters // Called directly from unmanaged code so don't do much private void BulletLoggerPhysLog(string msg) { - PhysicsLogging.Write("[BULLETS UNMANAGED]:" + msg); + DetailLog("[BULLETS UNMANAGED]:" + msg); } public override void Dispose() @@ -493,6 +490,10 @@ public class BSScene : PhysicsScene, IPhysicsParameters // step the physical world one interval m_simulationStep++; int numSubSteps = 0; + + // DEBUG + // DetailLog("{0},BSScene.Simulate,beforeStep,ntaimts={1},step={2}", DetailLogZero, numTaints, m_simulationStep); + try { if (PhysicsLogging.Enabled) beforeTime = Util.EnvironmentTickCount(); @@ -501,8 +502,8 @@ public class BSScene : PhysicsScene, IPhysicsParameters out updatedEntityCount, out updatedEntitiesPtr, out collidersCount, out collidersPtr); if (PhysicsLogging.Enabled) simTime = Util.EnvironmentTickCountSubtract(beforeTime); - DetailLog("{0},Simulate,call, nTaints={1}, simTime={2}, substeps={3}, updates={4}, colliders={5}", - DetailLogZero, numTaints, simTime, numSubSteps, updatedEntityCount, collidersCount); + DetailLog("{0},Simulate,call, frame={1}, nTaints={2}, simTime={3}, substeps={4}, updates={5}, colliders={6}", + DetailLogZero, m_simulationStep, numTaints, simTime, numSubSteps, updatedEntityCount, collidersCount); } catch (Exception e) { @@ -536,7 +537,7 @@ public class BSScene : PhysicsScene, IPhysicsParameters } // This is a kludge to get avatar movement updates. - // ODE sends collisions for avatars even if there are have been no collisions. This updates + // the simulator expects collisions for avatars even if there are have been no collisions. This updates // avatar animations and stuff. // If you fix avatar animation updates, remove this overhead and let normal collision processing happen. foreach (BSPhysObject bsp in m_avatars) @@ -556,7 +557,7 @@ public class BSScene : PhysicsScene, IPhysicsParameters } // Objects that are done colliding are removed from the ObjectsWithCollisions list. - // This can't be done by SendCollisions because it is inside an iteration of ObjectWithCollisions. + // Not done above because it is inside an iteration of ObjectWithCollisions. if (ObjectsWithNoMoreCollisions.Count > 0) { foreach (BSPhysObject po in ObjectsWithNoMoreCollisions) @@ -578,19 +579,11 @@ public class BSScene : PhysicsScene, IPhysicsParameters } } - // If enabled, call into the physics engine to dump statistics - if (m_detailedStatsStep > 0) - { - if ((m_simulationStep % m_detailedStatsStep) == 0) - { - BulletSimAPI.DumpBulletStatistics(); - } - } - // The physics engine returns the number of milliseconds it simulated this call. // These are summed and normalized to one second and divided by 1000 to give the reported physics FPS. - // Since Bullet normally does 5 or 6 substeps, this will normally sum to about 60 FPS. - return numSubSteps * m_fixedTimeStep * 1000; + // We multiply by 45 to give a recognizable running rate (45 or less). + return numSubSteps * m_fixedTimeStep * 1000 * 45; + // return timeStep * 1000 * 45; } // Something has collided @@ -613,7 +606,7 @@ public class BSScene : PhysicsScene, IPhysicsParameters BSPhysObject collidee = null; PhysObjects.TryGetValue(collidingWith, out collidee); - DetailLog("{0},BSScene.SendCollision,collide,id={1},with={2}", DetailLogZero, localID, collidingWith); + // DetailLog("{0},BSScene.SendCollision,collide,id={1},with={2}", DetailLogZero, localID, collidingWith); if (collider.Collide(collidingWith, collidee, collidePoint, collideNormal, penetration)) { @@ -699,6 +692,35 @@ public class BSScene : PhysicsScene, IPhysicsParameters { if (_taintedObjects.Count > 0) // save allocating new list if there is nothing to process { + // swizzle a new list into the list location so we can process what's there + int taintCount = m_taintsToProcessPerStep; + TaintCallbackEntry oneCallback = new TaintCallbackEntry(); + while (_taintedObjects.Count > 0 && taintCount-- > 0) + { + bool gotOne = false; + lock (_taintLock) + { + if (_taintedObjects.Count > 0) + { + oneCallback = _taintedObjects[0]; + _taintedObjects.RemoveAt(0); + gotOne = true; + } + } + if (gotOne) + { + try + { + DetailLog("{0},BSScene.ProcessTaints,doTaint,id={1}", DetailLogZero, oneCallback.ident); // DEBUG DEBUG DEBUG + oneCallback.callback(); + } + catch (Exception e) + { + m_log.ErrorFormat("{0}: ProcessTaints: {1}: Exception: {2}", LogHeader, oneCallback.ident, e); + } + } + } + /* // swizzle a new list into the list location so we can process what's there List oldList; lock (_taintLock) @@ -711,6 +733,7 @@ public class BSScene : PhysicsScene, IPhysicsParameters { try { + DetailLog("{0},BSScene.ProcessTaints,doTaint,id={1}", DetailLogZero, tcbe.ident); // DEBUG DEBUG DEBUG tcbe.callback(); } catch (Exception e) @@ -719,6 +742,7 @@ public class BSScene : PhysicsScene, IPhysicsParameters } } oldList.Clear(); + */ } } @@ -726,13 +750,10 @@ public class BSScene : PhysicsScene, IPhysicsParameters public void VehicleInSceneTypeChanged(BSPrim vehic, Vehicle newType) { - if (newType == Vehicle.TYPE_NONE) + RemoveVehiclePrim(vehic); + if (newType != Vehicle.TYPE_NONE) { - RemoveVehiclePrim(vehic); - } - else - { - // make it so the scene will call us each tick to do vehicle things + // make it so the scene will call us each tick to do vehicle things AddVehiclePrim(vehic); } } @@ -764,7 +785,7 @@ public class BSScene : PhysicsScene, IPhysicsParameters } // Some prims have extra vehicle actions - // no locking because only called when physics engine is not busy + // Called at taint time! private void ProcessVehicles(float timeStep) { foreach (BSPhysObject pobj in m_vehicles) @@ -833,6 +854,11 @@ public class BSScene : PhysicsScene, IPhysicsParameters (s,cf,p,v) => { s.ShouldForceSimplePrimMeshing = cf.GetBoolean(p, s.BoolNumeric(v)); }, (s) => { return s.NumericBool(s.ShouldForceSimplePrimMeshing); }, (s,p,l,v) => { s.ShouldForceSimplePrimMeshing = s.BoolNumeric(v); } ), + new ParameterDefn("UseHullsForPhysicalObjects", "If true, create hulls for physical objects", + ConfigurationParameters.numericTrue, + (s,cf,p,v) => { s.ShouldUseHullsForPhysicalObjects = cf.GetBoolean(p, s.BoolNumeric(v)); }, + (s) => { return s.NumericBool(s.ShouldUseHullsForPhysicalObjects); }, + (s,p,l,v) => { s.ShouldUseHullsForPhysicalObjects = s.BoolNumeric(v); } ), new ParameterDefn("MeshLevelOfDetail", "Level of detail to render meshes (32, 16, 8 or 4. 32=most detailed)", 8f, @@ -875,6 +901,11 @@ public class BSScene : PhysicsScene, IPhysicsParameters (s,cf,p,v) => { s.m_maxUpdatesPerFrame = cf.GetInt(p, (int)v); }, (s) => { return (float)s.m_maxUpdatesPerFrame; }, (s,p,l,v) => { s.m_maxUpdatesPerFrame = (int)v; } ), + new ParameterDefn("MaxTaintsToProcessPerStep", "Number of update taints to process before each simulation step", + 100f, + (s,cf,p,v) => { s.m_taintsToProcessPerStep = cf.GetInt(p, (int)v); }, + (s) => { return (float)s.m_taintsToProcessPerStep; }, + (s,p,l,v) => { s.m_taintsToProcessPerStep = (int)v; } ), new ParameterDefn("MaxObjectMass", "Maximum object mass (10000.01)", 10000.01f, (s,cf,p,v) => { s.MaximumObjectMass = cf.GetFloat(p, v); }, @@ -1008,12 +1039,12 @@ public class BSScene : PhysicsScene, IPhysicsParameters new ParameterDefn("MaxPersistantManifoldPoolSize", "Number of manifolds pooled (0 means default of 4096)", - 0f, // zero to disable + 0f, (s,cf,p,v) => { s.m_params[0].maxPersistantManifoldPoolSize = cf.GetFloat(p, v); }, (s) => { return s.m_params[0].maxPersistantManifoldPoolSize; }, (s,p,l,v) => { s.m_params[0].maxPersistantManifoldPoolSize = v; } ), new ParameterDefn("MaxCollisionAlgorithmPoolSize", "Number of collisions pooled (0 means default of 4096)", - 0f, // zero to disable + 0f, (s,cf,p,v) => { s.m_params[0].maxCollisionAlgorithmPoolSize = cf.GetFloat(p, v); }, (s) => { return s.m_params[0].maxCollisionAlgorithmPoolSize; }, (s,p,l,v) => { s.m_params[0].maxCollisionAlgorithmPoolSize = v; } ), @@ -1028,7 +1059,7 @@ public class BSScene : PhysicsScene, IPhysicsParameters (s) => { return s.m_params[0].shouldForceUpdateAllAabbs; }, (s,p,l,v) => { s.m_params[0].shouldForceUpdateAllAabbs = v; } ), new ParameterDefn("ShouldRandomizeSolverOrder", "Enable for slightly better stacking interaction", - ConfigurationParameters.numericFalse, + ConfigurationParameters.numericTrue, (s,cf,p,v) => { s.m_params[0].shouldRandomizeSolverOrder = s.NumericBool(cf.GetBoolean(p, s.BoolNumeric(v))); }, (s) => { return s.m_params[0].shouldRandomizeSolverOrder; }, (s,p,l,v) => { s.m_params[0].shouldRandomizeSolverOrder = v; } ), @@ -1069,12 +1100,12 @@ public class BSScene : PhysicsScene, IPhysicsParameters (s) => { return s.m_params[0].linkConstraintTransMotorMaxForce; }, (s,p,l,v) => { s.m_params[0].linkConstraintTransMotorMaxForce = v; } ), new ParameterDefn("LinkConstraintCFM", "Amount constraint can be violated. 0=no violation, 1=infinite. Default=0.1", - 0.1f, + 0.001f, (s,cf,p,v) => { s.m_params[0].linkConstraintCFM = cf.GetFloat(p, v); }, (s) => { return s.m_params[0].linkConstraintCFM; }, (s,p,l,v) => { s.m_params[0].linkConstraintCFM = v; } ), new ParameterDefn("LinkConstraintERP", "Amount constraint is corrected each tick. 0=none, 1=all. Default = 0.2", - 0.2f, + 0.8f, (s,cf,p,v) => { s.m_params[0].linkConstraintERP = cf.GetFloat(p, v); }, (s) => { return s.m_params[0].linkConstraintERP; }, (s,p,l,v) => { s.m_params[0].linkConstraintERP = v; } ), @@ -1084,11 +1115,11 @@ public class BSScene : PhysicsScene, IPhysicsParameters (s) => { return s.m_params[0].linkConstraintSolverIterations; }, (s,p,l,v) => { s.m_params[0].linkConstraintSolverIterations = v; } ), - new ParameterDefn("DetailedStats", "Frames between outputting detailed phys stats. (0 is off)", + new ParameterDefn("LogPhysicsStatisticsFrames", "Frames between outputting detailed phys stats. (0 is off)", 0f, - (s,cf,p,v) => { s.m_detailedStatsStep = cf.GetInt(p, (int)v); }, - (s) => { return (float)s.m_detailedStatsStep; }, - (s,p,l,v) => { s.m_detailedStatsStep = (int)v; } ), + (s,cf,p,v) => { s.m_params[0].physicsLoggingFrames = cf.GetInt(p, (int)v); }, + (s) => { return (float)s.m_params[0].physicsLoggingFrames; }, + (s,p,l,v) => { s.m_params[0].physicsLoggingFrames = (int)v; } ), }; // Convert a boolean to our numeric true and false values @@ -1152,7 +1183,6 @@ public class BSScene : PhysicsScene, IPhysicsParameters { if (SettableParameters.Length < ParameterDefinitions.Length) { - List entries = new List(); for (int ii = 0; ii < ParameterDefinitions.Length; ii++) { @@ -1270,6 +1300,8 @@ public class BSScene : PhysicsScene, IPhysicsParameters public void DetailLog(string msg, params Object[] args) { PhysicsLogging.Write(msg, args); + // Add the Flush() if debugging crashes to get all the messages written out. + // PhysicsLogging.Flush(); } // used to fill in the LocalID when there isn't one public const string DetailLogZero = "0000000000"; diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSShapeCollection.cs b/OpenSim/Region/Physics/BulletSPlugin/BSShapeCollection.cs index dee6243b7a..d189f1d287 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSShapeCollection.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSShapeCollection.cs @@ -36,7 +36,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin { public class BSShapeCollection : IDisposable { - private static string LogHeader = "[BULLETSIM SHAPE COLLECTION]"; + // private static string LogHeader = "[BULLETSIM SHAPE COLLECTION]"; protected BSScene PhysicsScene { get; set; } @@ -67,8 +67,8 @@ public class BSShapeCollection : IDisposable public DateTime lastReferenced; } - private Dictionary Meshes = new Dictionary(); - private Dictionary Hulls = new Dictionary(); + private Dictionary Meshes = new Dictionary(); + private Dictionary Hulls = new Dictionary(); private Dictionary Bodies = new Dictionary(); public BSShapeCollection(BSScene physScene) @@ -108,7 +108,8 @@ public class BSShapeCollection : IDisposable // If we had to select a new shape geometry for the object, // rebuild the body around it. // Updates prim.BSBody with information/pointers to requested body - bool newBody = CreateBody((newGeom || forceRebuild), prim, PhysicsScene.World, prim.BSShape, shapeData, bodyCallback); + bool newBody = CreateBody((newGeom || forceRebuild), prim, PhysicsScene.World, + prim.BSShape, shapeData, bodyCallback); ret = newGeom || newBody; } DetailLog("{0},BSShapeCollection.GetBodyAndShape,force={1},ret={2},body={3},shape={4}", @@ -120,7 +121,7 @@ public class BSShapeCollection : IDisposable // Track another user of a body // We presume the caller has allocated the body. // Bodies only have one user so the reference count is either 1 or 0. - public void ReferenceBody(BulletBody body, bool atTaintTime) + public void ReferenceBody(BulletBody body, bool inTaintTime) { lock (m_collectionActivityLock) { @@ -135,12 +136,26 @@ public class BSShapeCollection : IDisposable // New entry bodyDesc.ptr = body.ptr; bodyDesc.referenceCount = 1; - DetailLog("{0},BSShapeCollection.ReferenceBody,newBody,ref={1}", body.ID, body, bodyDesc.referenceCount); + DetailLog("{0},BSShapeCollection.ReferenceBody,newBody,ref={2}", + body.ID, body, bodyDesc.referenceCount); + BSScene.TaintCallback createOperation = delegate() + { + if (!BulletSimAPI.IsInWorld2(body.ptr)) + { + BulletSimAPI.AddObjectToWorld2(PhysicsScene.World.ptr, body.ptr); + DetailLog("{0},BSShapeCollection.ReferenceBody,addedToWorld,ref={1}", + body.ID, body); + } + }; + if (inTaintTime) + createOperation(); + else + PhysicsScene.TaintedObject("BSShapeCollection.ReferenceBody", createOperation); } bodyDesc.lastReferenced = System.DateTime.Now; Bodies[body.ID] = bodyDesc; } -} + } // Release the usage of a body. // Called when releasing use of a BSBody. BSShape is handled separately. @@ -159,21 +174,22 @@ public class BSShapeCollection : IDisposable Bodies[body.ID] = bodyDesc; DetailLog("{0},BSShapeCollection.DereferenceBody,ref={1}", body.ID, bodyDesc.referenceCount); - // If body is no longer being used, free it -- bodies are never shared. + // If body is no longer being used, free it -- bodies can never be shared. if (bodyDesc.referenceCount == 0) { Bodies.Remove(body.ID); BSScene.TaintCallback removeOperation = delegate() { - DetailLog("{0},BSShapeCollection.DereferenceBody,DestroyingBody. ptr={1}", - body.ID, body.ptr.ToString("X")); - // If the caller needs to know, pass the event up. + DetailLog("{0},BSShapeCollection.DereferenceBody,DestroyingBody. ptr={1}, inTaintTime={2}", + body.ID, body.ptr.ToString("X"), inTaintTime); + // If the caller needs to know the old body is going away, pass the event up. if (bodyCallback != null) bodyCallback(body); + // It may have already been removed from the world in which case the next is a NOOP. + BulletSimAPI.RemoveObjectFromWorld2(PhysicsScene.World.ptr, body.ptr); + // Zero any reference to the shape so it is not freed when the body is deleted. BulletSimAPI.SetCollisionShape2(PhysicsScene.World.ptr, body.ptr, IntPtr.Zero); - // It may have already been removed from the world in which case the next is a NOOP. - BulletSimAPI.RemoveObjectFromWorld2(PhysicsScene.World.ptr, body.ptr); BulletSimAPI.DestroyObject2(PhysicsScene.World.ptr, body.ptr); }; // If already in taint-time, do the operations now. Otherwise queue for later. @@ -207,7 +223,7 @@ public class BSShapeCollection : IDisposable { // There is an existing instance of this mesh. meshDesc.referenceCount++; - DetailLog("{0},BSShapeColliction.ReferenceShape,existingMesh,key={1},cnt={2}", + DetailLog("{0},BSShapeCollection.ReferenceShape,existingMesh,key={1},cnt={2}", BSScene.DetailLogZero, shape.shapeKey.ToString("X"), meshDesc.referenceCount); } else @@ -216,7 +232,7 @@ public class BSShapeCollection : IDisposable meshDesc.ptr = shape.ptr; // We keep a reference to the underlying IMesh data so a hull can be built meshDesc.referenceCount = 1; - DetailLog("{0},BSShapeColliction.ReferenceShape,newMesh,key={1},cnt={2}", + DetailLog("{0},BSShapeCollection.ReferenceShape,newMesh,key={1},cnt={2}", BSScene.DetailLogZero, shape.shapeKey.ToString("X"), meshDesc.referenceCount); ret = true; } @@ -229,7 +245,7 @@ public class BSShapeCollection : IDisposable { // There is an existing instance of this hull. hullDesc.referenceCount++; - DetailLog("{0},BSShapeColliction.ReferenceShape,existingHull,key={1},cnt={2}", + DetailLog("{0},BSShapeCollection.ReferenceShape,existingHull,key={1},cnt={2}", BSScene.DetailLogZero, shape.shapeKey.ToString("X"), hullDesc.referenceCount); } else @@ -237,7 +253,7 @@ public class BSShapeCollection : IDisposable // This is a new reference to a hull hullDesc.ptr = shape.ptr; hullDesc.referenceCount = 1; - DetailLog("{0},BSShapeColliction.ReferenceShape,newHull,key={1},cnt={2}", + DetailLog("{0},BSShapeCollection.ReferenceShape,newHull,key={1},cnt={2}", BSScene.DetailLogZero, shape.shapeKey.ToString("X"), hullDesc.referenceCount); ret = true; @@ -256,7 +272,7 @@ public class BSShapeCollection : IDisposable // Release the usage of a shape. // The collisionObject is released since it is a copy of the real collision shape. - public void DereferenceShape(BulletShape shape, bool atTaintTime, ShapeDestructionCallback shapeCallback) + public void DereferenceShape(BulletShape shape, bool inTaintTime, ShapeDestructionCallback shapeCallback) { if (shape.ptr == IntPtr.Zero) return; @@ -278,14 +294,14 @@ public class BSShapeCollection : IDisposable if (shape.ptr != IntPtr.Zero & shape.isNativeShape) { DetailLog("{0},BSShapeCollection.DereferenceShape,deleteNativeShape,ptr={1},taintTime={2}", - BSScene.DetailLogZero, shape.ptr.ToString("X"), atTaintTime); + BSScene.DetailLogZero, shape.ptr.ToString("X"), inTaintTime); if (shapeCallback != null) shapeCallback(shape); BulletSimAPI.DeleteCollisionShape2(PhysicsScene.World.ptr, shape.ptr); } break; } }; - if (atTaintTime) + if (inTaintTime) { lock (m_collectionActivityLock) { @@ -357,7 +373,8 @@ public class BSShapeCollection : IDisposable && pbs.PathScaleX == 100 && pbs.PathScaleY == 100 && pbs.PathShearX == 0 && pbs.PathShearY == 0) ) ) { - if (pbs.ProfileShape == ProfileShape.HalfCircle && pbs.PathCurve == (byte)Extrusion.Curve1) + if ((pbs.ProfileShape == ProfileShape.HalfCircle && pbs.PathCurve == (byte)Extrusion.Curve1) + && pbs.Scale.X == pbs.Scale.Y && pbs.Scale.Y == pbs.Scale.Z) { haveShape = true; if (forceRebuild @@ -371,7 +388,7 @@ public class BSShapeCollection : IDisposable prim.LocalID, forceRebuild, prim.BSShape); } } - else + if (pbs.ProfileShape == ProfileShape.Square && pbs.PathCurve == (byte)Extrusion.Straight) { haveShape = true; if (forceRebuild @@ -391,7 +408,7 @@ public class BSShapeCollection : IDisposable // made. Native shapes are best used in either case. if (!haveShape) { - if (prim.IsPhysical) + if (prim.IsPhysical && PhysicsScene.ShouldUseHullsForPhysicalObjects) { // Update prim.BSShape to reference a hull of this shape. ret = GetReferenceToHull(prim, shapeData, pbs, shapeCallback); @@ -425,7 +442,7 @@ public class BSShapeCollection : IDisposable // Native shapes are always built independently. newShape = new BulletShape(BulletSimAPI.BuildNativeShape2(PhysicsScene.World.ptr, shapeData), shapeType); - newShape.shapeKey = (ulong)shapeKey; + newShape.shapeKey = (System.UInt64)shapeKey; newShape.isNativeShape = true; // Don't need to do a 'ReferenceShape()' here because native shapes are not tracked. @@ -445,10 +462,11 @@ public class BSShapeCollection : IDisposable BulletShape newShape = new BulletShape(IntPtr.Zero); float lod; - ulong newMeshKey = ComputeShapeKey(shapeData, pbs, out lod); + System.UInt64 newMeshKey = ComputeShapeKey(shapeData, pbs, out lod); // if this new shape is the same as last time, don't recreate the mesh - if (prim.BSShape.shapeKey == newMeshKey) return false; + if (newMeshKey == prim.BSShape.shapeKey && prim.BSShape.type == ShapeData.PhysicsShapeType.SHAPE_MESH) + return false; DetailLog("{0},BSShapeCollection.CreateGeomMesh,create,oldKey={1},newKey={2}", prim.LocalID, prim.BSShape.shapeKey.ToString("X"), newMeshKey.ToString("X")); @@ -467,7 +485,7 @@ public class BSShapeCollection : IDisposable return true; // 'true' means a new shape has been added to this prim } - private BulletShape CreatePhysicalMesh(string objName, ulong newMeshKey, PrimitiveBaseShape pbs, OMV.Vector3 size, float lod) + private BulletShape CreatePhysicalMesh(string objName, System.UInt64 newMeshKey, PrimitiveBaseShape pbs, OMV.Vector3 size, float lod) { IMesh meshData = null; IntPtr meshPtr; @@ -514,7 +532,7 @@ public class BSShapeCollection : IDisposable BulletShape newShape; float lod; - ulong newHullKey = ComputeShapeKey(shapeData, pbs, out lod); + System.UInt64 newHullKey = ComputeShapeKey(shapeData, pbs, out lod); // if the hull hasn't changed, don't rebuild it if (newHullKey == prim.BSShape.shapeKey && prim.BSShape.type == ShapeData.PhysicsShapeType.SHAPE_HULL) @@ -523,7 +541,7 @@ public class BSShapeCollection : IDisposable DetailLog("{0},BSShapeCollection.CreateGeomHull,create,oldKey={1},newKey={2}", prim.LocalID, prim.BSShape.shapeKey.ToString("X"), newHullKey.ToString("X")); - // Remove usage of the previous shape. Also removes reference to underlying mesh if it is a hull. + // Remove usage of the previous shape. DereferenceShape(prim.BSShape, true, shapeCallback); newShape = CreatePhysicalHull(prim.PhysObjectName, newHullKey, pbs, shapeData.Size, lod); @@ -537,7 +555,7 @@ public class BSShapeCollection : IDisposable } List m_hulls; - private BulletShape CreatePhysicalHull(string objName, ulong newHullKey, PrimitiveBaseShape pbs, OMV.Vector3 size, float lod) + private BulletShape CreatePhysicalHull(string objName, System.UInt64 newHullKey, PrimitiveBaseShape pbs, OMV.Vector3 size, float lod) { IntPtr hullPtr; @@ -650,22 +668,23 @@ public class BSShapeCollection : IDisposable // Create a hash of all the shape parameters to be used as a key // for this particular shape. - private ulong ComputeShapeKey(ShapeData shapeData, PrimitiveBaseShape pbs, out float retLod) + private System.UInt64 ComputeShapeKey(ShapeData shapeData, PrimitiveBaseShape pbs, out float retLod) { // level of detail based on size and type of the object float lod = PhysicsScene.MeshLOD; if (pbs.SculptEntry) lod = PhysicsScene.SculptLOD; + // Mega prims usually get more detail because one can interact with shape approximations at this size. float maxAxis = Math.Max(shapeData.Size.X, Math.Max(shapeData.Size.Y, shapeData.Size.Z)); if (maxAxis > PhysicsScene.MeshMegaPrimThreshold) lod = PhysicsScene.MeshMegaPrimLOD; retLod = lod; - return (ulong)pbs.GetMeshKey(shapeData.Size, lod); + return pbs.GetMeshKey(shapeData.Size, lod); } // For those who don't want the LOD - private ulong ComputeShapeKey(ShapeData shapeData, PrimitiveBaseShape pbs) + private System.UInt64 ComputeShapeKey(ShapeData shapeData, PrimitiveBaseShape pbs) { float lod; return ComputeShapeKey(shapeData, pbs, out lod); @@ -699,6 +718,7 @@ public class BSShapeCollection : IDisposable if (mustRebuild || forceRebuild) { + // Free any old body DereferenceBody(prim.BSBody, true, bodyCallback); BulletBody aBody; @@ -707,13 +727,13 @@ public class BSShapeCollection : IDisposable { bodyPtr = BulletSimAPI.CreateBodyFromShape2(sim.ptr, shape.ptr, shapeData.ID, shapeData.Position, shapeData.Rotation); - // DetailLog("{0},BSShapeCollection.CreateBody,mesh,ptr={1}", prim.LocalID, bodyPtr.ToString("X")); + DetailLog("{0},BSShapeCollection.CreateBody,mesh,ptr={1}", prim.LocalID, bodyPtr.ToString("X")); } else { bodyPtr = BulletSimAPI.CreateGhostFromShape2(sim.ptr, shape.ptr, shapeData.ID, shapeData.Position, shapeData.Rotation); - // DetailLog("{0},BSShapeCollection.CreateBody,ghost,ptr={1}", prim.LocalID, bodyPtr.ToString("X")); + DetailLog("{0},BSShapeCollection.CreateBody,ghost,ptr={1}", prim.LocalID, bodyPtr.ToString("X")); } aBody = new BulletBody(shapeData.ID, bodyPtr); @@ -729,7 +749,8 @@ public class BSShapeCollection : IDisposable private void DetailLog(string msg, params Object[] args) { - PhysicsScene.PhysicsLogging.Write(msg, args); + if (PhysicsScene.PhysicsLogging.Enabled) + PhysicsScene.DetailLog(msg, args); } } } diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSTerrainManager.cs b/OpenSim/Region/Physics/BulletSPlugin/BSTerrainManager.cs index 70aa429fc3..caf411e695 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSTerrainManager.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSTerrainManager.cs @@ -114,6 +114,8 @@ public class BSTerrainManager BulletSimAPI.CreateBodyWithDefaultMotionState2(groundPlaneShape.ptr, BSScene.GROUNDPLANE_ID, Vector3.Zero, Quaternion.Identity)); BulletSimAPI.AddObjectToWorld2(PhysicsScene.World.ptr, m_groundPlane.ptr); + // Ground plane does not move + BulletSimAPI.ForceActivationState2(m_groundPlane.ptr, ActivationState.DISABLE_SIMULATION); // Everything collides with the ground plane. BulletSimAPI.SetCollisionFilterMask2(m_groundPlane.ptr, (uint)CollisionFilterGroups.GroundPlaneFilter, (uint)CollisionFilterGroups.GroundPlaneMask); @@ -201,10 +203,10 @@ public class BSTerrainManager // The 'doNow' boolean says whether to do all the unmanaged activities right now (like when // calling this routine from initialization or taint-time routines) or whether to delay // all the unmanaged activities to taint-time. - private void UpdateOrCreateTerrain(uint id, float[] heightMap, Vector3 minCoords, Vector3 maxCoords, bool atTaintTime) + private void UpdateOrCreateTerrain(uint id, float[] heightMap, Vector3 minCoords, Vector3 maxCoords, bool inTaintTime) { - DetailLog("{0},BSTerrainManager.UpdateOrCreateTerrain,call,minC={1},maxC={2},atTaintTime={3}", - BSScene.DetailLogZero, minCoords, maxCoords, atTaintTime); + DetailLog("{0},BSTerrainManager.UpdateOrCreateTerrain,call,minC={1},maxC={2},inTaintTime={3}", + BSScene.DetailLogZero, minCoords, maxCoords, inTaintTime); float minZ = float.MaxValue; float maxZ = float.MinValue; @@ -296,16 +298,16 @@ public class BSTerrainManager mapInfo.Ptr = BulletSimAPI.CreateHeightMapInfo2(PhysicsScene.World.ptr, mapInfo.ID, mapInfo.minCoords, mapInfo.maxCoords, mapInfo.heightMap, TERRAIN_COLLISION_MARGIN); + // Create the terrain shape from the mapInfo + mapInfo.terrainShape = new BulletShape(BulletSimAPI.CreateTerrainShape2(mapInfo.Ptr), + ShapeData.PhysicsShapeType.SHAPE_TERRAIN); + // The terrain object initial position is at the center of the object Vector3 centerPos; centerPos.X = minCoords.X + (mapInfo.sizeX / 2f); centerPos.Y = minCoords.Y + (mapInfo.sizeY / 2f); centerPos.Z = minZ + ((maxZ - minZ) / 2f); - // Create the terrain shape from the mapInfo - mapInfo.terrainShape = new BulletShape(BulletSimAPI.CreateTerrainShape2(mapInfo.Ptr), - ShapeData.PhysicsShapeType.SHAPE_TERRAIN); - mapInfo.terrainBody = new BulletBody(mapInfo.ID, BulletSimAPI.CreateBodyWithDefaultMotionState2(mapInfo.terrainShape.ptr, id, centerPos, Quaternion.Identity)); @@ -320,9 +322,6 @@ public class BSTerrainManager BulletSimAPI.SetRestitution2(mapInfo.terrainBody.ptr, PhysicsScene.Params.terrainRestitution); BulletSimAPI.SetCollisionFlags2(mapInfo.terrainBody.ptr, CollisionFlags.CF_STATIC_OBJECT); - BulletSimAPI.SetMassProps2(mapInfo.terrainBody.ptr, 0f, Vector3.Zero); - BulletSimAPI.UpdateInertiaTensor2(mapInfo.terrainBody.ptr); - // Return the new terrain to the world of physical objects BulletSimAPI.AddObjectToWorld2(PhysicsScene.World.ptr, mapInfo.terrainBody.ptr); @@ -342,7 +341,7 @@ public class BSTerrainManager // There is the option to do the changes now (we're already in 'taint time'), or // to do the Bullet operations later. - if (atTaintTime) + if (inTaintTime) rebuildOperation(); else PhysicsScene.TaintedObject("BSScene.UpdateOrCreateTerrain:UpdateExisting", rebuildOperation); @@ -381,7 +380,7 @@ public class BSTerrainManager }; // If already in taint-time, just call Bullet. Otherwise queue the operations for the safe time. - if (atTaintTime) + if (inTaintTime) createOperation(); else PhysicsScene.TaintedObject("BSScene.UpdateOrCreateTerrain:NewTerrain", createOperation); diff --git a/OpenSim/Region/Physics/BulletSPlugin/BulletSimAPI.cs b/OpenSim/Region/Physics/BulletSPlugin/BulletSimAPI.cs index 1125d7eb6a..276111c2b1 100644 --- a/OpenSim/Region/Physics/BulletSPlugin/BulletSimAPI.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BulletSimAPI.cs @@ -71,7 +71,7 @@ public struct BulletBody buff.Append(ID.ToString()); buff.Append(",p="); buff.Append(ptr.ToString("X")); - if (collisionFilter != 0 && collisionMask != 0) + if (collisionFilter != 0 || collisionMask != 0) { buff.Append(",f="); buff.Append(collisionFilter.ToString("X")); @@ -101,9 +101,8 @@ public struct BulletShape } public IntPtr ptr; public ShapeData.PhysicsShapeType type; - public ulong shapeKey; + public System.UInt64 shapeKey; public bool isNativeShape; - // Hulls have an underlying mesh. A pointer to it is hidden here. public override string ToString() { StringBuilder buff = new StringBuilder(); @@ -192,8 +191,9 @@ public struct ShapeData SHAPE_SPHERE = 5, SHAPE_MESH = 6, SHAPE_HULL = 7, - SHAPE_GROUNDPLANE = 8, - SHAPE_TERRAIN = 9, + // following defined by BulletSim + SHAPE_GROUNDPLANE = 20, + SHAPE_TERRAIN = 21, }; public uint ID; public PhysicsShapeType Type; @@ -305,6 +305,8 @@ public struct ConfigurationParameters public float linkConstraintCFM; public float linkConstraintSolverIterations; + public float physicsLoggingFrames; + public const float numericTrue = 1f; public const float numericFalse = 0f; } @@ -344,10 +346,7 @@ public enum CollisionFlags : uint CF_DISABLE_SPU_COLLISION_PROCESS = 1 << 6, // Following used by BulletSim to control collisions BS_SUBSCRIBE_COLLISION_EVENTS = 1 << 10, - // BS_VOLUME_DETECT_OBJECT = 1 << 11, - // BS_PHANTOM_OBJECT = 1 << 12, - // BS_PHYSICAL_OBJECT = 1 << 13, - // BS_TERRAIN_OBJECT = 1 << 14, + BS_FLOATS_ON_WATER = 1 << 11, BS_NONE = 0, BS_ALL = 0xFFFFFFFF, @@ -356,9 +355,6 @@ public enum CollisionFlags : uint BS_ACTIVE = CF_STATIC_OBJECT | CF_KINEMATIC_OBJECT | CF_NO_CONTACT_RESPONSE - // | BS_VOLUME_DETECT_OBJECT - // | BS_PHANTOM_OBJECT - // | BS_PHYSICAL_OBJECT, }; // Values for collisions groups and masks @@ -1042,18 +1038,6 @@ public static extern IntPtr GetConstraintRef2(IntPtr obj, int index); [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] public static extern int GetNumConstraintRefs2(IntPtr obj); -[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] -public static extern Vector3 GetDeltaLinearVelocity2(IntPtr obj); - -[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] -public static extern Vector3 GetDeltaAngularVelocity2(IntPtr obj); - -[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] -public static extern Vector3 GetPushVelocity2(IntPtr obj); - -[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] -public static extern Vector3 GetTurnVelocity2(IntPtr obj); - [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] public static extern void SetCollisionFilterMask2(IntPtr body, uint filter, uint mask); @@ -1113,6 +1097,15 @@ public static extern float GetMargin2(IntPtr shape); [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] public static extern void DumpRigidBody2(IntPtr sim, IntPtr collisionObject); +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern void DumpCollisionShape2(IntPtr sim, IntPtr collisionShape); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern void DumpConstraint2(IntPtr sim, IntPtr constrain); + +[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] +public static extern void DumpAllInfo2(IntPtr sim); + [DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] public static extern void DumpMapInfo2(IntPtr sim, IntPtr manInfo); diff --git a/OpenSim/Region/Physics/Manager/PhysicsScene.cs b/OpenSim/Region/Physics/Manager/PhysicsScene.cs index 6a0558a35e..488900e4ff 100644 --- a/OpenSim/Region/Physics/Manager/PhysicsScene.cs +++ b/OpenSim/Region/Physics/Manager/PhysicsScene.cs @@ -76,7 +76,7 @@ namespace OpenSim.Region.Physics.Manager get { return new NullPhysicsScene(); } } - public RequestAssetDelegate RequestAssetMethod { private get; set; } + public RequestAssetDelegate RequestAssetMethod { get; set; } public virtual void TriggerPhysicsBasedRestart() { diff --git a/OpenSim/Region/Physics/OdePlugin/ODECharacter.cs b/OpenSim/Region/Physics/OdePlugin/ODECharacter.cs index f3b0630843..c73655791b 100644 --- a/OpenSim/Region/Physics/OdePlugin/ODECharacter.cs +++ b/OpenSim/Region/Physics/OdePlugin/ODECharacter.cs @@ -100,7 +100,7 @@ namespace OpenSim.Region.Physics.OdePlugin private bool m_hackSentFly = false; private int m_requestedUpdateFrequency = 0; private Vector3 m_taintPosition; - + internal bool m_avatarplanted = false; /// /// Hold set forces so we can process them outside physics calculations. This prevents race conditions if we set force /// while calculatios are going on @@ -413,7 +413,7 @@ namespace OpenSim.Region.Physics.OdePlugin set { m_iscollidingObj = value; - if (value) + if (value && !m_avatarplanted) m_pidControllerActive = false; else m_pidControllerActive = true; diff --git a/OpenSim/Region/Physics/OdePlugin/ODEPrim.cs b/OpenSim/Region/Physics/OdePlugin/ODEPrim.cs index 071621495a..5a1fdf9dff 100644 --- a/OpenSim/Region/Physics/OdePlugin/ODEPrim.cs +++ b/OpenSim/Region/Physics/OdePlugin/ODEPrim.cs @@ -63,6 +63,9 @@ namespace OpenSim.Region.Physics.OdePlugin private bool m_isphysical; + public int ExpectedCollisionContacts { get { return m_expectedCollisionContacts; } } + private int m_expectedCollisionContacts = 0; + /// /// Is this prim subject to physics? Even if not, it's still solid for collision purposes. /// @@ -97,6 +100,9 @@ namespace OpenSim.Region.Physics.OdePlugin private Vector3 m_taintAngularLock = Vector3.One; private IntPtr Amotor = IntPtr.Zero; + private object m_assetsLock = new object(); + private bool m_assetFailed = false; + private Vector3 m_PIDTarget; private float m_PIDTau; private float PID_D = 35f; @@ -150,7 +156,7 @@ namespace OpenSim.Region.Physics.OdePlugin private PrimitiveBaseShape _pbs; private OdeScene _parent_scene; - + /// /// The physics space which contains prim geometries /// @@ -279,6 +285,7 @@ namespace OpenSim.Region.Physics.OdePlugin } m_taintadd = true; + m_assetFailed = false; _parent_scene.AddPhysicsActorTaint(this); } @@ -334,8 +341,17 @@ namespace OpenSim.Region.Physics.OdePlugin prim_geom = geom; //Console.WriteLine("SetGeom to " + prim_geom + " for " + Name); - d.GeomSetCategoryBits(prim_geom, (int)m_collisionCategories); - d.GeomSetCollideBits(prim_geom, (int)m_collisionFlags); + if (m_assetFailed) + { + d.GeomSetCategoryBits(prim_geom, 0); + d.GeomSetCollideBits(prim_geom, BadAssetColideBits()); + } + else + { + + d.GeomSetCategoryBits(prim_geom, (int)m_collisionCategories); + d.GeomSetCollideBits(prim_geom, (int)m_collisionFlags); + } _parent_scene.geom_name_map[prim_geom] = Name; _parent_scene.actor_name_map[prim_geom] = this; @@ -398,8 +414,17 @@ namespace OpenSim.Region.Physics.OdePlugin myrot.W = _orientation.W; d.BodySetQuaternion(Body, ref myrot); d.GeomSetBody(prim_geom, Body); - m_collisionCategories |= CollisionCategories.Body; - m_collisionFlags |= (CollisionCategories.Land | CollisionCategories.Wind); + + if (m_assetFailed) + { + d.GeomSetCategoryBits(prim_geom, 0); + d.GeomSetCollideBits(prim_geom, BadAssetColideBits()); + } + else + { + m_collisionCategories |= CollisionCategories.Body; + m_collisionFlags |= (CollisionCategories.Land | CollisionCategories.Wind); + } d.GeomSetCategoryBits(prim_geom, (int)m_collisionCategories); d.GeomSetCollideBits(prim_geom, (int)m_collisionFlags); @@ -771,8 +796,16 @@ namespace OpenSim.Region.Physics.OdePlugin m_collisionCategories &= ~CollisionCategories.Body; m_collisionFlags &= ~(CollisionCategories.Wind | CollisionCategories.Land); - d.GeomSetCategoryBits(prim_geom, (int)m_collisionCategories); - d.GeomSetCollideBits(prim_geom, (int)m_collisionFlags); + if (m_assetFailed) + { + d.GeomSetCategoryBits(prim_geom, 0); + d.GeomSetCollideBits(prim_geom, 0); + } + else + { + d.GeomSetCategoryBits(prim_geom, (int)m_collisionCategories); + d.GeomSetCollideBits(prim_geom, (int)m_collisionFlags); + } d.BodyDestroy(Body); lock (childrenPrim) @@ -796,8 +829,17 @@ namespace OpenSim.Region.Physics.OdePlugin m_collisionCategories &= ~CollisionCategories.Body; m_collisionFlags &= ~(CollisionCategories.Wind | CollisionCategories.Land); - d.GeomSetCategoryBits(prim_geom, (int)m_collisionCategories); - d.GeomSetCollideBits(prim_geom, (int)m_collisionFlags); + if (m_assetFailed) + { + d.GeomSetCategoryBits(prim_geom, 0); + d.GeomSetCollideBits(prim_geom, 0); + } + else + { + + d.GeomSetCategoryBits(prim_geom, (int)m_collisionCategories); + d.GeomSetCollideBits(prim_geom, (int)m_collisionFlags); + } Body = IntPtr.Zero; } @@ -809,6 +851,11 @@ namespace OpenSim.Region.Physics.OdePlugin private static Dictionary m_MeshToTriMeshMap = new Dictionary(); + public int BadAssetColideBits() + { + return (m_isphysical ? (int)CollisionCategories.Land : 0); + } + private void setMesh(OdeScene parent_scene, IMesh mesh) { // m_log.DebugFormat("[ODE PRIM]: Setting mesh on {0} to {1}", Name, mesh); @@ -840,7 +887,7 @@ namespace OpenSim.Region.Physics.OdePlugin int vertexStride, triStride; mesh.getVertexListAsPtrToFloatArray(out vertices, out vertexStride, out vertexCount); // Note, that vertices are fixed in unmanaged heap mesh.getIndexListAsPtrToIntArray(out indices, out triStride, out indexCount); // Also fixed, needs release after usage - + m_expectedCollisionContacts = indexCount; mesh.releaseSourceMeshData(); // free up the original mesh data to save memory // We must lock here since m_MeshToTriMeshMap is static and multiple scene threads may call this method at @@ -1087,8 +1134,16 @@ Console.WriteLine("ZProcessTaints for " + Name); prm.m_collisionFlags |= (CollisionCategories.Land | CollisionCategories.Wind); //Console.WriteLine(" GeomSetCategoryBits 1: " + prm.prim_geom + " - " + (int)prm.m_collisionCategories + " for " + Name); - d.GeomSetCategoryBits(prm.prim_geom, (int)prm.m_collisionCategories); - d.GeomSetCollideBits(prm.prim_geom, (int)prm.m_collisionFlags); + if (prm.m_assetFailed) + { + d.GeomSetCategoryBits(prm.prim_geom, 0); + d.GeomSetCollideBits(prm.prim_geom, prm.BadAssetColideBits()); + } + else + { + d.GeomSetCategoryBits(prm.prim_geom, (int)prm.m_collisionCategories); + d.GeomSetCollideBits(prm.prim_geom, (int)prm.m_collisionFlags); + } d.Quaternion quat = new d.Quaternion(); quat.W = prm._orientation.W; @@ -1133,10 +1188,18 @@ Console.WriteLine("ZProcessTaints for " + Name); m_collisionCategories |= CollisionCategories.Body; m_collisionFlags |= (CollisionCategories.Land | CollisionCategories.Wind); -//Console.WriteLine("GeomSetCategoryBits 2: " + prim_geom + " - " + (int)m_collisionCategories + " for " + Name); - d.GeomSetCategoryBits(prim_geom, (int)m_collisionCategories); -//Console.WriteLine(" Post GeomSetCategoryBits 2"); - d.GeomSetCollideBits(prim_geom, (int)m_collisionFlags); + if (m_assetFailed) + { + d.GeomSetCategoryBits(prim_geom, 0); + d.GeomSetCollideBits(prim_geom, BadAssetColideBits()); + } + else + { + //Console.WriteLine("GeomSetCategoryBits 2: " + prim_geom + " - " + (int)m_collisionCategories + " for " + Name); + d.GeomSetCategoryBits(prim_geom, (int)m_collisionCategories); + //Console.WriteLine(" Post GeomSetCategoryBits 2"); + d.GeomSetCollideBits(prim_geom, (int)m_collisionFlags); + } d.Quaternion quat2 = new d.Quaternion(); quat2.W = _orientation.W; @@ -1297,8 +1360,16 @@ Console.WriteLine("ZProcessTaints for " + Name); disableBodySoft(); } - d.GeomSetCategoryBits(prim_geom, (int)m_collisionCategories); - d.GeomSetCollideBits(prim_geom, (int)m_collisionFlags); + if (m_assetFailed) + { + d.GeomSetCategoryBits(prim_geom, 0); + d.GeomSetCollideBits(prim_geom, 0); + } + else + { + d.GeomSetCategoryBits(prim_geom, (int)m_collisionCategories); + d.GeomSetCollideBits(prim_geom, (int)m_collisionFlags); + } if (IsPhysical) { @@ -1319,8 +1390,16 @@ Console.WriteLine("ZProcessTaints for " + Name); if (m_collidesWater) m_collisionFlags |= CollisionCategories.Water; - d.GeomSetCategoryBits(prim_geom, (int)m_collisionCategories); - d.GeomSetCollideBits(prim_geom, (int)m_collisionFlags); + if (m_assetFailed) + { + d.GeomSetCategoryBits(prim_geom, 0); + d.GeomSetCollideBits(prim_geom, BadAssetColideBits()); + } + else + { + d.GeomSetCategoryBits(prim_geom, (int)m_collisionCategories); + d.GeomSetCollideBits(prim_geom, (int)m_collisionFlags); + } if (IsPhysical) { @@ -1377,6 +1456,7 @@ Console.WriteLine("CreateGeom:"); { //Console.WriteLine(" CreateGeom 1"); SetGeom(d.CreateSphere(m_targetSpace, _size.X / 2)); + m_expectedCollisionContacts = 3; } catch (AccessViolationException) { @@ -1391,6 +1471,7 @@ Console.WriteLine("CreateGeom:"); { //Console.WriteLine(" CreateGeom 2"); SetGeom(d.CreateBox(m_targetSpace, _size.X, _size.Y, _size.Z)); + m_expectedCollisionContacts = 4; } catch (AccessViolationException) { @@ -1406,6 +1487,7 @@ Console.WriteLine("CreateGeom:"); { //Console.WriteLine(" CreateGeom 3"); SetGeom(d.CreateBox(m_targetSpace, _size.X, _size.Y, _size.Z)); + m_expectedCollisionContacts = 4; } catch (AccessViolationException) { @@ -1421,6 +1503,7 @@ Console.WriteLine("CreateGeom:"); { //Console.WriteLine(" CreateGeom 4"); SetGeom(d.CreateBox(m_targetSpace, _size.X, _size.Y, _size.Z)); + m_expectedCollisionContacts = 4; } catch (AccessViolationException) { @@ -1446,11 +1529,13 @@ Console.WriteLine("CreateGeom:"); _parent_scene.geom_name_map.Remove(prim_geom); _parent_scene.actor_name_map.Remove(prim_geom); d.GeomDestroy(prim_geom); + m_expectedCollisionContacts = 0; prim_geom = IntPtr.Zero; } catch (System.AccessViolationException) { prim_geom = IntPtr.Zero; + m_expectedCollisionContacts = 0; m_log.ErrorFormat("[PHYSICS]: PrimGeom dead for {0}", Name); return false; @@ -1489,6 +1574,10 @@ Console.WriteLine("CreateGeom:"); mesh = _parent_scene.mesher.CreateMesh(Name, _pbs, _size, _parent_scene.meshSculptLOD, IsPhysical); // createmesh returns null when it's a shape that isn't a cube. // m_log.Debug(m_localID); + if (mesh == null) + CheckMeshAsset(); + else + m_assetFailed = false; } #if SPAM @@ -1988,7 +2077,14 @@ Console.WriteLine(" JointCreateFixed"); // Don't need to re-enable body.. it's done in SetMesh if (_parent_scene.needsMeshing(_pbs)) + { mesh = _parent_scene.mesher.CreateMesh(Name, _pbs, _size, meshlod, IsPhysical); + if (mesh == null) + CheckMeshAsset(); + else + m_assetFailed = false; + } + } CreateGeom(m_targetSpace, mesh); @@ -2040,14 +2136,19 @@ Console.WriteLine(" JointCreateFixed"); m_collisionFlags &= ~CollisionCategories.Water; } - d.GeomSetCollideBits(prim_geom, (int)m_collisionFlags); - } + if (m_assetFailed) + d.GeomSetCollideBits(prim_geom, BadAssetColideBits()); + else + d.GeomSetCollideBits(prim_geom, (int)m_collisionFlags); + } /// /// Change prim in response to a shape taint. /// private void changeshape() { + m_taintshape = false; + // Cleanup of old prim geometry and Bodies if (IsPhysical && Body != IntPtr.Zero) { @@ -2075,6 +2176,7 @@ Console.WriteLine(" JointCreateFixed"); IMesh mesh = null; + if (_parent_scene.needsMeshing(_pbs)) { // Don't need to re-enable body.. it's done in CreateMesh @@ -2085,6 +2187,10 @@ Console.WriteLine(" JointCreateFixed"); // createmesh returns null when it doesn't mesh. mesh = _parent_scene.mesher.CreateMesh(Name, _pbs, _size, meshlod, IsPhysical); + if (mesh == null) + CheckMeshAsset(); + else + m_assetFailed = false; } CreateGeom(m_targetSpace, mesh); @@ -2121,7 +2227,7 @@ Console.WriteLine(" JointCreateFixed"); } resetCollisionAccounting(); - m_taintshape = false; +// m_taintshape = false; } /// @@ -2387,6 +2493,7 @@ Console.WriteLine(" JointCreateFixed"); set { _pbs = value; + m_assetFailed = false; m_taintshape = true; } } @@ -2395,15 +2502,15 @@ Console.WriteLine(" JointCreateFixed"); { get { - // Averate previous velocity with the new one so + // Average previous velocity with the new one so // client object interpolation works a 'little' better if (_zeroFlag) return Vector3.Zero; Vector3 returnVelocity = Vector3.Zero; - returnVelocity.X = (m_lastVelocity.X + _velocity.X)/2; - returnVelocity.Y = (m_lastVelocity.Y + _velocity.Y)/2; - returnVelocity.Z = (m_lastVelocity.Z + _velocity.Z)/2; + returnVelocity.X = (m_lastVelocity.X + _velocity.X) * 0.5f; // 0.5f is mathematically equiv to '/ 2' + returnVelocity.Y = (m_lastVelocity.Y + _velocity.Y) * 0.5f; + returnVelocity.Z = (m_lastVelocity.Z + _velocity.Z) * 0.5f; return returnVelocity; } set @@ -2600,6 +2707,7 @@ Console.WriteLine(" JointCreateFixed"); { Vector3 pv = Vector3.Zero; bool lastZeroFlag = _zeroFlag; + float m_minvelocity = 0; if (Body != (IntPtr)0) // FIXME -> or if it is a joint { d.Vector3 vec = d.BodyGetPosition(Body); @@ -2752,8 +2860,21 @@ Console.WriteLine(" JointCreateFixed"); _acceleration = ((_velocity - m_lastVelocity) / 0.1f); _acceleration = new Vector3(_velocity.X - m_lastVelocity.X / 0.1f, _velocity.Y - m_lastVelocity.Y / 0.1f, _velocity.Z - m_lastVelocity.Z / 0.1f); //m_log.Info("[PHYSICS]: V1: " + _velocity + " V2: " + m_lastVelocity + " Acceleration: " + _acceleration.ToString()); + + // Note here that linearvelocity is affecting angular velocity... so I'm guessing this is a vehicle specific thing... + // it does make sense to do this for tiny little instabilities with physical prim, however 0.5m/frame is fairly large. + // reducing this to 0.02m/frame seems to help the angular rubberbanding quite a bit, however, to make sure it doesn't affect elevators and vehicles + // adding these logical exclusion situations to maintain this where I think it was intended to be. + if (m_throttleUpdates || m_usePID || (m_vehicle != null && m_vehicle.Type != Vehicle.TYPE_NONE) || (Amotor != IntPtr.Zero)) + { + m_minvelocity = 0.5f; + } + else + { + m_minvelocity = 0.02f; + } - if (_velocity.ApproxEquals(pv, 0.5f)) + if (_velocity.ApproxEquals(pv, m_minvelocity)) { m_rotationalVelocity = pv; } @@ -3211,5 +3332,37 @@ Console.WriteLine(" JointCreateFixed"); { m_material = pMaterial; } + + + private void CheckMeshAsset() + { + if (_pbs.SculptEntry && !m_assetFailed && _pbs.SculptTexture != UUID.Zero) + { + m_assetFailed = true; + Util.FireAndForget(delegate + { + RequestAssetDelegate assetProvider = _parent_scene.RequestAssetMethod; + if (assetProvider != null) + assetProvider(_pbs.SculptTexture, MeshAssetReveived); + }); + } + } + + void MeshAssetReveived(AssetBase asset) + { + if (asset.Data != null && asset.Data.Length > 0) + { + if (!_pbs.SculptEntry) + return; + if (_pbs.SculptTexture.ToString() != asset.ID) + return; + + _pbs.SculptData = new byte[asset.Data.Length]; + asset.Data.CopyTo(_pbs.SculptData, 0); +// m_assetFailed = false; + m_taintshape = true; + _parent_scene.AddPhysicsActorTaint(this); + } + } } } \ No newline at end of file diff --git a/OpenSim/Region/Physics/OdePlugin/OdeScene.cs b/OpenSim/Region/Physics/OdePlugin/OdeScene.cs index 929b019ecf..d53bd90b61 100644 --- a/OpenSim/Region/Physics/OdePlugin/OdeScene.cs +++ b/OpenSim/Region/Physics/OdePlugin/OdeScene.cs @@ -336,6 +336,7 @@ namespace OpenSim.Region.Physics.OdePlugin public int geomContactPointsStartthrottle = 3; public int geomUpdatesPerThrottledUpdate = 15; + private const int avatarExpectedContacts = 3; public float bodyPIDD = 35f; public float bodyPIDG = 25; @@ -474,6 +475,8 @@ namespace OpenSim.Region.Physics.OdePlugin private OdePrim cp1; private OdeCharacter cc2; private OdePrim cp2; + private int p1ExpectedPoints = 0; + private int p2ExpectedPoints = 0; //private int cStartStop = 0; //private string cDictKey = ""; @@ -498,6 +501,9 @@ namespace OpenSim.Region.Physics.OdePlugin public int physics_logging_interval = 0; public bool physics_logging_append_existing_logfile = false; + private bool avplanted = false; + private bool av_av_collisions_off = false; + public d.Vector3 xyz = new d.Vector3(128.1640f, 128.3079f, 25.7600f); public d.Vector3 hpr = new d.Vector3(125.5000f, -17.0000f, 0.0000f); @@ -640,11 +646,14 @@ namespace OpenSim.Region.Physics.OdePlugin avMovementDivisorWalk = physicsconfig.GetFloat("av_movement_divisor_walk", 1.3f); avMovementDivisorRun = physicsconfig.GetFloat("av_movement_divisor_run", 0.8f); avCapRadius = physicsconfig.GetFloat("av_capsule_radius", 0.37f); + avplanted = physicsconfig.GetBoolean("av_planted", false); + av_av_collisions_off = physicsconfig.GetBoolean("av_av_collisions_off", false); + IsAvCapsuleTilted = physicsconfig.GetBoolean("av_capsule_tilted", false); contactsPerCollision = physicsconfig.GetInt("contacts_per_collision", 80); - geomContactPointsStartthrottle = physicsconfig.GetInt("geom_contactpoints_start_throttling", 3); + geomContactPointsStartthrottle = physicsconfig.GetInt("geom_contactpoints_start_throttling", 5); geomUpdatesPerThrottledUpdate = physicsconfig.GetInt("geom_updates_before_throttled_update", 15); geomCrossingFailuresBeforeOutofbounds = physicsconfig.GetInt("geom_crossing_failures_before_outofbounds", 5); @@ -659,6 +668,8 @@ namespace OpenSim.Region.Physics.OdePlugin meshSculptLOD = physicsconfig.GetFloat("mesh_lod", 32f); MeshSculptphysicalLOD = physicsconfig.GetFloat("mesh_physical_lod", 16f); m_filterCollisions = physicsconfig.GetBoolean("filter_collisions", false); + + if (Environment.OSVersion.Platform == PlatformID.Unix) { @@ -1064,7 +1075,10 @@ namespace OpenSim.Region.Physics.OdePlugin PhysicsActor p1; PhysicsActor p2; - + + p1ExpectedPoints = 0; + p2ExpectedPoints = 0; + if (!actor_name_map.TryGetValue(g1, out p1)) { p1 = PANull; @@ -1121,9 +1135,13 @@ namespace OpenSim.Region.Physics.OdePlugin switch (p1.PhysicsActorType) { case (int)ActorTypes.Agent: + p1ExpectedPoints = avatarExpectedContacts; p2.CollidingObj = true; break; case (int)ActorTypes.Prim: + if (p1 != null && p1 is OdePrim) + p1ExpectedPoints = ((OdePrim) p1).ExpectedCollisionContacts; + if (p2.Velocity.LengthSquared() > 0.0f) p2.CollidingObj = true; break; @@ -1298,6 +1316,10 @@ namespace OpenSim.Region.Physics.OdePlugin if ((p1 is OdePrim) && (((OdePrim)p1).m_isVolumeDetect)) skipThisContact = true; // No collision on volume detect prims + if (av_av_collisions_off) + if ((p1 is OdeCharacter) && (p2 is OdeCharacter)) + skipThisContact = true; + if (!skipThisContact && (p2 is OdePrim) && (((OdePrim)p2).m_isVolumeDetect)) skipThisContact = true; // No collision on volume detect prims @@ -1319,6 +1341,7 @@ namespace OpenSim.Region.Physics.OdePlugin if ((p2.PhysicsActorType == (int) ActorTypes.Agent) && (Math.Abs(p2.Velocity.X) > 0.01f || Math.Abs(p2.Velocity.Y) > 0.01f)) { + p2ExpectedPoints = avatarExpectedContacts; // Avatar is moving on terrain, use the movement terrain contact AvatarMovementTerrainContact.geom = curContact; @@ -1332,6 +1355,7 @@ namespace OpenSim.Region.Physics.OdePlugin { if (p2.PhysicsActorType == (int)ActorTypes.Agent) { + p2ExpectedPoints = avatarExpectedContacts; // Avatar is standing on terrain, use the non moving terrain contact TerrainContact.geom = curContact; @@ -1356,9 +1380,18 @@ namespace OpenSim.Region.Physics.OdePlugin } if (p2 is OdePrim) - material = ((OdePrim)p2).m_material; - + { + material = ((OdePrim) p2).m_material; + p2ExpectedPoints = ((OdePrim)p2).ExpectedCollisionContacts; + } + + // Unnessesary because p1 is defined above + //if (p1 is OdePrim) + // { + // p1ExpectedPoints = ((OdePrim)p1).ExpectedCollisionContacts; + // } //m_log.DebugFormat("Material: {0}", material); + m_materialContacts[material, movintYN].geom = curContact; if (m_global_contactcount < maxContactsbeforedeath) @@ -1379,7 +1412,10 @@ namespace OpenSim.Region.Physics.OdePlugin int material = (int)Material.Wood; if (p2 is OdePrim) + { material = ((OdePrim)p2).m_material; + p2ExpectedPoints = ((OdePrim)p2).ExpectedCollisionContacts; + } //m_log.DebugFormat("Material: {0}", material); m_materialContacts[material, movintYN].geom = curContact; @@ -1429,6 +1465,7 @@ namespace OpenSim.Region.Physics.OdePlugin { if ((p2.PhysicsActorType == (int)ActorTypes.Agent)) { + p2ExpectedPoints = avatarExpectedContacts; if ((Math.Abs(p2.Velocity.X) > 0.01f || Math.Abs(p2.Velocity.Y) > 0.01f)) { // Avatar is moving on a prim, use the Movement prim contact @@ -1458,7 +1495,10 @@ namespace OpenSim.Region.Physics.OdePlugin int material = (int)Material.Wood; if (p2 is OdePrim) + { material = ((OdePrim)p2).m_material; + p2ExpectedPoints = ((OdePrim)p2).ExpectedCollisionContacts; + } //m_log.DebugFormat("Material: {0}", material); m_materialContacts[material, 0].geom = curContact; @@ -1479,8 +1519,8 @@ namespace OpenSim.Region.Physics.OdePlugin } collision_accounting_events(p1, p2, maxDepthContact); - - if (count > geomContactPointsStartthrottle) + + if (count > ((p1ExpectedPoints + p2ExpectedPoints) * 0.25) + (geomContactPointsStartthrottle)) { // If there are more then 3 contact points, it's likely // that we've got a pile of objects, so ... @@ -1943,7 +1983,8 @@ namespace OpenSim.Region.Physics.OdePlugin newAv.Flying = isFlying; newAv.MinimumGroundFlightOffset = minimumGroundFlightOffset; - + newAv.m_avatarplanted = avplanted; + return newAv; } @@ -1958,6 +1999,7 @@ namespace OpenSim.Region.Physics.OdePlugin internal void AddCharacter(OdeCharacter chr) { + chr.m_avatarplanted = avplanted; if (!_characters.Contains(chr)) { _characters.Add(chr); @@ -4278,4 +4320,4 @@ namespace OpenSim.Region.Physics.OdePlugin m_stats[ODEPrimUpdateFrameMsStatName] = 0; } } -} \ No newline at end of file +} diff --git a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs index 52d96bc5c6..cf801baecf 100644 --- a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs +++ b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs @@ -56,6 +56,7 @@ using GridRegion = OpenSim.Services.Interfaces.GridRegion; using PresenceInfo = OpenSim.Services.Interfaces.PresenceInfo; using PrimType = OpenSim.Region.Framework.Scenes.PrimType; using AssetLandmark = OpenSim.Framework.AssetLandmark; +using RegionFlags = OpenSim.Framework.RegionFlags; using LSL_Float = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLFloat; using LSL_Integer = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLInteger; @@ -3772,6 +3773,16 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api } /// + /// Returns the name of the child prim or seated avatar matching the + /// specified link number. + /// + /// + /// The number of a link in the linkset or a link-related constant. + /// + /// + /// The name determined to match the specified link number. + /// + /// /// The rules governing the returned name are not simple. The only /// time a blank name is returned is if the target prim has a blank /// name. If no prim with the given link number can be found then @@ -3799,10 +3810,14 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api /// Mentions NULL_KEY being returned /// http://wiki.secondlife.com/wiki/LlGetLinkName /// Mentions using the LINK_* constants, some of which are negative - /// + /// public LSL_String llGetLinkName(int linknum) { m_host.AddScriptLPS(1); + // simplest case, this prims link number + if (linknum == m_host.LinkNum || linknum == ScriptBaseClass.LINK_THIS) + return m_host.Name; + // parse for sitting avatare-names List nametable = new List(); World.ForEachRootScenePresence(delegate(ScenePresence presence) @@ -3826,10 +3841,6 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api return nametable[totalprims - linknum]; } - // simplest case, this prims link number - if (m_host.LinkNum == linknum) - return m_host.Name; - // Single prim if (m_host.LinkNum == 0) { @@ -3978,7 +3989,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api World.RegionInfo.RegionName+" "+ m_host.AbsolutePosition.ToString(), agentItem.ID, true, m_host.AbsolutePosition, - bucket); + bucket, true); // TODO: May actually send no timestamp m_TransferModule.SendInstantMessage(msg, delegate(bool success) {}); } @@ -4006,7 +4017,18 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api { m_host.AddScriptLPS(1); Vector3 av3 = Util.Clip(color, 0.0f, 1.0f); - m_host.SetText(text.Length > 254 ? text.Remove(254) : text, av3, Util.Clip((float)alpha, 0.0f, 1.0f)); + if (text.Length > 254) + text = text.Remove(254); + + byte[] data; + do + { + data = Util.UTF8.GetBytes(text); + if (data.Length > 254) + text = text.Substring(0, text.Length - 1); + } while (data.Length > 254); + + m_host.SetText(text, av3, Util.Clip((float)alpha, 0.0f, 1.0f)); //m_host.ParentGroup.HasGroupChanged = true; //m_host.ParentGroup.ScheduleGroupForFullUpdate(); } @@ -6441,16 +6463,17 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api if (m_TransferModule != null) { byte[] bucket = new byte[] { (byte)AssetType.Folder }; - + + Vector3 pos = m_host.AbsolutePosition; + GridInstantMessage msg = new GridInstantMessage(World, - m_host.UUID, m_host.Name + ", an object owned by " + - resolveName(m_host.OwnerID) + ",", destID, + m_host.OwnerID, m_host.Name, destID, (byte)InstantMessageDialog.TaskInventoryOffered, - false, category + "\n" + m_host.Name + " is located at " + - World.RegionInfo.RegionName + " " + - m_host.AbsolutePosition.ToString(), - folderID, true, m_host.AbsolutePosition, - bucket); + false, string.Format("'{0}'", category), +// We won't go so far as to add a SLURL, but this is the format used by LL as of 2012-10-06 +// false, string.Format("'{0}' ( http://slurl.com/secondlife/{1}/{2}/{3}/{4} )", category, World.Name, (int)pos.X, (int)pos.Y, (int)pos.Z), + folderID, false, pos, + bucket, false); m_TransferModule.SendInstantMessage(msg, delegate(bool success) {}); } @@ -9301,11 +9324,10 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api GridRegion info; - if (m_ScriptEngine.World.RegionInfo.RegionName == simulator) //Det data for this simulator? - - info = new GridRegion(m_ScriptEngine.World.RegionInfo); + if (World.RegionInfo.RegionName == simulator) + info = new GridRegion(World.RegionInfo); else - info = m_ScriptEngine.World.GridService.GetRegionByName(m_ScriptEngine.World.RegionInfo.ScopeID, simulator); + info = World.GridService.GetRegionByName(m_ScriptEngine.World.RegionInfo.ScopeID, simulator); switch (data) { @@ -9315,9 +9337,24 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api ScriptSleep(1000); return UUID.Zero.ToString(); } - if (m_ScriptEngine.World.RegionInfo.RegionName != simulator) + + bool isHypergridRegion = false; + + if (World.RegionInfo.RegionName != simulator && info.RegionSecret != "") + { + // Hypergrid is currently placing real destination region co-ords into RegionSecret. + // But other code can also use this field for a genuine RegionSecret! Therefore, if + // anything is present we need to disambiguate. + // + // FIXME: Hypergrid should be storing this data in a different field. + RegionFlags regionFlags + = (RegionFlags)m_ScriptEngine.World.GridService.GetRegionFlags( + info.ScopeID, info.RegionID); + isHypergridRegion = (regionFlags & RegionFlags.Hyperlink) != 0; + } + + if (isHypergridRegion) { - //Hypergrid Region co-ordinates uint rx = 0, ry = 0; Utils.LongToUInts(Convert.ToUInt64(info.RegionSecret), out rx, out ry); @@ -9328,7 +9365,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api } else { - //Local-cooridnates + // Local grid co-oridnates reply = new LSL_Vector( info.RegionLocX, info.RegionLocY, diff --git a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/OSSL_Api.cs b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/OSSL_Api.cs index 8b73cd9ae0..29bc163a19 100644 --- a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/OSSL_Api.cs +++ b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/OSSL_Api.cs @@ -3538,17 +3538,103 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api return new LSL_Key(m_host.ParentGroup.FromPartID.ToString()); } - + /// /// Sets the response type for an HTTP request/response /// /// public void osSetContentType(LSL_Key id, string type) { - CheckThreatLevel(ThreatLevel.High,"osSetResponseType"); + CheckThreatLevel(ThreatLevel.High, "osSetContentType"); + if (m_UrlModule != null) m_UrlModule.HttpContentType(new UUID(id),type); } - - } + + /// Shout an error if the object owner did not grant the script the specified permissions. + /// + /// + /// boolean indicating whether an error was shouted. + protected bool ShoutErrorOnLackingOwnerPerms(int perms, string errorPrefix) + { + m_host.AddScriptLPS(1); + bool fail = false; + if (m_item.PermsGranter != m_host.OwnerID) + { + fail = true; + OSSLShoutError(string.Format("{0}. Permissions not granted to owner.", errorPrefix)); + } + else if ((m_item.PermsMask & perms) == 0) + { + fail = true; + OSSLShoutError(string.Format("{0}. Permissions not granted.", errorPrefix)); + } + + return fail; + } + + protected void DropAttachment(bool checkPerms) + { + if (checkPerms && ShoutErrorOnLackingOwnerPerms(ScriptBaseClass.PERMISSION_ATTACH, "Cannot drop attachment")) + { + return; + } + + IAttachmentsModule attachmentsModule = m_ScriptEngine.World.AttachmentsModule; + ScenePresence sp = attachmentsModule == null ? null : m_host.ParentGroup.Scene.GetScenePresence(m_host.ParentGroup.OwnerID); + + if (attachmentsModule != null && sp != null) + { + attachmentsModule.DetachSingleAttachmentToGround(sp, m_host.ParentGroup.LocalId); + } + } + + protected void DropAttachmentAt(bool checkPerms, LSL_Vector pos, LSL_Rotation rot) + { + if (checkPerms && ShoutErrorOnLackingOwnerPerms(ScriptBaseClass.PERMISSION_ATTACH, "Cannot drop attachment")) + { + return; + } + + IAttachmentsModule attachmentsModule = m_ScriptEngine.World.AttachmentsModule; + ScenePresence sp = attachmentsModule == null ? null : m_host.ParentGroup.Scene.GetScenePresence(m_host.ParentGroup.OwnerID); + + if (attachmentsModule != null && sp != null) + { + attachmentsModule.DetachSingleAttachmentToGround(sp, m_host.ParentGroup.LocalId, pos, rot); + } + } + + public void osDropAttachment() + { + CheckThreatLevel(ThreatLevel.Moderate, "osDropAttachment"); + m_host.AddScriptLPS(1); + + DropAttachment(true); + } + + public void osForceDropAttachment() + { + CheckThreatLevel(ThreatLevel.High, "osForceDropAttachment"); + m_host.AddScriptLPS(1); + + DropAttachment(false); + } + + public void osDropAttachmentAt(LSL_Vector pos, LSL_Rotation rot) + { + CheckThreatLevel(ThreatLevel.Moderate, "osDropAttachmentAt"); + m_host.AddScriptLPS(1); + + DropAttachmentAt(true, pos, rot); + } + + public void osForceDropAttachmentAt(LSL_Vector pos, LSL_Rotation rot) + { + CheckThreatLevel(ThreatLevel.High, "osForceDropAttachmentAt"); + m_host.AddScriptLPS(1); + + DropAttachmentAt(false, pos, rot); + } + } } \ No newline at end of file diff --git a/OpenSim/Region/ScriptEngine/Shared/Api/Interface/IOSSL_Api.cs b/OpenSim/Region/ScriptEngine/Shared/Api/Interface/IOSSL_Api.cs index 3985e6629c..93188c9ec8 100644 --- a/OpenSim/Region/ScriptEngine/Shared/Api/Interface/IOSSL_Api.cs +++ b/OpenSim/Region/ScriptEngine/Shared/Api/Interface/IOSSL_Api.cs @@ -394,5 +394,29 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api.Interfaces /// /// void osSetContentType(LSL_Key id, string type); + + /// + /// Attempts to drop an attachment to the ground + /// + void osDropAttachment(); + + /// + /// Attempts to drop an attachment to the ground while bypassing the script permissions + /// + void osForceDropAttachment(); + + /// + /// Attempts to drop an attachment at the specified coordinates. + /// + /// + /// + void osDropAttachmentAt(vector pos, rotation rot); + + /// + /// Attempts to drop an attachment at the specified coordinates while bypassing the script permissions + /// + /// + /// + void osForceDropAttachmentAt(vector pos, rotation rot); } } diff --git a/OpenSim/Region/ScriptEngine/Shared/Api/Runtime/OSSL_Stub.cs b/OpenSim/Region/ScriptEngine/Shared/Api/Runtime/OSSL_Stub.cs index 52ca3da154..dee1b28cca 100644 --- a/OpenSim/Region/ScriptEngine/Shared/Api/Runtime/OSSL_Stub.cs +++ b/OpenSim/Region/ScriptEngine/Shared/Api/Runtime/OSSL_Stub.cs @@ -972,5 +972,25 @@ namespace OpenSim.Region.ScriptEngine.Shared.ScriptBase { m_OSSL_Functions.osSetContentType(id,type); } + + public void osDropAttachment() + { + m_OSSL_Functions.osDropAttachment(); + } + + public void osForceDropAttachment() + { + m_OSSL_Functions.osForceDropAttachment(); + } + + public void osDropAttachmentAt(vector pos, rotation rot) + { + m_OSSL_Functions.osDropAttachmentAt(pos, rot); + } + + public void osForceDropAttachmentAt(vector pos, rotation rot) + { + m_OSSL_Functions.osForceDropAttachmentAt(pos, rot); + } } } diff --git a/OpenSim/Region/ScriptEngine/XEngine/EventManager.cs b/OpenSim/Region/ScriptEngine/XEngine/EventManager.cs index cee10df54a..9405075e51 100644 --- a/OpenSim/Region/ScriptEngine/XEngine/EventManager.cs +++ b/OpenSim/Region/ScriptEngine/XEngine/EventManager.cs @@ -96,9 +96,12 @@ namespace OpenSim.Region.ScriptEngine.XEngine if (part == null) return; + if ((part.ScriptEvents & scriptEvents.money) == 0) + part = part.ParentGroup.RootPart; + m_log.Debug("Paid: " + objectID + " from " + agentID + ", amount " + amount); - part = part.ParentGroup.RootPart; +// part = part.ParentGroup.RootPart; money(part.LocalId, agentID, amount); } diff --git a/OpenSim/Region/UserStatistics/WebStatsModule.cs b/OpenSim/Region/UserStatistics/WebStatsModule.cs index c11ea0225a..625eba4b0a 100644 --- a/OpenSim/Region/UserStatistics/WebStatsModule.cs +++ b/OpenSim/Region/UserStatistics/WebStatsModule.cs @@ -61,7 +61,7 @@ namespace OpenSim.Region.UserStatistics /// /// User statistics sessions keyed by agent ID /// - private Dictionary m_sessions = new Dictionary(); + private Dictionary m_sessions = new Dictionary(); private List m_scenes = new List(); private Dictionary reports = new Dictionary(); @@ -319,14 +319,18 @@ namespace OpenSim.Region.UserStatistics private void OnMakeRootAgent(ScenePresence agent) { +// m_log.DebugFormat( +// "[WEB STATS MODULE]: Looking for session {0} for {1} in {2}", +// agent.ControllingClient.SessionId, agent.Name, agent.Scene.Name); + lock (m_sessions) { - UserSessionID uid; + UserSession uid; if (!m_sessions.ContainsKey(agent.UUID)) { UserSessionData usd = UserSessionUtil.newUserSessionData(); - uid = new UserSessionID(); + uid = new UserSession(); uid.name_f = agent.Firstname; uid.name_l = agent.Lastname; uid.session_data = usd; @@ -411,9 +415,9 @@ namespace OpenSim.Region.UserStatistics return String.Empty; } - private UserSessionID ParseViewerStats(string request, UUID agentID) + private UserSession ParseViewerStats(string request, UUID agentID) { - UserSessionID uid = new UserSessionID(); + UserSession uid = new UserSession(); UserSessionData usd; OSD message = OSDParser.DeserializeLLSDXml(request); OSDMap mmap; @@ -425,22 +429,25 @@ namespace OpenSim.Region.UserStatistics if (!m_sessions.ContainsKey(agentID)) { m_log.WarnFormat("[WEB STATS MODULE]: no session for stat disclosure for agent {0}", agentID); - return new UserSessionID(); + return new UserSession(); } + uid = m_sessions[agentID]; + +// m_log.DebugFormat("[WEB STATS MODULE]: Got session {0} for {1}", uid.session_id, agentID); } else { // parse through the beginning to locate the session if (message.Type != OSDType.Map) - return new UserSessionID(); + return new UserSession(); mmap = (OSDMap)message; { UUID sessionID = mmap["session_id"].AsUUID(); if (sessionID == UUID.Zero) - return new UserSessionID(); + return new UserSession(); // search through each session looking for the owner @@ -459,7 +466,7 @@ namespace OpenSim.Region.UserStatistics // can't find a session if (agentID == UUID.Zero) { - return new UserSessionID(); + return new UserSession(); } } } @@ -468,12 +475,12 @@ namespace OpenSim.Region.UserStatistics usd = uid.session_data; if (message.Type != OSDType.Map) - return new UserSessionID(); + return new UserSession(); mmap = (OSDMap)message; { if (mmap["agent"].Type != OSDType.Map) - return new UserSessionID(); + return new UserSession(); OSDMap agent_map = (OSDMap)mmap["agent"]; usd.agent_id = agentID; usd.name_f = uid.name_f; @@ -493,17 +500,18 @@ namespace OpenSim.Region.UserStatistics (float)agent_map["fps"].AsReal()); if (mmap["downloads"].Type != OSDType.Map) - return new UserSessionID(); + return new UserSession(); OSDMap downloads_map = (OSDMap)mmap["downloads"]; usd.d_object_kb = (float)downloads_map["object_kbytes"].AsReal(); usd.d_texture_kb = (float)downloads_map["texture_kbytes"].AsReal(); usd.d_world_kb = (float)downloads_map["workd_kbytes"].AsReal(); +// m_log.DebugFormat("[WEB STATS MODULE]: mmap[\"session_id\"] = [{0}]", mmap["session_id"].AsUUID()); usd.session_id = mmap["session_id"].AsUUID(); if (mmap["system"].Type != OSDType.Map) - return new UserSessionID(); + return new UserSession(); OSDMap system_map = (OSDMap)mmap["system"]; usd.s_cpu = system_map["cpu"].AsString(); @@ -512,13 +520,13 @@ namespace OpenSim.Region.UserStatistics usd.s_ram = system_map["ram"].AsInteger(); if (mmap["stats"].Type != OSDType.Map) - return new UserSessionID(); + return new UserSession(); OSDMap stats_map = (OSDMap)mmap["stats"]; { if (stats_map["failures"].Type != OSDType.Map) - return new UserSessionID(); + return new UserSession(); OSDMap stats_failures = (OSDMap)stats_map["failures"]; usd.f_dropped = stats_failures["dropped"].AsInteger(); usd.f_failed_resends = stats_failures["failed_resends"].AsInteger(); @@ -527,18 +535,18 @@ namespace OpenSim.Region.UserStatistics usd.f_send_packet = stats_failures["send_packet"].AsInteger(); if (stats_map["net"].Type != OSDType.Map) - return new UserSessionID(); + return new UserSession(); OSDMap stats_net = (OSDMap)stats_map["net"]; { if (stats_net["in"].Type != OSDType.Map) - return new UserSessionID(); + return new UserSession(); OSDMap net_in = (OSDMap)stats_net["in"]; usd.n_in_kb = (float)net_in["kbytes"].AsReal(); usd.n_in_pk = net_in["packets"].AsInteger(); if (stats_net["out"].Type != OSDType.Map) - return new UserSessionID(); + return new UserSession(); OSDMap net_out = (OSDMap)stats_net["out"]; usd.n_out_kb = (float)net_out["kbytes"].AsReal(); @@ -549,11 +557,18 @@ namespace OpenSim.Region.UserStatistics uid.session_data = usd; m_sessions[agentID] = uid; + +// m_log.DebugFormat( +// "[WEB STATS MODULE]: Parse data for {0} {1}, session {2}", uid.name_f, uid.name_l, uid.session_id); + return uid; } - private void UpdateUserStats(UserSessionID uid, SqliteConnection db) + private void UpdateUserStats(UserSession uid, SqliteConnection db) { +// m_log.DebugFormat( +// "[WEB STATS MODULE]: Updating user stats for {0} {1}, session {2}", uid.name_f, uid.name_l, uid.session_id); + if (uid.session_id == UUID.Zero) return; @@ -740,7 +755,6 @@ VALUES s.min_ping = ArrayMin_f(__ping); s.max_ping = ArrayMax_f(__ping); s.mode_ping = ArrayMode_f(__ping); - } #region Statistics @@ -985,7 +999,7 @@ VALUES } #region structs - public struct UserSessionID + public class UserSession { public UUID session_id; public UUID region_id; diff --git a/OpenSim/Server/ServerMain.cs b/OpenSim/Server/ServerMain.cs index 21fb6785c3..45c13fb944 100644 --- a/OpenSim/Server/ServerMain.cs +++ b/OpenSim/Server/ServerMain.cs @@ -60,7 +60,25 @@ namespace OpenSim.Server } string connList = serverConfig.GetString("ServiceConnectors", String.Empty); - string[] conns = connList.Split(new char[] {',', ' '}); + + IConfig servicesConfig = m_Server.Config.Configs["ServiceList"]; + if (servicesConfig != null) + { + List servicesList = new List(); + if (connList != String.Empty) + servicesList.Add(connList); + + foreach (string k in servicesConfig.GetKeys()) + { + string v = servicesConfig.GetString(k); + if (v != String.Empty) + servicesList.Add(v); + } + + connList = String.Join(",", servicesList.ToArray()); + } + + string[] conns = connList.Split(new char[] {',', ' ', '\n', '\r', '\t'}); // int i = 0; foreach (string c in conns) @@ -130,4 +148,4 @@ namespace OpenSim.Server return 0; } } -} \ No newline at end of file +} diff --git a/OpenSim/Services/Connectors/Asset/AssetServicesConnector.cs b/OpenSim/Services/Connectors/Asset/AssetServicesConnector.cs index 086b5adc90..2b2f11f94b 100644 --- a/OpenSim/Services/Connectors/Asset/AssetServicesConnector.cs +++ b/OpenSim/Services/Connectors/Asset/AssetServicesConnector.cs @@ -47,7 +47,8 @@ namespace OpenSim.Services.Connectors private string m_ServerURI = String.Empty; private IImprovedAssetCache m_Cache = null; - + private int m_maxAssetRequestConcurrency = 30; + private delegate void AssetRetrievedEx(AssetBase asset); // Keeps track of concurrent requests for the same asset, so that it's only loaded once. @@ -71,6 +72,10 @@ namespace OpenSim.Services.Connectors public virtual void Initialise(IConfigSource source) { + IConfig netconfig = source.Configs["Network"]; + if (netconfig != null) + m_maxAssetRequestConcurrency = netconfig.GetInt("MaxRequestConcurrency",m_maxAssetRequestConcurrency); + IConfig assetConfig = source.Configs["AssetService"]; if (assetConfig == null) { @@ -108,7 +113,7 @@ namespace OpenSim.Services.Connectors if (asset == null) { asset = SynchronousRestObjectRequester. - MakeRequest("GET", uri, 0, 30); + MakeRequest("GET", uri, 0, m_maxAssetRequestConcurrency); if (m_Cache != null) m_Cache.Cache(asset); @@ -221,7 +226,7 @@ namespace OpenSim.Services.Connectors m_AssetHandlers.Remove(id); } handlers.Invoke(a); - }, 30); + }, m_maxAssetRequestConcurrency); success = true; } diff --git a/OpenSim/Services/Connectors/Friends/FriendsSimConnector.cs b/OpenSim/Services/Connectors/Friends/FriendsSimConnector.cs index 3fd0c53633..e235733734 100644 --- a/OpenSim/Services/Connectors/Friends/FriendsSimConnector.cs +++ b/OpenSim/Services/Connectors/Friends/FriendsSimConnector.cs @@ -153,7 +153,7 @@ namespace OpenSim.Services.Connectors.Friends if (!region.ServerURI.EndsWith("/")) path = "/" + path; string uri = region.ServerURI + path; - m_log.DebugFormat("[FRIENDS SIM CONNECTOR]: calling {0}", uri); +// m_log.DebugFormat("[FRIENDS SIM CONNECTOR]: calling {0}", uri); try { diff --git a/OpenSim/Services/Connectors/GridUser/GridUserServicesConnector.cs b/OpenSim/Services/Connectors/GridUser/GridUserServicesConnector.cs index 20d7eaf301..94bda82d14 100644 --- a/OpenSim/Services/Connectors/GridUser/GridUserServicesConnector.cs +++ b/OpenSim/Services/Connectors/GridUser/GridUserServicesConnector.cs @@ -207,7 +207,7 @@ namespace OpenSim.Services.Connectors if ((replyData != null) && replyData.ContainsKey("result") && (replyData["result"] != null)) { if (replyData["result"] is Dictionary) - guinfo = new GridUserInfo((Dictionary)replyData["result"]); + guinfo = Create((Dictionary)replyData["result"]); } return guinfo; @@ -273,7 +273,7 @@ namespace OpenSim.Services.Connectors { if (griduser is Dictionary) { - GridUserInfo pinfo = new GridUserInfo((Dictionary)griduser); + GridUserInfo pinfo = Create((Dictionary)griduser); rinfos.Add(pinfo); } else @@ -286,5 +286,10 @@ namespace OpenSim.Services.Connectors return rinfos.ToArray(); } + + protected virtual GridUserInfo Create(Dictionary griduser) + { + return new GridUserInfo(griduser); + } } } diff --git a/OpenSim/Services/Connectors/SimianGrid/SimianGridServiceConnector.cs b/OpenSim/Services/Connectors/SimianGrid/SimianGridServiceConnector.cs index 35cb408a91..038a4bf2b6 100644 --- a/OpenSim/Services/Connectors/SimianGrid/SimianGridServiceConnector.cs +++ b/OpenSim/Services/Connectors/SimianGrid/SimianGridServiceConnector.cs @@ -384,8 +384,8 @@ namespace OpenSim.Services.Connectors.SimianGrid if (response["Success"].AsBoolean()) { OSDMap extraData = response["ExtraData"] as OSDMap; - int enabled = response["Enabled"].AsBoolean() ? (int) OpenSim.Data.RegionFlags.RegionOnline : 0; - int hypergrid = extraData["HyperGrid"].AsBoolean() ? (int) OpenSim.Data.RegionFlags.Hyperlink : 0; + int enabled = response["Enabled"].AsBoolean() ? (int)OpenSim.Framework.RegionFlags.RegionOnline : 0; + int hypergrid = extraData["HyperGrid"].AsBoolean() ? (int)OpenSim.Framework.RegionFlags.Hyperlink : 0; int flags = enabled | hypergrid; m_log.DebugFormat("[SGGC] enabled - {0} hg - {1} flags - {2}", enabled, hypergrid, flags); return flags; diff --git a/OpenSim/Services/GridService/GridService.cs b/OpenSim/Services/GridService/GridService.cs index 5bdea06847..ee3b858867 100644 --- a/OpenSim/Services/GridService/GridService.cs +++ b/OpenSim/Services/GridService/GridService.cs @@ -151,11 +151,11 @@ namespace OpenSim.Services.GridService // // Get it's flags // - OpenSim.Data.RegionFlags rflags = (OpenSim.Data.RegionFlags)Convert.ToInt32(region.Data["flags"]); + OpenSim.Framework.RegionFlags rflags = (OpenSim.Framework.RegionFlags)Convert.ToInt32(region.Data["flags"]); // Is this a reservation? // - if ((rflags & OpenSim.Data.RegionFlags.Reservation) != 0) + if ((rflags & OpenSim.Framework.RegionFlags.Reservation) != 0) { // Regions reserved for the null key cannot be taken. if ((string)region.Data["PrincipalID"] == UUID.Zero.ToString()) @@ -166,10 +166,10 @@ namespace OpenSim.Services.GridService // NOTE: Fudging the flags value here, so these flags // should not be used elsewhere. Don't optimize // this with the later retrieval of the same flags! - rflags |= OpenSim.Data.RegionFlags.Authenticate; + rflags |= OpenSim.Framework.RegionFlags.Authenticate; } - if ((rflags & OpenSim.Data.RegionFlags.Authenticate) != 0) + if ((rflags & OpenSim.Framework.RegionFlags.Authenticate) != 0) { // Can we authenticate at all? // @@ -205,10 +205,10 @@ namespace OpenSim.Services.GridService if ((region != null) && (region.RegionID == regionInfos.RegionID) && ((region.posX != regionInfos.RegionLocX) || (region.posY != regionInfos.RegionLocY))) { - if ((Convert.ToInt32(region.Data["flags"]) & (int)OpenSim.Data.RegionFlags.NoMove) != 0) + if ((Convert.ToInt32(region.Data["flags"]) & (int)OpenSim.Framework.RegionFlags.NoMove) != 0) return "Can't move this region"; - if ((Convert.ToInt32(region.Data["flags"]) & (int)OpenSim.Data.RegionFlags.LockedOut) != 0) + if ((Convert.ToInt32(region.Data["flags"]) & (int)OpenSim.Framework.RegionFlags.LockedOut) != 0) return "Region locked out"; // Region reregistering in other coordinates. Delete the old entry @@ -233,7 +233,7 @@ namespace OpenSim.Services.GridService { int oldFlags = Convert.ToInt32(region.Data["flags"]); - oldFlags &= ~(int)OpenSim.Data.RegionFlags.Reservation; + oldFlags &= ~(int)OpenSim.Framework.RegionFlags.Reservation; rdata.Data["flags"] = oldFlags.ToString(); // Preserve flags } @@ -252,7 +252,7 @@ namespace OpenSim.Services.GridService } int flags = Convert.ToInt32(rdata.Data["flags"]); - flags |= (int)OpenSim.Data.RegionFlags.RegionOnline; + flags |= (int)OpenSim.Framework.RegionFlags.RegionOnline; rdata.Data["flags"] = flags.ToString(); try @@ -283,9 +283,9 @@ namespace OpenSim.Services.GridService int flags = Convert.ToInt32(region.Data["flags"]); - if (!m_DeleteOnUnregister || (flags & (int)OpenSim.Data.RegionFlags.Persistent) != 0) + if (!m_DeleteOnUnregister || (flags & (int)OpenSim.Framework.RegionFlags.Persistent) != 0) { - flags &= ~(int)OpenSim.Data.RegionFlags.RegionOnline; + flags &= ~(int)OpenSim.Framework.RegionFlags.RegionOnline; region.Data["flags"] = flags.ToString(); region.Data["last_seen"] = Util.UnixTimeSinceEpoch(); try @@ -320,7 +320,7 @@ namespace OpenSim.Services.GridService if (rdata.RegionID != regionID) { int flags = Convert.ToInt32(rdata.Data["flags"]); - if ((flags & (int)Data.RegionFlags.Hyperlink) == 0) // no hyperlinks as neighbours + if ((flags & (int)Framework.RegionFlags.Hyperlink) == 0) // no hyperlinks as neighbours rinfos.Add(RegionData2RegionInfo(rdata)); } } @@ -470,7 +470,7 @@ namespace OpenSim.Services.GridService foreach (RegionData r in regions) { - if ((Convert.ToInt32(r.Data["flags"]) & (int)OpenSim.Data.RegionFlags.RegionOnline) != 0) + if ((Convert.ToInt32(r.Data["flags"]) & (int)OpenSim.Framework.RegionFlags.RegionOnline) != 0) ret.Add(RegionData2RegionInfo(r)); } @@ -486,7 +486,7 @@ namespace OpenSim.Services.GridService foreach (RegionData r in regions) { - if ((Convert.ToInt32(r.Data["flags"]) & (int)OpenSim.Data.RegionFlags.RegionOnline) != 0) + if ((Convert.ToInt32(r.Data["flags"]) & (int)OpenSim.Framework.RegionFlags.RegionOnline) != 0) ret.Add(RegionData2RegionInfo(r)); } @@ -502,7 +502,7 @@ namespace OpenSim.Services.GridService foreach (RegionData r in regions) { - if ((Convert.ToInt32(r.Data["flags"]) & (int)OpenSim.Data.RegionFlags.RegionOnline) != 0) + if ((Convert.ToInt32(r.Data["flags"]) & (int)OpenSim.Framework.RegionFlags.RegionOnline) != 0) ret.Add(RegionData2RegionInfo(r)); } @@ -629,7 +629,7 @@ namespace OpenSim.Services.GridService private void OutputRegionToConsole(RegionData r) { - OpenSim.Data.RegionFlags flags = (OpenSim.Data.RegionFlags)Convert.ToInt32(r.Data["flags"]); + OpenSim.Framework.RegionFlags flags = (OpenSim.Framework.RegionFlags)Convert.ToInt32(r.Data["flags"]); ConsoleDisplayList dispList = new ConsoleDisplayList(); dispList.AddRow("Region Name", r.RegionName); @@ -659,7 +659,7 @@ namespace OpenSim.Services.GridService foreach (RegionData r in regions) { - OpenSim.Data.RegionFlags flags = (OpenSim.Data.RegionFlags)Convert.ToInt32(r.Data["flags"]); + OpenSim.Framework.RegionFlags flags = (OpenSim.Framework.RegionFlags)Convert.ToInt32(r.Data["flags"]); dispTable.AddRow( r.RegionName, r.RegionID.ToString(), @@ -673,7 +673,7 @@ namespace OpenSim.Services.GridService private int ParseFlags(int prev, string flags) { - OpenSim.Data.RegionFlags f = (OpenSim.Data.RegionFlags)prev; + OpenSim.Framework.RegionFlags f = (OpenSim.Framework.RegionFlags)prev; string[] parts = flags.Split(new char[] {',', ' '}, StringSplitOptions.RemoveEmptyEntries); @@ -685,18 +685,18 @@ namespace OpenSim.Services.GridService { if (p.StartsWith("+")) { - val = (int)Enum.Parse(typeof(OpenSim.Data.RegionFlags), p.Substring(1)); - f |= (OpenSim.Data.RegionFlags)val; + val = (int)Enum.Parse(typeof(OpenSim.Framework.RegionFlags), p.Substring(1)); + f |= (OpenSim.Framework.RegionFlags)val; } else if (p.StartsWith("-")) { - val = (int)Enum.Parse(typeof(OpenSim.Data.RegionFlags), p.Substring(1)); - f &= ~(OpenSim.Data.RegionFlags)val; + val = (int)Enum.Parse(typeof(OpenSim.Framework.RegionFlags), p.Substring(1)); + f &= ~(OpenSim.Framework.RegionFlags)val; } else { - val = (int)Enum.Parse(typeof(OpenSim.Data.RegionFlags), p); - f |= (OpenSim.Data.RegionFlags)val; + val = (int)Enum.Parse(typeof(OpenSim.Framework.RegionFlags), p); + f |= (OpenSim.Framework.RegionFlags)val; } } catch (Exception) @@ -728,7 +728,7 @@ namespace OpenSim.Services.GridService int flags = Convert.ToInt32(r.Data["flags"]); flags = ParseFlags(flags, cmd[4]); r.Data["flags"] = flags.ToString(); - OpenSim.Data.RegionFlags f = (OpenSim.Data.RegionFlags)flags; + OpenSim.Framework.RegionFlags f = (OpenSim.Framework.RegionFlags)flags; MainConsole.Instance.Output(String.Format("Set region {0} to {1}", r.RegionName, f)); m_Database.Store(r); diff --git a/OpenSim/Services/GridService/HypergridLinker.cs b/OpenSim/Services/GridService/HypergridLinker.cs index 78eab3dd2b..743d08947f 100644 --- a/OpenSim/Services/GridService/HypergridLinker.cs +++ b/OpenSim/Services/GridService/HypergridLinker.cs @@ -390,8 +390,8 @@ namespace OpenSim.Services.GridService List regions = m_Database.Get(mapName, m_ScopeID); if (regions != null && regions.Count > 0) { - OpenSim.Data.RegionFlags rflags = (OpenSim.Data.RegionFlags)Convert.ToInt32(regions[0].Data["flags"]); - if ((rflags & OpenSim.Data.RegionFlags.Hyperlink) != 0) + OpenSim.Framework.RegionFlags rflags = (OpenSim.Framework.RegionFlags)Convert.ToInt32(regions[0].Data["flags"]); + if ((rflags & OpenSim.Framework.RegionFlags.Hyperlink) != 0) { regInfo = new GridRegion(); regInfo.RegionID = regions[0].RegionID; @@ -460,7 +460,7 @@ namespace OpenSim.Services.GridService private void AddHyperlinkRegion(GridRegion regionInfo, ulong regionHandle) { RegionData rdata = m_GridService.RegionInfo2RegionData(regionInfo); - int flags = (int)OpenSim.Data.RegionFlags.Hyperlink + (int)OpenSim.Data.RegionFlags.NoDirectLogin + (int)OpenSim.Data.RegionFlags.RegionOnline; + int flags = (int)OpenSim.Framework.RegionFlags.Hyperlink + (int)OpenSim.Framework.RegionFlags.NoDirectLogin + (int)OpenSim.Framework.RegionFlags.RegionOnline; rdata.Data["flags"] = flags.ToString(); m_Database.Store(rdata); diff --git a/OpenSim/Services/Interfaces/IGridService.cs b/OpenSim/Services/Interfaces/IGridService.cs index d80999649f..d7da05688e 100644 --- a/OpenSim/Services/Interfaces/IGridService.cs +++ b/OpenSim/Services/Interfaces/IGridService.cs @@ -100,6 +100,19 @@ namespace OpenSim.Services.Interfaces List GetFallbackRegions(UUID scopeID, int x, int y); List GetHyperlinks(UUID scopeID); + /// + /// Get internal OpenSimulator region flags. + /// + /// + /// See OpenSimulator.Framework.RegionFlags. These are not returned in the GridRegion structure - + /// they currently need to be requested separately. Possibly this should change to avoid multiple service calls + /// in some situations. + /// + /// + /// The region flags. + /// + /// + /// int GetRegionFlags(UUID scopeID, UUID regionID); } diff --git a/OpenSim/Services/Interfaces/IGridUserService.cs b/OpenSim/Services/Interfaces/IGridUserService.cs index 8b738abf51..2e7237e104 100644 --- a/OpenSim/Services/Interfaces/IGridUserService.cs +++ b/OpenSim/Services/Interfaces/IGridUserService.cs @@ -80,7 +80,7 @@ namespace OpenSim.Services.Interfaces } - public Dictionary ToKeyValuePairs() + public virtual Dictionary ToKeyValuePairs() { Dictionary result = new Dictionary(); result["UserID"] = UserID; diff --git a/OpenSim/Services/LLLoginService/LLLoginService.cs b/OpenSim/Services/LLLoginService/LLLoginService.cs index 495dc5297c..59fb55981b 100644 --- a/OpenSim/Services/LLLoginService/LLLoginService.cs +++ b/OpenSim/Services/LLLoginService/LLLoginService.cs @@ -293,7 +293,7 @@ namespace OpenSim.Services.LLLoginService { m_log.InfoFormat( "[LLOGIN SERVICE]: Login failed for {0} {1}, reason: user level is {2} but minimum login level is {3}", - firstName, lastName, m_MinLoginLevel, account.UserLevel); + firstName, lastName, account.UserLevel, m_MinLoginLevel); return LLFailedLoginResponse.LoginBlockedProblem; } diff --git a/bin/OpenSimDefaults.ini b/bin/OpenSimDefaults.ini index 6e6a089d7f..b99a02eff9 100644 --- a/bin/OpenSimDefaults.ini +++ b/bin/OpenSimDefaults.ini @@ -427,6 +427,10 @@ ; " (Mozilla Compatible)" to the text where there are problems with a web server ;user_agent = "OpenSim LSL (Mozilla Compatible)" + ; OpenSim can send multiple simultaneous requests for services such as asset + ; retrieval. However, some versions of mono appear to hang when there are too + ; many simultaneous requests, default is 30 and is currently applied only to assets + ;MaxRequestConcurrency = 30 [XMLRPC] ; ## @@ -840,6 +844,15 @@ ; When the avatar flies, it will be moved up by this amount off the ground (in meters) minimum_ground_flight_offset = 3.0 + ; Plant avatar. This reduces the effect of physical contacts with the avatar. + ; If you have a group of unruly and rude visitors that bump each other, turn this on to make that less attractive. + ; The avatar still allows a small movement based on the PID settings above. Stronger PID settings AND this active + ; will lock the avatar in place + av_planted = false + + ; No Avatar Avatar Collissions. This causes avatar to be able to walk through each other as if they're ghosts but still interact with the environment + av_av_collisions_off = false + ; ## ; ## Object options ; ## @@ -1589,10 +1602,14 @@ [PacketPool] - ; Enables the experimental packet pool. Yes, we've been here before. ;RecyclePackets = true; ;RecycleDataBlocks = true; + ; If true, then the basic packet objects used to receive data are also recycled, not just the LLUDP packets. + ; This reduces data churn + ; This setting is currently experimental and defaults to false. + RecycleBaseUDPPackets = false; + [InterestManagement] ; This section controls how state updates are prioritized for each client diff --git a/bin/Robust.HG.ini.example b/bin/Robust.HG.ini.example index bef9974648..068061f60e 100644 --- a/bin/Robust.HG.ini.example +++ b/bin/Robust.HG.ini.example @@ -20,8 +20,38 @@ ; * The full syntax of a connector string is: ; * [[@]/][:] ; * -[Startup] -ServiceConnectors = "8003/OpenSim.Server.Handlers.dll:AssetServiceConnector,8003/OpenSim.Server.Handlers.dll:XInventoryInConnector,8004/OpenSim.Server.Handlers.dll:FreeswitchServerConnector,8003/OpenSim.Server.Handlers.dll:GridServiceConnector,8002/OpenSim.Server.Handlers.dll:GridInfoServerInConnector,8003/OpenSim.Server.Handlers.dll:AuthenticationServiceConnector,8002/OpenSim.Server.Handlers.dll:OpenIdServerConnector,8003/OpenSim.Server.Handlers.dll:AvatarServiceConnector,8002/OpenSim.Server.Handlers.dll:LLLoginServiceInConnector,8003/OpenSim.Server.Handlers.dll:PresenceServiceConnector,8003/OpenSim.Server.Handlers.dll:UserAccountServiceConnector,8003/OpenSim.Server.Handlers.dll:GridUserServiceConnector,8003/OpenSim.Server.Handlers.dll:FriendsServiceConnector,8002/OpenSim.Server.Handlers.dll:GatekeeperServiceInConnector,8002/OpenSim.Server.Handlers.dll:UserAgentServerConnector,HGInventoryService@8002/OpenSim.Server.Handlers.dll:XInventoryInConnector,HGAssetService@8002/OpenSim.Server.Handlers.dll:AssetServiceConnector,8002/OpenSim.Server.Handlers.dll:HeloServiceInConnector,8002/OpenSim.Server.Handlers.dll:HGFriendsServerConnector,8002/OpenSim.Server.Handlers.dll:InstantMessageServerConnector,8003/OpenSim.Server.Handlers.dll:MapAddServiceConnector,8002/OpenSim.Server.Handlers.dll:MapGetServiceConnector,8002/OpenSim.Server.Handlers.dll:IntegrationServiceConnector" + +[ServiceList] + +AssetServiceConnector = "8003/OpenSim.Server.Handlers.dll:AssetServiceConnector" +InventoryInConnector = "8003/OpenSim.Server.Handlers.dll:XInventoryInConnector" +VoiceConnector = "8004/OpenSim.Server.Handlers.dll:FreeswitchServerConnector" +GridServiceConnector = "8003/OpenSim.Server.Handlers.dll:GridServiceConnector" +GridInfoServerInConnector = "8002/OpenSim.Server.Handlers.dll:GridInfoServerInConnector" +AuthenticationServiceConnector = "8003/OpenSim.Server.Handlers.dll:AuthenticationServiceConnector" +OpenIdServerConnector = "8002/OpenSim.Server.Handlers.dll:OpenIdServerConnector" +AvatarServiceConnector = "8003/OpenSim.Server.Handlers.dll:AvatarServiceConnector" +LLLoginServiceInConnector = "8002/OpenSim.Server.Handlers.dll:LLLoginServiceInConnector" +PresenceServiceConnector = "8003/OpenSim.Server.Handlers.dll:PresenceServiceConnector" +UserAccountServiceConnector = "8003/OpenSim.Server.Handlers.dll:UserAccountServiceConnector" +GridUserServiceConnector = "8003/OpenSim.Server.Handlers.dll:GridUserServiceConnector" +FriendsServiceConnector = "8003/OpenSim.Server.Handlers.dll:FriendsServiceConnector" +MapAddServiceConnector = "8003/OpenSim.Server.Handlers.dll:MapAddServiceConnector" +MapGetServiceConnector = "8002/OpenSim.Server.Handlers.dll:MapGetServiceConnector" + +;; Additions for Hypergrid + +GatekeeperServiceInConnector = "8002/OpenSim.Server.Handlers.dll:GatekeeperServiceInConnector" +UserAgentServerConnector = "8002/OpenSim.Server.Handlers.dll:UserAgentServerConnector" +HeloServiceInConnector = "8002/OpenSim.Server.Handlers.dll:HeloServiceInConnector" +HGFriendsServerConnector = "8002/OpenSim.Server.Handlers.dll:HGFriendsServerConnector" +InstantMessageServerConnector = "8002/OpenSim.Server.Handlers.dll:InstantMessageServerConnector" +HGInventoryServiceConnector = "HGInventoryService@8002/OpenSim.Server.Handlers.dll:XInventoryInConnector" +HGAssetServiceConnector = "HGAssetService@8002/OpenSim.Server.Handlers.dll:AssetServiceConnector" + +;; Additions for other add-on modules. For example: +;; WifiServerConnector = "8002/Diva.Wifi.dll:WifiServerConnector" +IntegrationService = "8002/OpenSim.Server.Handlers.dll:IntegrationServiceConnector" ; * This is common for all services, it's the network setup for the entire ; * server instance, if none is specified above diff --git a/bin/Robust.ini.example b/bin/Robust.ini.example index 36863dc90b..98a14bab2f 100644 --- a/bin/Robust.ini.example +++ b/bin/Robust.ini.example @@ -13,8 +13,24 @@ ; * [[@]/][:] ; * [Startup] -ServiceConnectors = "8003/OpenSim.Server.Handlers.dll:AssetServiceConnector,8003/OpenSim.Server.Handlers.dll:XInventoryInConnector,8004/OpenSim.Server.Handlers.dll:FreeswitchServerConnector,8003/OpenSim.Server.Handlers.dll:GridServiceConnector,8002/OpenSim.Server.Handlers.dll:GridInfoServerInConnector,8003/OpenSim.Server.Handlers.dll:AuthenticationServiceConnector,8002/OpenSim.Server.Handlers.dll:OpenIdServerConnector,8003/OpenSim.Server.Handlers.dll:AvatarServiceConnector,8002/OpenSim.Server.Handlers.dll:LLLoginServiceInConnector,8003/OpenSim.Server.Handlers.dll:PresenceServiceConnector,8003/OpenSim.Server.Handlers.dll:UserAccountServiceConnector,8003/OpenSim.Server.Handlers.dll:GridUserServiceConnector,8003/OpenSim.Server.Handlers.dll:FriendsServiceConnector,8003/OpenSim.Server.Handlers.dll:MapAddServiceConnector,8002/OpenSim.Server.Handlers.dll:MapGetServiceConnector,8002/OpenSim.Server.Handlers.dll:IntegrationServiceConnector" +[ServiceList] +AssetServiceConnector = "8003/OpenSim.Server.Handlers.dll:AssetServiceConnector" +InventoryInConnector = "8003/OpenSim.Server.Handlers.dll:XInventoryInConnector" +VoiceConnector = "8004/OpenSim.Server.Handlers.dll:FreeswitchServerConnector" +GridServiceConnector = "8003/OpenSim.Server.Handlers.dll:GridServiceConnector" +GridInfoServerInConnector = "8002/OpenSim.Server.Handlers.dll:GridInfoServerInConnector" +AuthenticationServiceConnector = "8003/OpenSim.Server.Handlers.dll:AuthenticationServiceConnector" +OpenIdServerConnector = "8002/OpenSim.Server.Handlers.dll:OpenIdServerConnector" +AvatarServiceConnector = "8003/OpenSim.Server.Handlers.dll:AvatarServiceConnector" +LLLoginServiceInConnector = "8002/OpenSim.Server.Handlers.dll:LLLoginServiceInConnector" +PresenceServiceConnector = "8003/OpenSim.Server.Handlers.dll:PresenceServiceConnector" +UserAccountServiceConnector = "8003/OpenSim.Server.Handlers.dll:UserAccountServiceConnector" +GridUserServiceConnector = "8003/OpenSim.Server.Handlers.dll:GridUserServiceConnector" +FriendsServiceConnector = "8003/OpenSim.Server.Handlers.dll:FriendsServiceConnector" +MapAddServiceConnector = "8003/OpenSim.Server.Handlers.dll:MapAddServiceConnector" +MapGetServiceConnector = "8002/OpenSim.Server.Handlers.dll:MapGetServiceConnector" +IntegrationService = "8002/OpenSim.Server.Handlers.dll:IntegrationServiceConnector" ; * This is common for all services, it's the network setup for the entire ; * server instance, if none is specified above ; * diff --git a/bin/lib32/BulletSim.dll b/bin/lib32/BulletSim.dll index da6e31cdd2..785f01f92d 100755 Binary files a/bin/lib32/BulletSim.dll and b/bin/lib32/BulletSim.dll differ diff --git a/bin/lib32/libBulletSim.so b/bin/lib32/libBulletSim.so index 9503cdb188..6af2fb4b28 100755 Binary files a/bin/lib32/libBulletSim.so and b/bin/lib32/libBulletSim.so differ diff --git a/bin/lib64/BulletSim.dll b/bin/lib64/BulletSim.dll index 1782c8e741..e9e521a5e1 100755 Binary files a/bin/lib64/BulletSim.dll and b/bin/lib64/BulletSim.dll differ diff --git a/bin/lib64/libBulletSim.so b/bin/lib64/libBulletSim.so index 06e6ce27fb..cbf4f792e6 100755 Binary files a/bin/lib64/libBulletSim.so and b/bin/lib64/libBulletSim.so differ diff --git a/prebuild.xml b/prebuild.xml index b33a178767..4aa4f03f04 100644 --- a/prebuild.xml +++ b/prebuild.xml @@ -112,6 +112,34 @@ + + + + ../../../bin/ + + + + + ../../../bin/ + + + + ../../../bin/ + + + + + + + + + + + + + + + @@ -205,34 +233,6 @@ - - - - ../../../bin/ - - - - - ../../../bin/ - - - - ../../../bin/ - - - - - - - - - - - - - - - @@ -1415,110 +1415,6 @@ - - - - ../../../bin/ - true - - - - - ../../../bin/ - true - - - - ../../../bin/ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ../../../bin/ - - - - - ../../../bin/ - - - - ../../../bin/ - - - - - - - - - - - - - - - - - - - - - - - - - @@ -1639,6 +1535,111 @@ + + + + ../../../bin/ + true + + + + + ../../../bin/ + true + + + + ../../../bin/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ../../../bin/ + + + + + ../../../bin/ + + + + ../../../bin/ + + + + + + + + + + + + + + + + + + + + + + + + +