diff --git a/OpenSim/Framework/AvatarAppearance.cs b/OpenSim/Framework/AvatarAppearance.cs index 4f598b04ae..95e9667f6b 100644 --- a/OpenSim/Framework/AvatarAppearance.cs +++ b/OpenSim/Framework/AvatarAppearance.cs @@ -330,6 +330,9 @@ namespace OpenSim.Framework SetVisualParams(visualParams); } + /// + /// Set avatar height by a calculation based on their visual parameters. + /// public virtual void SetHeight() { // Start with shortest possible female avatar height diff --git a/OpenSim/Framework/Client/IClientChat.cs b/OpenSim/Framework/Client/IClientChat.cs index 078ea9b668..86b1faa8f6 100644 --- a/OpenSim/Framework/Client/IClientChat.cs +++ b/OpenSim/Framework/Client/IClientChat.cs @@ -33,7 +33,8 @@ namespace OpenSim.Framework.Client { event ChatMessage OnChatFromClient; - void SendChatMessage(string message, byte type, Vector3 fromPos, string fromName, UUID fromAgentID, byte source, - byte audible); + void SendChatMessage( + string message, byte type, Vector3 fromPos, string fromName, UUID fromAgentID, UUID ownerID, byte source, + byte audible); } -} +} \ No newline at end of file diff --git a/OpenSim/Framework/Console/ConsoleUtil.cs b/OpenSim/Framework/Console/ConsoleUtil.cs index a7cf0c03b2..16a63e0ca5 100644 --- a/OpenSim/Framework/Console/ConsoleUtil.cs +++ b/OpenSim/Framework/Console/ConsoleUtil.cs @@ -27,88 +27,202 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Reflection; using log4net; using OpenMetaverse; -public class ConsoleUtil +namespace OpenSim.Framework.Console { -// 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) + public class ConsoleUtil { - return TryParseConsoleVector(rawConsoleVector, c => float.MinValue.ToString(), out vector); - } + // private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); - /// - /// 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); - } + public const int LocalIdNotFound = 0; + + /// + /// Used by modules to display stock co-ordinate help, though possibly this should be under some general section + /// rather than in each help summary. + /// + public const string CoordHelp + = @"Each component of the coord is comma separated. There must be no spaces between the commas. + If you don't care about the z component you can simply omit it. + If you don't care about the x or y components then you can leave them blank (though a comma is still required) + If you want to specify the maxmimum value of a component then you can use ~ instead of a number + If you want to specify the minimum value of a component then you can use -~ instead of a number + e.g. + delete object pos 20,20,20 to 40,40,40 + delete object pos 20,20 to 40,40 + delete object pos ,20,20 to ,40,40 + delete object pos ,,30 to ,,~ + delete object pos ,,-~ to ,,30"; + + public const string MinRawConsoleVectorValue = "-~"; + public const string MaxRawConsoleVectorValue = "~"; + + public const string VectorSeparator = ","; + public static char[] VectorSeparatorChars = VectorSeparator.ToCharArray(); - /// - /// 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) + /// + /// Check if the given file path exists. + /// + /// If not, warning is printed to the given console. + /// true if the file does not exist, false otherwise. + /// + /// + public static bool CheckFileDoesNotExist(ICommandConsole console, string path) { - vector = Vector3.Zero; - return false; + if (File.Exists(path)) + { + console.OutputFormat("File {0} already exists. Please move or remove it.", path); + return false; + } + + return true; + } + + /// + /// Try to parse a console UUID from the console. + /// + /// + /// Will complain to the console if parsing fails. + /// + /// + /// If null then no complaint is printed. + /// + /// + public static bool TryParseConsoleUuid(ICommandConsole console, string rawUuid, out UUID uuid) + { + if (!UUID.TryParse(rawUuid, out uuid)) + { + if (console != null) + console.OutputFormat("{0} is not a valid uuid", rawUuid); + + return false; + } + + return true; } - for (int i = components.Count; i < 3; i++) - components.Add(""); + public static bool TryParseConsoleLocalId(ICommandConsole console, string rawLocalId, out uint localId) + { + if (!uint.TryParse(rawLocalId, out localId)) + { + if (console != null) + console.OutputFormat("{0} is not a valid local id", localId); - 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; - }); + return false; + } - string semiDigestedConsoleVector = string.Join(VectorSeparator, semiDigestedComponents.ToArray()); + if (localId == 0) + { + if (console != null) + console.OutputFormat("{0} is not a valid local id - it must be greater than 0", localId); -// m_log.DebugFormat("[CONSOLE UTIL]: Parsing {0} into OpenMetaverse.Vector3", semiDigestedConsoleVector); + return false; + } - return Vector3.TryParse(semiDigestedConsoleVector, out vector); + return true; + } + + /// + /// Tries to parse the input as either a UUID or a local ID. + /// + /// true if parsing succeeded, false otherwise. + /// + /// + /// + /// + /// Will be set to ConsoleUtil.LocalIdNotFound if parsing result was a UUID or no parse succeeded. + /// + public static bool TryParseConsoleId(ICommandConsole console, string rawId, out UUID uuid, out uint localId) + { + if (TryParseConsoleUuid(null, rawId, out uuid)) + { + localId = LocalIdNotFound; + return true; + } + + if (TryParseConsoleLocalId(null, rawId, out localId)) + { + return true; + } + + if (console != null) + console.OutputFormat("{0} is not a valid UUID or local id", rawId); + + return false; + } + + /// + /// 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/IClientAPI.cs b/OpenSim/Framework/IClientAPI.cs index 9856978cfd..87433cc3cb 100644 --- a/OpenSim/Framework/IClientAPI.cs +++ b/OpenSim/Framework/IClientAPI.cs @@ -1099,8 +1099,20 @@ namespace OpenSim.Framework void SendAnimations(UUID[] animID, int[] seqs, UUID sourceAgentId, UUID[] objectIDs); void SendRegionHandshake(RegionInfo regionInfo, RegionHandshakeArgs args); - void SendChatMessage(string message, byte type, Vector3 fromPos, string fromName, UUID fromAgentID, byte source, - byte audible); + /// + /// Send chat to the viewer. + /// + /// + /// + /// + /// + /// + /// + /// + /// + void SendChatMessage( + string message, byte type, Vector3 fromPos, string fromName, UUID fromAgentID, UUID ownerID, byte source, + byte audible); void SendInstantMessage(GridInstantMessage im); diff --git a/OpenSim/Framework/Monitoring/StatsManager.cs b/OpenSim/Framework/Monitoring/StatsManager.cs index 31989e50ad..4844336e31 100644 --- a/OpenSim/Framework/Monitoring/StatsManager.cs +++ b/OpenSim/Framework/Monitoring/StatsManager.cs @@ -207,7 +207,7 @@ namespace OpenSim.Framework.Monitoring return false; newContainer = new Dictionary(container); - newContainer.Remove(stat.UniqueName); + newContainer.Remove(stat.ShortName); newCategory = new Dictionary>(category); newCategory.Remove(stat.Container); @@ -248,6 +248,19 @@ namespace OpenSim.Framework.Monitoring } } + /// + /// Stat type. + /// + /// + /// A push stat is one which is continually updated and so it's value can simply by read. + /// A pull stat is one where reading the value triggers a collection method - the stat is not continually updated. + /// + public enum StatType + { + Push, + Pull + } + /// /// Verbosity of stat. /// @@ -265,11 +278,6 @@ namespace OpenSim.Framework.Monitoring /// 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). /// @@ -285,29 +293,65 @@ namespace OpenSim.Framework.Monitoring /// public string Container { get; private set; } + public StatType StatType { get; private set; } + + /// + /// Action used to update this stat when the value is requested if it's a pull type. + /// + public Action PullAction { 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; } + public virtual double Value + { + get + { + // Asking for an update here means that the updater cannot access this value without infinite recursion. + // XXX: A slightly messy but simple solution may be to flick a flag so we can tell if this is being + // called by the pull action and just return the value. + if (StatType == StatType.Pull) + PullAction(this); + + return m_value; + } + + set + { + m_value = value; + } + } + + private double m_value; /// /// Constructor /// /// Short name for the stat. Must not contain spaces. e.g. "LongFrames" /// Human readable name for the stat. e.g. "Long frames" + /// Description of stat /// /// 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. + /// Push or pull + /// Pull stats need an action to update the stat on request. Push stats should set null here. /// 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) + string shortName, + string name, + string description, + string unitName, + string category, + string container, + StatType type, + Action pullAction, + StatVerbosity verbosity) { if (StatsManager.SubCommands.Contains(category)) throw new Exception( @@ -315,18 +359,18 @@ namespace OpenSim.Framework.Monitoring ShortName = shortName; Name = name; + Description = description; UnitName = unitName; Category = category; Container = container; + StatType = type; + + if (StatType == StatType.Push && pullAction != null) + throw new Exception("A push stat cannot have a pull action"); + else + PullAction = pullAction; + 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() @@ -361,8 +405,15 @@ namespace OpenSim.Framework.Monitoring } public PercentageStat( - string shortName, string name, string category, string container, StatVerbosity verbosity, string description) - : base(shortName, name, "%", category, container, verbosity, description) {} + string shortName, + string name, + string description, + string category, + string container, + StatType type, + Action pullAction, + StatVerbosity verbosity) + : base(shortName, name, description, "%", category, container, type, pullAction, verbosity) {} public override string ToConsoleString() { diff --git a/OpenSim/Framework/Pool.cs b/OpenSim/Framework/Pool.cs index 1ca06c3065..5484f5c2d5 100644 --- a/OpenSim/Framework/Pool.cs +++ b/OpenSim/Framework/Pool.cs @@ -38,8 +38,23 @@ namespace OpenSim.Framework /// public class Pool { + /// + /// Number of objects in the pool. + /// + public int Count + { + get + { + lock (m_pool) + return m_pool.Count; + } + } + private Stack m_pool; + /// + /// Maximum pool size. Beyond this, any returned objects are not pooled. + /// private int m_maxPoolSize; private Func m_createFunction; diff --git a/OpenSim/Framework/Servers/HttpServer/BaseHttpServer.cs b/OpenSim/Framework/Servers/HttpServer/BaseHttpServer.cs index b018e57c45..410a76a798 100644 --- a/OpenSim/Framework/Servers/HttpServer/BaseHttpServer.cs +++ b/OpenSim/Framework/Servers/HttpServer/BaseHttpServer.cs @@ -542,11 +542,8 @@ namespace OpenSim.Framework.Servers.HttpServer { case null: case "text/html": - if (DebugLevel >= 3) - m_log.DebugFormat( - "[BASE HTTP SERVER]: HTTP IN {0} :{1} {2} content type handler {3} {4} from {5}", - RequestNumber, Port, request.ContentType, request.HttpMethod, request.Url.PathAndQuery, request.RemoteIPEndPoint); + LogIncomingToContentTypeHandler(request); buffer = HandleHTTPRequest(request, response); break; @@ -554,11 +551,8 @@ namespace OpenSim.Framework.Servers.HttpServer case "application/llsd+xml": case "application/xml+llsd": case "application/llsd+json": - if (DebugLevel >= 3) - m_log.DebugFormat( - "[BASE HTTP SERVER]: HTTP IN {0} :{1} {2} content type handler {3} {4} from {5}", - RequestNumber, Port, request.ContentType, request.HttpMethod, request.Url.PathAndQuery, request.RemoteIPEndPoint); + LogIncomingToContentTypeHandler(request); buffer = HandleLLSDRequests(request, response); break; @@ -693,7 +687,7 @@ namespace OpenSim.Framework.Servers.HttpServer "[BASE HTTP SERVER]: HTTP IN {0} :{1} {2} content type handler {3} {4} from {5}", RequestNumber, Port, - request.ContentType, + (request.ContentType == null || request.ContentType == "") ? "not set" : request.ContentType, request.HttpMethod, request.Url.PathAndQuery, request.RemoteIPEndPoint); diff --git a/OpenSim/Region/Application/OpenSimBase.cs b/OpenSim/Region/Application/OpenSimBase.cs index 4f1b4392ad..7b591a88be 100644 --- a/OpenSim/Region/Application/OpenSimBase.cs +++ b/OpenSim/Region/Application/OpenSimBase.cs @@ -543,7 +543,7 @@ namespace OpenSim if (account == null) { m_log.ErrorFormat( - "[OPENSIM]: Unable to store account. If this simulator is connected to a grid, you must create the estate owner account first."); + "[OPENSIM]: Unable to store account. If this simulator is connected to a grid, you must create the estate owner account first at the grid level."); } else { diff --git a/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs b/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs index 7427c5947a..8e5a6d2b42 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs @@ -817,8 +817,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP OutPacket(mov, ThrottleOutPacketType.Unknown); } - public void SendChatMessage(string message, byte type, Vector3 fromPos, string fromName, - UUID fromAgentID, byte source, byte audible) + public void SendChatMessage( + string message, byte type, Vector3 fromPos, string fromName, + UUID fromAgentID, UUID ownerID, byte source, byte audible) { ChatFromSimulatorPacket reply = (ChatFromSimulatorPacket)PacketPool.Instance.GetPacket(PacketType.ChatFromSimulator); reply.ChatData.Audible = audible; @@ -827,7 +828,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP reply.ChatData.SourceType = source; reply.ChatData.Position = fromPos; reply.ChatData.FromName = Util.StringToBytes256(fromName); - reply.ChatData.OwnerID = fromAgentID; + reply.ChatData.OwnerID = ownerID; reply.ChatData.SourceID = fromAgentID; OutPacket(reply, ThrottleOutPacketType.Task); diff --git a/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs b/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs index 419de66e7b..14cc863f6b 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs @@ -170,6 +170,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP private Pool m_incomingPacketPool; + private Stat m_incomingPacketPoolStat; + private int m_defaultRTO = 0; private int m_maxRTO = 0; private int m_ackTimeout = 0; @@ -214,6 +216,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP m_circuitManager = circuitManager; int sceneThrottleBps = 0; + bool usePools = false; IConfig config = configSource.Configs["ClientStack.LindenUDP"]; if (config != null) @@ -246,7 +249,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP { PacketPool.Instance.RecyclePackets = packetConfig.GetBoolean("RecyclePackets", true); PacketPool.Instance.RecycleDataBlocks = packetConfig.GetBoolean("RecycleDataBlocks", true); - UsePools = packetConfig.GetBoolean("RecycleBaseUDPPackets", false); + usePools = packetConfig.GetBoolean("RecycleBaseUDPPackets", usePools); } #region BinaryStats @@ -277,8 +280,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP m_throttle = new TokenBucket(null, sceneThrottleBps); ThrottleRates = new ThrottleRates(configSource); - if (UsePools) - m_incomingPacketPool = new Pool(() => new IncomingPacket(), 500); + if (usePools) + EnablePools(); } public void Start() @@ -331,6 +334,50 @@ namespace OpenSim.Region.ClientStack.LindenUDP base.StopInbound(); } + protected override bool EnablePools() + { + if (!UsePools) + { + base.EnablePools(); + + m_incomingPacketPool = new Pool(() => new IncomingPacket(), 500); + + m_incomingPacketPoolStat + = new Stat( + "IncomingPacketPoolCount", + "Objects within incoming packet pool", + "The number of objects currently stored within the incoming packet pool", + "", + "clientstack", + "packetpool", + StatType.Pull, + stat => stat.Value = m_incomingPacketPool.Count, + StatVerbosity.Debug); + + StatsManager.RegisterStat(m_incomingPacketPoolStat); + + return true; + } + + return false; + } + + protected override bool DisablePools() + { + if (UsePools) + { + base.DisablePools(); + + StatsManager.DeregisterStat(m_incomingPacketPoolStat); + + // We won't null out the pool to avoid a race condition with code that may be in the middle of using it. + + return true; + } + + return false; + } + /// /// If the outgoing UDP thread times out, then return client that was being processed to help with debugging. /// @@ -394,6 +441,14 @@ namespace OpenSim.Region.ClientStack.LindenUDP + "all - stop in and outbound processing.\n", HandleStopCommand); + MainConsole.Instance.Commands.AddCommand( + "Debug", + false, + "debug lludp pool", + "debug lludp pool ", + "Turn object pooling within the lludp component on or off.", + HandlePoolCommand); + MainConsole.Instance.Commands.AddCommand( "Debug", false, @@ -437,6 +492,32 @@ namespace OpenSim.Region.ClientStack.LindenUDP StopOutbound(); } + private void HandlePoolCommand(string module, string[] args) + { + if (args.Length != 4) + { + MainConsole.Instance.Output("Usage: debug lludp pool "); + return; + } + + string enabled = args[3]; + + if (enabled == "on") + { + if (EnablePools()) + MainConsole.Instance.OutputFormat("Packet pools enabled on {0}", m_scene.Name); + } + else if (enabled == "off") + { + if (DisablePools()) + MainConsole.Instance.OutputFormat("Packet pools disabled on {0}", m_scene.Name); + } + else + { + MainConsole.Instance.Output("Usage: debug lludp pool "); + } + } + private void HandleStatusCommand(string module, string[] args) { MainConsole.Instance.OutputFormat( @@ -444,6 +525,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP MainConsole.Instance.OutputFormat( "OUT LLUDP packet processing for {0} is {1}", m_scene.Name, IsRunningOutbound ? "enabled" : "disabled"); + + MainConsole.Instance.OutputFormat("LLUDP pools in {0} are {1}", m_scene.Name, UsePools ? "on" : "off"); } public bool HandlesRegion(Location x) diff --git a/OpenSim/Region/ClientStack/Linden/UDP/OpenSimUDPBase.cs b/OpenSim/Region/ClientStack/Linden/UDP/OpenSimUDPBase.cs index 6e6b3ef1b5..85cbb0659b 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/OpenSimUDPBase.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/OpenSimUDPBase.cs @@ -31,6 +31,7 @@ using System.Net.Sockets; using System.Threading; using log4net; using OpenSim.Framework; +using OpenSim.Framework.Monitoring; namespace OpenMetaverse { @@ -76,6 +77,8 @@ namespace OpenMetaverse /// If IsRunningOut = false, then any request to send a packet is simply dropped. public bool IsRunningOutbound { get; private set; } + private Stat m_poolCountStat; + /// /// Default constructor /// @@ -106,11 +109,6 @@ namespace OpenMetaverse /// necessary public void StartInbound(int recvBufferSize, bool asyncPacketHandling) { - if (UsePools) - m_pool = new Pool(() => new UDPPacketBuffer(), 500); - else - m_pool = null; - m_asyncPacketHandling = asyncPacketHandling; if (!IsRunningInbound) @@ -180,6 +178,49 @@ namespace OpenMetaverse IsRunningOutbound = false; } + protected virtual bool EnablePools() + { + if (!UsePools) + { + m_pool = new Pool(() => new UDPPacketBuffer(), 500); + + m_poolCountStat + = new Stat( + "UDPPacketBufferPoolCount", + "Objects within the UDPPacketBuffer pool", + "The number of objects currently stored within the UDPPacketBuffer pool", + "", + "clientstack", + "packetpool", + StatType.Pull, + stat => stat.Value = m_pool.Count, + StatVerbosity.Debug); + + StatsManager.RegisterStat(m_poolCountStat); + + UsePools = true; + + return true; + } + + return false; + } + + protected virtual bool DisablePools() + { + if (UsePools) + { + UsePools = false; + StatsManager.DeregisterStat(m_poolCountStat); + + // We won't null out the pool to avoid a race condition with code that may be in the middle of using it. + + return true; + } + + return false; + } + private void AsyncBeginReceive() { UDPPacketBuffer buf; diff --git a/OpenSim/Region/ClientStack/Linden/UDP/PacketPool.cs b/OpenSim/Region/ClientStack/Linden/UDP/PacketPool.cs index 2a3d14f69f..9f22fb48ec 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/PacketPool.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/PacketPool.cs @@ -47,18 +47,22 @@ namespace OpenSim.Region.ClientStack.LindenUDP private PercentageStat m_packetsReusedStat = new PercentageStat( "PacketsReused", "Packets reused", + "Number of packets reused out of all requests to the packet pool", "clientstack", "packetpool", - StatVerbosity.Debug, - "Number of packets reused out of all requests to the packet pool"); + StatType.Push, + null, + StatVerbosity.Debug); private PercentageStat m_blocksReusedStat = new PercentageStat( - "BlocksReused", - "Blocks reused", + "PacketDataBlocksReused", + "Packet data blocks reused", + "Number of data blocks reused out of all requests to the packet pool", "clientstack", "packetpool", - StatVerbosity.Debug, - "Number of data blocks reused out of all requests to the packet pool"); + StatType.Push, + null, + StatVerbosity.Debug); /// /// Pool of packets available for reuse. @@ -88,6 +92,30 @@ namespace OpenSim.Region.ClientStack.LindenUDP { StatsManager.RegisterStat(m_packetsReusedStat); StatsManager.RegisterStat(m_blocksReusedStat); + + StatsManager.RegisterStat( + new Stat( + "PacketsPoolCount", + "Objects within the packet pool", + "The number of objects currently stored within the packet pool", + "", + "clientstack", + "packetpool", + StatType.Pull, + stat => { lock (pool) { stat.Value = pool.Count; } }, + StatVerbosity.Debug)); + + StatsManager.RegisterStat( + new Stat( + "PacketDataBlocksPoolCount", + "Objects within the packet data block pool", + "The number of objects currently stored within the packet data block pool", + "", + "clientstack", + "packetpool", + StatType.Pull, + stat => { lock (DataBlocks) { stat.Value = DataBlocks.Count; } }, + StatVerbosity.Debug)); } /// diff --git a/OpenSim/Region/CoreModules/Avatar/Chat/ChatModule.cs b/OpenSim/Region/CoreModules/Avatar/Chat/ChatModule.cs index e4452fbb87..ddf92c3e44 100644 --- a/OpenSim/Region/CoreModules/Avatar/Chat/ChatModule.cs +++ b/OpenSim/Region/CoreModules/Avatar/Chat/ChatModule.cs @@ -186,6 +186,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Chat { string fromName = c.From; UUID fromID = UUID.Zero; + UUID ownerID = UUID.Zero; UUID targetID = c.TargetUUID; string message = c.Message; IScene scene = c.Scene; @@ -208,12 +209,16 @@ namespace OpenSim.Region.CoreModules.Avatar.Chat fromPos = avatar.AbsolutePosition; fromName = avatar.Name; fromID = c.Sender.AgentId; + ownerID = c.Sender.AgentId; break; case ChatSourceType.Object: fromID = c.SenderUUID; + if (c.SenderObject != null && c.SenderObject is SceneObjectPart) + ownerID = ((SceneObjectPart)c.SenderObject).OwnerID; + break; } @@ -236,7 +241,8 @@ namespace OpenSim.Region.CoreModules.Avatar.Chat s.ForEachRootScenePresence( delegate(ScenePresence presence) { - if (TrySendChatMessage(presence, fromPos, regionPos, fromID, fromName, c.Type, message, sourceType, false)) + if (TrySendChatMessage( + presence, fromPos, regionPos, fromID, ownerID, fromName, c.Type, message, sourceType, false)) receiverIDs.Add(presence.UUID); } ); @@ -248,8 +254,9 @@ namespace OpenSim.Region.CoreModules.Avatar.Chat ScenePresence presence = s.GetScenePresence(targetID); if (presence != null && !presence.IsChildAgent) { - if (TrySendChatMessage(presence, fromPos, regionPos, fromID, fromName, ChatTypeEnum.Say, message, sourceType, true)) - receiverIDs.Add(presence.UUID); + if (TrySendChatMessage( + presence, fromPos, regionPos, fromID, ownerID, fromName, ChatTypeEnum.Say, message, sourceType, true)) + receiverIDs.Add(presence.UUID); } } } @@ -305,9 +312,11 @@ namespace OpenSim.Region.CoreModules.Avatar.Chat (null != c.SenderObject) && (((SceneObjectPart)c.SenderObject).OwnerID != client.AgentId)) return; - - client.SendChatMessage(c.Message, (byte)cType, CenterOfRegion, fromName, fromID, - (byte)sourceType, (byte)ChatAudibleLevel.Fully); + + client.SendChatMessage( + c.Message, (byte)cType, CenterOfRegion, fromName, fromID, fromID, + (byte)sourceType, (byte)ChatAudibleLevel.Fully); + receiverIDs.Add(client.AgentId); }); @@ -322,15 +331,20 @@ namespace OpenSim.Region.CoreModules.Avatar.Chat /// /// /param> /// + /// + /// Owner of the message. For at least some messages from objects, this has to be correctly filled with the owner's UUID. + /// This is the case for script error messages in viewer 3 since LLViewer change EXT-7762 + /// /// /// /// /// /// true if the message was sent to the receiver, false if it was not sent due to failing a /// precondition - protected virtual bool TrySendChatMessage(ScenePresence presence, Vector3 fromPos, Vector3 regionPos, - UUID fromAgentID, string fromName, ChatTypeEnum type, - string message, ChatSourceType src, bool ignoreDistance) + protected virtual bool TrySendChatMessage( + ScenePresence presence, Vector3 fromPos, Vector3 regionPos, + UUID fromAgentID, UUID ownerID, string fromName, ChatTypeEnum type, + string message, ChatSourceType src, bool ignoreDistance) { // don't send stuff to child agents if (presence.IsChildAgent) return false; @@ -353,10 +367,11 @@ namespace OpenSim.Region.CoreModules.Avatar.Chat } // TODO: should change so the message is sent through the avatar rather than direct to the ClientView - presence.ControllingClient.SendChatMessage(message, (byte) type, fromPos, fromName, - fromAgentID, (byte)src, (byte)ChatAudibleLevel.Fully); + presence.ControllingClient.SendChatMessage( + message, (byte) type, fromPos, fromName, + fromAgentID, ownerID, (byte)src, (byte)ChatAudibleLevel.Fully); return true; } } -} +} \ No newline at end of file diff --git a/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/InventoryArchiverModule.cs b/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/InventoryArchiverModule.cs index cf870100a4..afe12003b1 100644 --- a/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/InventoryArchiverModule.cs +++ b/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/InventoryArchiverModule.cs @@ -35,6 +35,7 @@ using Nini.Config; using OpenMetaverse; using OpenSim.Framework; using OpenSim.Framework.Communications; +using OpenSim.Framework.Console; using OpenSim.Region.Framework.Interfaces; using OpenSim.Region.Framework.Scenes; using OpenSim.Services.Interfaces; @@ -209,6 +210,9 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver Guid id, string firstName, string lastName, string invPath, string pass, string savePath, Dictionary options) { + if (!ConsoleUtil.CheckFileDoesNotExist(MainConsole.Instance, savePath)) + return false; + if (m_scenes.Count > 0) { UserAccount userInfo = GetUserInfo(firstName, lastName, pass); diff --git a/OpenSim/Region/CoreModules/Avatar/Inventory/Transfer/InventoryTransferModule.cs b/OpenSim/Region/CoreModules/Avatar/Inventory/Transfer/InventoryTransferModule.cs index 33b48393fb..8ff20ddb08 100644 --- a/OpenSim/Region/CoreModules/Avatar/Inventory/Transfer/InventoryTransferModule.cs +++ b/OpenSim/Region/CoreModules/Avatar/Inventory/Transfer/InventoryTransferModule.cs @@ -38,15 +38,15 @@ using OpenSim.Services.Interfaces; namespace OpenSim.Region.CoreModules.Avatar.Inventory.Transfer { - public class InventoryTransferModule : IInventoryTransferModule, ISharedRegionModule + public class InventoryTransferModule : ISharedRegionModule { private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); /// private List m_Scenelist = new List(); - private Dictionary m_AgentRegions = - new Dictionary(); +// private Dictionary m_AgentRegions = +// new Dictionary(); private IMessageTransferModule m_TransferModule = null; private bool m_Enabled = true; @@ -76,12 +76,12 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Transfer m_Scenelist.Add(scene); - scene.RegisterModuleInterface(this); +// scene.RegisterModuleInterface(this); scene.EventManager.OnNewClient += OnNewClient; - scene.EventManager.OnClientClosed += ClientLoggedOut; +// scene.EventManager.OnClientClosed += ClientLoggedOut; scene.EventManager.OnIncomingInstantMessage += OnGridInstantMessage; - scene.EventManager.OnSetRootAgentScene += OnSetRootAgentScene; +// scene.EventManager.OnSetRootAgentScene += OnSetRootAgentScene; } public void RegionLoaded(Scene scene) @@ -96,9 +96,9 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Transfer m_Scenelist.Clear(); scene.EventManager.OnNewClient -= OnNewClient; - scene.EventManager.OnClientClosed -= ClientLoggedOut; +// scene.EventManager.OnClientClosed -= ClientLoggedOut; scene.EventManager.OnIncomingInstantMessage -= OnGridInstantMessage; - scene.EventManager.OnSetRootAgentScene -= OnSetRootAgentScene; +// scene.EventManager.OnSetRootAgentScene -= OnSetRootAgentScene; } } } @@ -106,9 +106,9 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Transfer public void RemoveRegion(Scene scene) { scene.EventManager.OnNewClient -= OnNewClient; - scene.EventManager.OnClientClosed -= ClientLoggedOut; +// scene.EventManager.OnClientClosed -= ClientLoggedOut; scene.EventManager.OnIncomingInstantMessage -= OnGridInstantMessage; - scene.EventManager.OnSetRootAgentScene -= OnSetRootAgentScene; +// scene.EventManager.OnSetRootAgentScene -= OnSetRootAgentScene; m_Scenelist.Remove(scene); } @@ -138,10 +138,10 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Transfer client.OnInstantMessage += OnInstantMessage; } - protected void OnSetRootAgentScene(UUID id, Scene scene) - { - m_AgentRegions[id] = scene; - } +// protected void OnSetRootAgentScene(UUID id, Scene scene) +// { +// m_AgentRegions[id] = scene; +// } private Scene FindClientScene(UUID agentId) { @@ -448,69 +448,69 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Transfer } } - public bool NeedSceneCacheClear(UUID agentID, Scene scene) - { - if (!m_AgentRegions.ContainsKey(agentID)) - { - // Since we can get here two ways, we need to scan - // the scenes here. This is somewhat more expensive - // but helps avoid a nasty bug - // - - foreach (Scene s in m_Scenelist) - { - ScenePresence presence; - - if (s.TryGetScenePresence(agentID, out presence)) - { - // If the agent is in this scene, then we - // are being called twice in a single - // teleport. This is wasteful of cycles - // but harmless due to this 2nd level check - // - // If the agent is found in another scene - // then the list wasn't current - // - // If the agent is totally unknown, then what - // are we even doing here?? - // - if (s == scene) - { - //m_log.Debug("[INVTRANSFERMOD]: s == scene. Returning true in " + scene.RegionInfo.RegionName); - return true; - } - else - { - //m_log.Debug("[INVTRANSFERMOD]: s != scene. Returning false in " + scene.RegionInfo.RegionName); - return false; - } - } - } - //m_log.Debug("[INVTRANSFERMOD]: agent not in scene. Returning true in " + scene.RegionInfo.RegionName); - return true; - } - - // The agent is left in current Scene, so we must be - // going to another instance - // - if (m_AgentRegions[agentID] == scene) - { - //m_log.Debug("[INVTRANSFERMOD]: m_AgentRegions[agentID] == scene. Returning true in " + scene.RegionInfo.RegionName); - m_AgentRegions.Remove(agentID); - return true; - } - - // Another region has claimed the agent - // - //m_log.Debug("[INVTRANSFERMOD]: last resort. Returning false in " + scene.RegionInfo.RegionName); - return false; - } - - public void ClientLoggedOut(UUID agentID, Scene scene) - { - if (m_AgentRegions.ContainsKey(agentID)) - m_AgentRegions.Remove(agentID); - } +// public bool NeedSceneCacheClear(UUID agentID, Scene scene) +// { +// if (!m_AgentRegions.ContainsKey(agentID)) +// { +// // Since we can get here two ways, we need to scan +// // the scenes here. This is somewhat more expensive +// // but helps avoid a nasty bug +// // +// +// foreach (Scene s in m_Scenelist) +// { +// ScenePresence presence; +// +// if (s.TryGetScenePresence(agentID, out presence)) +// { +// // If the agent is in this scene, then we +// // are being called twice in a single +// // teleport. This is wasteful of cycles +// // but harmless due to this 2nd level check +// // +// // If the agent is found in another scene +// // then the list wasn't current +// // +// // If the agent is totally unknown, then what +// // are we even doing here?? +// // +// if (s == scene) +// { +// //m_log.Debug("[INVTRANSFERMOD]: s == scene. Returning true in " + scene.RegionInfo.RegionName); +// return true; +// } +// else +// { +// //m_log.Debug("[INVTRANSFERMOD]: s != scene. Returning false in " + scene.RegionInfo.RegionName); +// return false; +// } +// } +// } +// //m_log.Debug("[INVTRANSFERMOD]: agent not in scene. Returning true in " + scene.RegionInfo.RegionName); +// return true; +// } +// +// // The agent is left in current Scene, so we must be +// // going to another instance +// // +// if (m_AgentRegions[agentID] == scene) +// { +// //m_log.Debug("[INVTRANSFERMOD]: m_AgentRegions[agentID] == scene. Returning true in " + scene.RegionInfo.RegionName); +// m_AgentRegions.Remove(agentID); +// return true; +// } +// +// // Another region has claimed the agent +// // +// //m_log.Debug("[INVTRANSFERMOD]: last resort. Returning false in " + scene.RegionInfo.RegionName); +// return false; +// } +// +// public void ClientLoggedOut(UUID agentID, Scene scene) +// { +// if (m_AgentRegions.ContainsKey(agentID)) +// m_AgentRegions.Remove(agentID); +// } /// /// diff --git a/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferModule.cs b/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferModule.cs index 90fe43034b..7498cfe73e 100644 --- a/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferModule.cs +++ b/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferModule.cs @@ -672,13 +672,14 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer sp.Reset(); } - // REFACTORING PROBLEM. Well, not a problem, but this method is HORRIBLE! - if (sp.Scene.NeedSceneCacheClear(sp.UUID)) - { - m_log.DebugFormat( - "[ENTITY TRANSFER MODULE]: User {0} is going to another region, profile cache removed", - sp.UUID); - } + // Commented pending deletion since this method no longer appears to do anything at all +// // REFACTORING PROBLEM. Well, not a problem, but this method is HORRIBLE! +// if (sp.Scene.NeedSceneCacheClear(sp.UUID)) +// { +// m_log.DebugFormat( +// "[ENTITY TRANSFER MODULE]: User {0} is going to another region, profile cache removed", +// sp.UUID); +// } m_entityTransferStateMachine.ResetFromTransit(sp.UUID); } @@ -1237,14 +1238,14 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer AgentHasMovedAway(agent, false); - // the user may change their profile information in other region, - // so the userinfo in UserProfileCache is not reliable any more, delete it - // REFACTORING PROBLEM. Well, not a problem, but this method is HORRIBLE! - if (agent.Scene.NeedSceneCacheClear(agent.UUID)) - { - m_log.DebugFormat( - "[ENTITY TRANSFER MODULE]: User {0} is going to another region", agent.UUID); - } +// // the user may change their profile information in other region, +// // so the userinfo in UserProfileCache is not reliable any more, delete it +// // REFACTORING PROBLEM. Well, not a problem, but this method is HORRIBLE! +// if (agent.Scene.NeedSceneCacheClear(agent.UUID)) +// { +// m_log.DebugFormat( +// "[ENTITY TRANSFER MODULE]: User {0} is going to another region", agent.UUID); +// } //m_log.Debug("AFTER CROSS"); //Scene.DumpChildrenSeeds(UUID); diff --git a/OpenSim/Region/CoreModules/Scripting/WorldComm/WorldCommModule.cs b/OpenSim/Region/CoreModules/Scripting/WorldComm/WorldCommModule.cs index 8358bc0c64..401ff6cdef 100644 --- a/OpenSim/Region/CoreModules/Scripting/WorldComm/WorldCommModule.cs +++ b/OpenSim/Region/CoreModules/Scripting/WorldComm/WorldCommModule.cs @@ -28,6 +28,7 @@ using System; using System.Collections; using System.Collections.Generic; +using System.Text.RegularExpressions; using Nini.Config; using OpenMetaverse; using OpenSim.Framework; @@ -170,12 +171,42 @@ namespace OpenSim.Region.CoreModules.Scripting.WorldComm /// UUID of the SceneObjectPart /// channel to listen on /// name to filter on - /// key to filter on (user given, could be totally faked) + /// + /// key to filter on (user given, could be totally faked) + /// /// msg to filter on /// number of the scripts handle - public int Listen(uint localID, UUID itemID, UUID hostID, int channel, string name, UUID id, string msg) + public int Listen(uint localID, UUID itemID, UUID hostID, int channel, + string name, UUID id, string msg) { - return m_listenerManager.AddListener(localID, itemID, hostID, channel, name, id, msg); + return m_listenerManager.AddListener(localID, itemID, hostID, + channel, name, id, msg); + } + + /// + /// Create a listen event callback with the specified filters. + /// The parameters localID,itemID are needed to uniquely identify + /// the script during 'peek' time. Parameter hostID is needed to + /// determine the position of the script. + /// + /// localID of the script engine + /// UUID of the script engine + /// UUID of the SceneObjectPart + /// channel to listen on + /// name to filter on + /// + /// key to filter on (user given, could be totally faked) + /// + /// msg to filter on + /// + /// Bitfield indicating which strings should be processed as regex. + /// + /// number of the scripts handle + public int Listen(uint localID, UUID itemID, UUID hostID, int channel, + string name, UUID id, string msg, int regexBitfield) + { + return m_listenerManager.AddListener(localID, itemID, hostID, + channel, name, id, msg, regexBitfield); } /// @@ -465,15 +496,25 @@ namespace OpenSim.Region.CoreModules.Scripting.WorldComm m_curlisteners = 0; } - public int AddListener(uint localID, UUID itemID, UUID hostID, int channel, string name, UUID id, string msg) + public int AddListener(uint localID, UUID itemID, UUID hostID, + int channel, string name, UUID id, string msg) + { + return AddListener(localID, itemID, hostID, channel, name, id, + msg, 0); + } + + public int AddListener(uint localID, UUID itemID, UUID hostID, + int channel, string name, UUID id, string msg, + int regexBitfield) { // do we already have a match on this particular filter event? - List coll = GetListeners(itemID, channel, name, id, msg); + List coll = GetListeners(itemID, channel, name, id, + msg); if (coll.Count > 0) { - // special case, called with same filter settings, return same handle - // (2008-05-02, tested on 1.21.1 server, still holds) + // special case, called with same filter settings, return same + // handle (2008-05-02, tested on 1.21.1 server, still holds) return coll[0].GetHandle(); } @@ -485,7 +526,9 @@ namespace OpenSim.Region.CoreModules.Scripting.WorldComm if (newHandle > 0) { - ListenerInfo li = new ListenerInfo(newHandle, localID, itemID, hostID, channel, name, id, msg); + ListenerInfo li = new ListenerInfo(newHandle, localID, + itemID, hostID, channel, name, id, msg, + regexBitfield); List listeners; if (!m_listeners.TryGetValue(channel,out listeners)) @@ -626,6 +669,22 @@ namespace OpenSim.Region.CoreModules.Scripting.WorldComm return -1; } + /// These are duplicated from ScriptBaseClass + /// http://opensimulator.org/mantis/view.php?id=6106#c21945 + #region Constants for the bitfield parameter of osListenRegex + + /// + /// process name parameter as regex + /// + public const int OS_LISTEN_REGEX_NAME = 0x1; + + /// + /// process message parameter as regex + /// + public const int OS_LISTEN_REGEX_MESSAGE = 0x2; + + #endregion + // Theres probably a more clever and efficient way to // do this, maybe with regex. // PM2008: Ha, one could even be smart and define a specialized Enumerator. @@ -651,7 +710,10 @@ namespace OpenSim.Region.CoreModules.Scripting.WorldComm { continue; } - if (li.GetName().Length > 0 && !li.GetName().Equals(name)) + if (li.GetName().Length > 0 && ( + ((li.RegexBitfield & OS_LISTEN_REGEX_NAME) != OS_LISTEN_REGEX_NAME && !li.GetName().Equals(name)) || + ((li.RegexBitfield & OS_LISTEN_REGEX_NAME) == OS_LISTEN_REGEX_NAME && !Regex.IsMatch(name, li.GetName())) + )) { continue; } @@ -659,7 +721,10 @@ namespace OpenSim.Region.CoreModules.Scripting.WorldComm { continue; } - if (li.GetMessage().Length > 0 && !li.GetMessage().Equals(msg)) + if (li.GetMessage().Length > 0 && ( + ((li.RegexBitfield & OS_LISTEN_REGEX_MESSAGE) != OS_LISTEN_REGEX_MESSAGE && !li.GetMessage().Equals(msg)) || + ((li.RegexBitfield & OS_LISTEN_REGEX_MESSAGE) == OS_LISTEN_REGEX_MESSAGE && !Regex.IsMatch(msg, li.GetMessage())) + )) { continue; } @@ -692,10 +757,13 @@ namespace OpenSim.Region.CoreModules.Scripting.WorldComm { int idx = 0; Object[] item = new Object[6]; + int dataItemLength = 6; while (idx < data.Length) { - Array.Copy(data, idx, item, 0, 6); + dataItemLength = (idx + 7 == data.Length || (idx + 7 < data.Length && data[idx + 7] is bool)) ? 7 : 6; + item = new Object[dataItemLength]; + Array.Copy(data, idx, item, 0, dataItemLength); ListenerInfo info = ListenerInfo.FromData(localID, itemID, hostID, item); @@ -707,12 +775,12 @@ namespace OpenSim.Region.CoreModules.Scripting.WorldComm m_listeners[(int)item[2]].Add(info); } - idx+=6; + idx+=dataItemLength; } } } - public class ListenerInfo: IWorldCommListenerInfo + public class ListenerInfo : IWorldCommListenerInfo { private bool m_active; // Listener is active or not private int m_handle; // Assigned handle of this listener @@ -726,16 +794,29 @@ namespace OpenSim.Region.CoreModules.Scripting.WorldComm public ListenerInfo(int handle, uint localID, UUID ItemID, UUID hostID, int channel, string name, UUID id, string message) { - Initialise(handle, localID, ItemID, hostID, channel, name, id, message); + Initialise(handle, localID, ItemID, hostID, channel, name, id, + message, 0); + } + + public ListenerInfo(int handle, uint localID, UUID ItemID, + UUID hostID, int channel, string name, UUID id, + string message, int regexBitfield) + { + Initialise(handle, localID, ItemID, hostID, channel, name, id, + message, regexBitfield); } public ListenerInfo(ListenerInfo li, string name, UUID id, string message) { - Initialise(li.m_handle, li.m_localID, li.m_itemID, li.m_hostID, li.m_channel, name, id, message); + Initialise(li.m_handle, li.m_localID, li.m_itemID, li.m_hostID, li.m_channel, name, id, message, 0); } - private void Initialise(int handle, uint localID, UUID ItemID, UUID hostID, int channel, string name, - UUID id, string message) + public ListenerInfo(ListenerInfo li, string name, UUID id, string message, int regexBitfield) + { + Initialise(li.m_handle, li.m_localID, li.m_itemID, li.m_hostID, li.m_channel, name, id, message, regexBitfield); + } + + private void Initialise(int handle, uint localID, UUID ItemID, UUID hostID, int channel, string name, UUID id, string message, int regexBitfield) { m_active = true; m_handle = handle; @@ -746,11 +827,12 @@ namespace OpenSim.Region.CoreModules.Scripting.WorldComm m_name = name; m_id = id; m_message = message; + RegexBitfield = regexBitfield; } public Object[] GetSerializationData() { - Object[] data = new Object[6]; + Object[] data = new Object[7]; data[0] = m_active; data[1] = m_handle; @@ -758,16 +840,19 @@ namespace OpenSim.Region.CoreModules.Scripting.WorldComm data[3] = m_name; data[4] = m_id; data[5] = m_message; + data[6] = RegexBitfield; return data; } public static ListenerInfo FromData(uint localID, UUID ItemID, UUID hostID, Object[] data) { - ListenerInfo linfo = new ListenerInfo((int)data[1], localID, - ItemID, hostID, (int)data[2], (string)data[3], - (UUID)data[4], (string)data[5]); - linfo.m_active=(bool)data[0]; + ListenerInfo linfo = new ListenerInfo((int)data[1], localID, ItemID, hostID, (int)data[2], (string)data[3], (UUID)data[4], (string)data[5]); + linfo.m_active = (bool)data[0]; + if (data.Length >= 7) + { + linfo.RegexBitfield = (int)data[6]; + } return linfo; } @@ -826,5 +911,7 @@ namespace OpenSim.Region.CoreModules.Scripting.WorldComm { return m_id; } + + public int RegexBitfield { get; private set; } } } diff --git a/OpenSim/Region/CoreModules/World/Archiver/ArchiveScenesGroup.cs b/OpenSim/Region/CoreModules/World/Archiver/ArchiveScenesGroup.cs index a66ed88ad1..d8dace20c8 100644 --- a/OpenSim/Region/CoreModules/World/Archiver/ArchiveScenesGroup.cs +++ b/OpenSim/Region/CoreModules/World/Archiver/ArchiveScenesGroup.cs @@ -110,7 +110,7 @@ namespace OpenSim.Region.CoreModules.World.Archiver lastX = (lastX == null) ? curLastX : (lastX > curLastX) ? lastX : curLastX; } - Rect = new Rectangle((int)firstX, (int)firstY, (int)(lastY - firstY + 1), (int)(lastX - firstX + 1)); + Rect = new Rectangle((int)firstX, (int)firstY, (int)(lastX - firstX + 1), (int)(lastY - firstY + 1)); // Calculate the subdirectory in which each region will be stored in the archive diff --git a/OpenSim/Region/CoreModules/World/Archiver/ArchiverModule.cs b/OpenSim/Region/CoreModules/World/Archiver/ArchiverModule.cs index 2a87dc2a5f..970487a3d4 100644 --- a/OpenSim/Region/CoreModules/World/Archiver/ArchiverModule.cs +++ b/OpenSim/Region/CoreModules/World/Archiver/ArchiverModule.cs @@ -32,6 +32,8 @@ using System.Reflection; using log4net; using NDesk.Options; using Nini.Config; +using OpenSim.Framework; +using OpenSim.Framework.Console; using OpenSim.Region.Framework.Interfaces; using OpenSim.Region.Framework.Scenes; @@ -117,7 +119,7 @@ namespace OpenSim.Region.CoreModules.World.Archiver // // foreach (string param in mainParams) // m_log.DebugFormat("GOT PARAM [{0}]", param); - + if (mainParams.Count > 2) { DearchiveRegion(mainParams[2], mergeOar, skipAssets, Guid.Empty); @@ -150,14 +152,16 @@ namespace OpenSim.Region.CoreModules.World.Archiver List mainParams = ops.Parse(cmdparams); + string path; if (mainParams.Count > 2) - { - ArchiveRegion(mainParams[2], options); - } + path = mainParams[2]; else - { - ArchiveRegion(DEFAULT_OAR_BACKUP_FILENAME, options); - } + path = DEFAULT_OAR_BACKUP_FILENAME; + + if (!ConsoleUtil.CheckFileDoesNotExist(MainConsole.Instance, path)) + return; + + ArchiveRegion(path, options); } public void ArchiveRegion(string savePath, Dictionary options) diff --git a/OpenSim/Region/CoreModules/World/Objects/Commands/ObjectCommandsModule.cs b/OpenSim/Region/CoreModules/World/Objects/Commands/ObjectCommandsModule.cs index 87241e1559..b2c9bce7e5 100644 --- a/OpenSim/Region/CoreModules/World/Objects/Commands/ObjectCommandsModule.cs +++ b/OpenSim/Region/CoreModules/World/Objects/Commands/ObjectCommandsModule.cs @@ -27,10 +27,12 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Reflection; using System.Text; using System.Text.RegularExpressions; +using System.Xml; using log4net; using Mono.Addins; using NDesk.Options; @@ -41,6 +43,7 @@ using OpenSim.Framework.Console; using OpenSim.Framework.Monitoring; using OpenSim.Region.Framework.Interfaces; using OpenSim.Region.Framework.Scenes; +using OpenSim.Region.Framework.Scenes.Serialization; namespace OpenSim.Region.CoreModules.World.Objects.Commands { @@ -96,9 +99,9 @@ namespace OpenSim.Region.CoreModules.World.Objects.Commands HandleDeleteObject); m_console.Commands.AddCommand( - "Objects", false, "delete object uuid", - "delete object uuid ", - "Delete a scene object by uuid", + "Objects", false, "delete object id", + "delete object id ", + "Delete a scene object by uuid or localID", HandleDeleteObject); m_console.Commands.AddCommand( @@ -122,28 +125,18 @@ namespace OpenSim.Region.CoreModules.World.Objects.Commands "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", + ConsoleUtil.CoordHelp, HandleDeleteObject); m_console.Commands.AddCommand( "Objects", false, - "show object uuid", - "show object uuid [--full] ", - "Show details of a scene object with the given UUID", + "show object id", + "show object id [--full] ", + "Show details of a scene object with the given UUID or localID", "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); + HandleShowObjectById); m_console.Commands.AddCommand( "Objects", @@ -164,25 +157,15 @@ namespace OpenSim.Region.CoreModules.World.Objects.Commands "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", + + ConsoleUtil.CoordHelp, HandleShowObjectByPos); m_console.Commands.AddCommand( "Objects", false, - "show part uuid", - "show part uuid ", - "Show details of a scene object parts with the given UUID", HandleShowPartByUuid); + "show part id", + "show part id ", + "Show details of a scene object part with the given UUID or localID", HandleShowPartById); m_console.Commands.AddCommand( "Objects", @@ -190,7 +173,7 @@ namespace OpenSim.Region.CoreModules.World.Objects.Commands "show part name", "show part name [--regex] ", "Show details of scene object parts with the given name.", - "If --regex is specified then the name is treatead as a regular expression", + "If --regex is specified then the name is treated as a regular expression", HandleShowPartByName); m_console.Commands.AddCommand( @@ -199,18 +182,19 @@ namespace OpenSim.Region.CoreModules.World.Objects.Commands "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", + ConsoleUtil.CoordHelp, HandleShowPartByPos); + + m_console.Commands.AddCommand( + "Objects", + false, + "dump object id", + "dump object id ", + "Dump the formatted serialization of the given object to the file .xml", + "e.g. dump object uuid c1ed6809-cc24-4061-a4c2-93082a2d1f1d will dump serialization to c1ed6809-cc24-4061-a4c2-93082a2d1f1d.xml\n" + + "To locate the UUID or localID in the first place, you need to use the other show object commands.\n" + + "If a local ID is given then the filename used is still that for the UUID", + HandleDumpObjectById); } public void RemoveRegion(Scene scene) @@ -265,7 +249,7 @@ namespace OpenSim.Region.CoreModules.World.Objects.Commands m_console.OutputFormat(sb.ToString()); } - private void HandleShowObjectByUuid(string module, string[] cmdparams) + private void HandleShowObjectById(string module, string[] cmdparams) { if (!(m_console.ConsoleScene == null || m_console.ConsoleScene == m_scene)) return; @@ -281,14 +265,17 @@ namespace OpenSim.Region.CoreModules.World.Objects.Commands return; } - UUID objectUuid; - if (!UUID.TryParse(mainParams[3], out objectUuid)) - { - m_console.OutputFormat("{0} is not a valid uuid", mainParams[3]); + UUID uuid; + uint localId; + if (!ConsoleUtil.TryParseConsoleId(m_console, mainParams[3], out uuid, out localId)) return; - } - SceneObjectGroup so = m_scene.GetSceneObjectGroup(objectUuid); + SceneObjectGroup so; + + if (localId != ConsoleUtil.LocalIdNotFound) + so = m_scene.GetSceneObjectGroup(localId); + else + so = m_scene.GetSceneObjectGroup(uuid); if (so == null) { @@ -365,7 +352,7 @@ namespace OpenSim.Region.CoreModules.World.Objects.Commands OutputSogsToConsole(searchPredicate, showFull); } - private void HandleShowPartByUuid(string module, string[] cmdparams) + private void HandleShowPartById(string module, string[] cmdparams) { if (!(m_console.ConsoleScene == null || m_console.ConsoleScene == m_scene)) return; @@ -378,18 +365,20 @@ namespace OpenSim.Region.CoreModules.World.Objects.Commands if (mainParams.Count < 4) { - m_console.OutputFormat("Usage: show part uuid [--full] "); + m_console.OutputFormat("Usage: show part id [--full] "); return; } UUID objectUuid; - if (!UUID.TryParse(mainParams[3], out objectUuid)) - { - m_console.OutputFormat("{0} is not a valid uuid", mainParams[3]); + uint localId; + if (!ConsoleUtil.TryParseConsoleId(m_console, mainParams[3], out objectUuid, out localId)) return; - } - SceneObjectPart sop = m_scene.GetSceneObjectPart(objectUuid); + SceneObjectPart sop; + if (localId == ConsoleUtil.LocalIdNotFound) + sop = m_scene.GetSceneObjectPart(objectUuid); + else + sop = m_scene.GetSceneObjectPart(localId); if (sop == null) { @@ -477,6 +466,51 @@ namespace OpenSim.Region.CoreModules.World.Objects.Commands OutputSopsToConsole(searchPredicate, true); } + private void HandleDumpObjectById(string module, string[] cmdparams) + { + if (!(m_console.ConsoleScene == null || m_console.ConsoleScene == m_scene)) + return; + + if (cmdparams.Length < 4) + { + m_console.OutputFormat("Usage: dump object id "); + return; + } + + UUID objectUuid; + uint localId; + if (!ConsoleUtil.TryParseConsoleId(m_console, cmdparams[3], out objectUuid, out localId)) + return; + + SceneObjectGroup so; + if (localId == ConsoleUtil.LocalIdNotFound) + so = m_scene.GetSceneObjectGroup(objectUuid); + else + so = m_scene.GetSceneObjectGroup(localId); + + if (so == null) + { +// m_console.OutputFormat("No part found with uuid {0}", objectUuid); + return; + } + + // In case we found it via local ID. + objectUuid = so.UUID; + + string fileName = string.Format("{0}.xml", objectUuid); + + if (!ConsoleUtil.CheckFileDoesNotExist(m_console, fileName)) + return; + + using (XmlTextWriter xtw = new XmlTextWriter(fileName, Encoding.UTF8)) + { + xtw.Formatting = Formatting.Indented; + SceneObjectSerializer.ToOriginalXmlFormat(so, xtw, true); + } + + m_console.OutputFormat("Object dumped to file {0}", fileName); + } + /// /// Append a scene object report to an input StringBuilder /// @@ -641,19 +675,24 @@ namespace OpenSim.Region.CoreModules.World.Objects.Commands break; - case "uuid": - if (!UUID.TryParse(o, out match)) + case "id": + UUID uuid; + uint localId; + if (!ConsoleUtil.TryParseConsoleId(m_console, o, out uuid, out localId)) return; requireConfirmation = false; deletes = new List(); - - m_scene.ForEachSOG(delegate (SceneObjectGroup g) - { - if (g.UUID == match && !g.IsAttachment) - deletes.Add(g); - }); - + + SceneObjectGroup so; + if (localId == ConsoleUtil.LocalIdNotFound) + so = m_scene.GetSceneObjectGroup(uuid); + else + so = m_scene.GetSceneObjectGroup(localId); + + if (!so.IsAttachment) + deletes.Add(so); + // if (deletes.Count == 0) // m_console.OutputFormat("No objects were found with uuid {0}", match); diff --git a/OpenSim/Region/Framework/Interfaces/IWorldComm.cs b/OpenSim/Region/Framework/Interfaces/IWorldComm.cs index 4e74781b9b..d76a0d755d 100644 --- a/OpenSim/Region/Framework/Interfaces/IWorldComm.cs +++ b/OpenSim/Region/Framework/Interfaces/IWorldComm.cs @@ -45,6 +45,13 @@ namespace OpenSim.Region.Framework.Interfaces void Deactivate(); void Activate(); UUID GetID(); + + /// + /// Bitfield indicating which strings should be processed as regex. + /// 1 corresponds to IWorldCommListenerInfo::GetName() + /// 2 corresponds to IWorldCommListenerInfo::GetMessage() + /// + int RegexBitfield { get; } } public interface IWorldComm @@ -60,7 +67,7 @@ namespace OpenSim.Region.Framework.Interfaces /// the script during 'peek' time. Parameter hostID is needed to /// determine the position of the script. /// - /// localID of the script engine + /// localID of the script engine /// UUID of the script engine /// UUID of the SceneObjectPart /// channel to listen on @@ -70,6 +77,23 @@ namespace OpenSim.Region.Framework.Interfaces /// number of the scripts handle int Listen(uint LocalID, UUID itemID, UUID hostID, int channel, string name, UUID id, string msg); + /// + /// Create a listen event callback with the specified filters. + /// The parameters localID,itemID are needed to uniquely identify + /// the script during 'peek' time. Parameter hostID is needed to + /// determine the position of the script. + /// + /// localID of the script engine + /// UUID of the script engine + /// UUID of the SceneObjectPart + /// channel to listen on + /// name to filter on + /// key to filter on (user given, could be totally faked) + /// msg to filter on + /// Bitfield indicating which strings should be processed as regex. + /// number of the scripts handle + int Listen(uint LocalID, UUID itemID, UUID hostID, int channel, string name, UUID id, string msg, int regexBitfield); + /// /// This method scans over the objects which registered an interest in listen callbacks. /// For everyone it finds, it checks if it fits the given filter. If it does, then diff --git a/OpenSim/Region/Framework/Scenes/Animation/AnimationSet.cs b/OpenSim/Region/Framework/Scenes/Animation/AnimationSet.cs index ad421eeedb..3d8e8be795 100644 --- a/OpenSim/Region/Framework/Scenes/Animation/AnimationSet.cs +++ b/OpenSim/Region/Framework/Scenes/Animation/AnimationSet.cs @@ -41,12 +41,13 @@ namespace OpenSim.Region.Framework.Scenes.Animation { // private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + private OpenSim.Framework.Animation m_implicitDefaultAnimation = new OpenSim.Framework.Animation(); private OpenSim.Framework.Animation m_defaultAnimation = new OpenSim.Framework.Animation(); private List m_animations = new List(); - public OpenSim.Framework.Animation DefaultAnimation + public OpenSim.Framework.Animation ImplicitDefaultAnimation { - get { return m_defaultAnimation; } + get { return m_implicitDefaultAnimation; } } public AnimationSet() @@ -119,6 +120,7 @@ namespace OpenSim.Region.Framework.Scenes.Animation if (m_defaultAnimation.AnimID != animID) { m_defaultAnimation = new OpenSim.Framework.Animation(animID, sequenceNum, objectID); + m_implicitDefaultAnimation = m_defaultAnimation; return true; } return false; diff --git a/OpenSim/Region/Framework/Scenes/Scene.PacketHandlers.cs b/OpenSim/Region/Framework/Scenes/Scene.PacketHandlers.cs index 6e53951003..df432718d4 100644 --- a/OpenSim/Region/Framework/Scenes/Scene.PacketHandlers.cs +++ b/OpenSim/Region/Framework/Scenes/Scene.PacketHandlers.cs @@ -38,7 +38,18 @@ namespace OpenSim.Region.Framework.Scenes { public partial class Scene { - + /// + /// Send chat to listeners. + /// + /// + /// /param> + /// + /// + /// + /// + /// + /// + /// protected void SimChat(byte[] message, ChatTypeEnum type, int channel, Vector3 fromPos, string fromName, UUID fromID, UUID targetID, bool fromAgent, bool broadcast) { @@ -66,6 +77,10 @@ namespace OpenSim.Region.Framework.Scenes args.From = fromName; args.TargetUUID = targetID; +// m_log.DebugFormat( +// "[SCENE]: Sending message {0} on channel {1}, type {2} from {3}, broadcast {4}", +// args.Message.Replace("\n", "\\n"), args.Channel, args.Type, fromName, broadcast); + if (broadcast) EventManager.TriggerOnChatBroadcast(this, args); else diff --git a/OpenSim/Region/Framework/Scenes/Scene.cs b/OpenSim/Region/Framework/Scenes/Scene.cs index 5f45529ca8..9c61fe7da8 100644 --- a/OpenSim/Region/Framework/Scenes/Scene.cs +++ b/OpenSim/Region/Framework/Scenes/Scene.cs @@ -1267,6 +1267,12 @@ namespace OpenSim.Region.Framework.Scenes // This is the method that shuts down the scene. public override void Close() { + if (m_shuttingDown) + { + m_log.WarnFormat("[SCENE]: Ignoring close request because already closing {0}", Name); + return; + } + m_log.InfoFormat("[SCENE]: Closing down the single simulator: {0}", RegionInfo.RegionName); StatsReporter.Close(); @@ -1310,6 +1316,14 @@ namespace OpenSim.Region.Framework.Scenes m_sceneGraph.Close(); + if (!GridService.DeregisterRegion(RegionInfo.RegionID)) + m_log.WarnFormat("[SCENE]: Deregister from grid failed for region {0}", Name); + + base.Close(); + + // XEngine currently listens to the EventManager.OnShutdown event to trigger script stop and persistence. + // Therefore. we must dispose of the PhysicsScene after this to prevent a window where script code can + // attempt to reference a null or disposed physics scene. if (PhysicsScene != null) { PhysicsScene phys = PhysicsScene; @@ -1318,12 +1332,6 @@ namespace OpenSim.Region.Framework.Scenes phys.Dispose(); phys = null; } - - if (!GridService.DeregisterRegion(RegionInfo.RegionID)) - m_log.WarnFormat("[SCENE]: Deregister from grid failed for region {0}", Name); - - // call the base class Close method. - base.Close(); } /// @@ -3427,9 +3435,10 @@ namespace OpenSim.Region.Framework.Scenes 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); +// // REFACTORING PROBLEM -- well not really a problem, but just to point out that whatever +// // this method is doing is HORRIBLE!!! + // Commented pending deletion since this method no longer appears to do anything at all +// avatar.Scene.NeedSceneCacheClear(avatar.UUID); if (closeChildAgents && !isChildAgent) { @@ -4688,12 +4697,23 @@ namespace OpenSim.Region.Framework.Scenes /// Get a group via its UUID /// /// - /// null if no group with that name exists + /// null if no group with that id exists public SceneObjectGroup GetSceneObjectGroup(UUID fullID) { return m_sceneGraph.GetSceneObjectGroup(fullID); } + /// + /// Get a group via its local ID + /// + /// This will only return a group if the local ID matches a root part + /// + /// null if no group with that id exists + public SceneObjectGroup GetSceneObjectGroup(uint localID) + { + return m_sceneGraph.GetSceneObjectGroup(localID); + } + /// /// Get a group by name from the scene (will return the first /// found, if there are more than one prim with the same name) @@ -4854,14 +4874,15 @@ namespace OpenSim.Region.Framework.Scenes client.SendRegionHandle(regionID, handle); } - public bool NeedSceneCacheClear(UUID agentID) - { - IInventoryTransferModule inv = RequestModuleInterface(); - if (inv == null) - return true; - - return inv.NeedSceneCacheClear(agentID, this); - } +// Commented pending deletion since this method no longer appears to do anything at all +// public bool NeedSceneCacheClear(UUID agentID) +// { +// IInventoryTransferModule inv = RequestModuleInterface(); +// if (inv == null) +// return true; +// +// return inv.NeedSceneCacheClear(agentID, this); +// } public void CleanTempObjects() { diff --git a/OpenSim/Region/Framework/Scenes/SceneGraph.cs b/OpenSim/Region/Framework/Scenes/SceneGraph.cs index 209a770396..a4383fd23b 100644 --- a/OpenSim/Region/Framework/Scenes/SceneGraph.cs +++ b/OpenSim/Region/Framework/Scenes/SceneGraph.cs @@ -982,6 +982,30 @@ namespace OpenSim.Region.Framework.Scenes return null; } + /// + /// Get a group in the scene + /// + /// + /// This will only return a group if the local ID matches the root part, not other parts. + /// + /// Local id of the root part of the group + /// null if no such group was found + protected internal SceneObjectGroup GetSceneObjectGroup(uint localID) + { + lock (SceneObjectGroupsByLocalPartID) + { + if (SceneObjectGroupsByLocalPartID.ContainsKey(localID)) + { + SceneObjectGroup so = SceneObjectGroupsByLocalPartID[localID]; + + if (so.LocalId == localID) + return so; + } + } + + return null; + } + /// /// Get a group by name from the scene (will return the first /// found, if there are more than one prim with the same name) diff --git a/OpenSim/Region/Framework/Scenes/ScenePresence.cs b/OpenSim/Region/Framework/Scenes/ScenePresence.cs index 1222ac6bcf..b6d0a3b661 100644 --- a/OpenSim/Region/Framework/Scenes/ScenePresence.cs +++ b/OpenSim/Region/Framework/Scenes/ScenePresence.cs @@ -68,7 +68,7 @@ namespace OpenSim.Region.Framework.Scenes public ScriptControlled eventControls; } - public delegate void SendCourseLocationsMethod(UUID scene, ScenePresence presence, List coarseLocations, List avatarUUIDs); + public delegate void SendCoarseLocationsMethod(UUID scene, ScenePresence presence, List coarseLocations, List avatarUUIDs); public class ScenePresence : EntityBase, IScenePresence { @@ -186,7 +186,7 @@ namespace OpenSim.Region.Framework.Scenes /// public bool SitGround { get; private set; } - private SendCourseLocationsMethod m_sendCourseLocationsMethod; + private SendCoarseLocationsMethod m_sendCoarseLocationsMethod; //private Vector3 m_requestedSitOffset = new Vector3(); @@ -695,7 +695,7 @@ namespace OpenSim.Region.Framework.Scenes AttachmentsSyncLock = new Object(); AllowMovement = true; IsChildAgent = true; - m_sendCourseLocationsMethod = SendCoarseLocationsDefault; + m_sendCoarseLocationsMethod = SendCoarseLocationsDefault; Animator = new ScenePresenceAnimator(this); PresenceType = type; DrawDistance = world.DefaultDrawDistance; @@ -2432,17 +2432,17 @@ namespace OpenSim.Region.Framework.Scenes public void SendCoarseLocations(List coarseLocations, List avatarUUIDs) { - SendCourseLocationsMethod d = m_sendCourseLocationsMethod; + SendCoarseLocationsMethod d = m_sendCoarseLocationsMethod; if (d != null) { d.Invoke(m_scene.RegionInfo.originRegionID, this, coarseLocations, avatarUUIDs); } } - public void SetSendCourseLocationMethod(SendCourseLocationsMethod d) + public void SetSendCoarseLocationMethod(SendCoarseLocationsMethod d) { if (d != null) - m_sendCourseLocationsMethod = d; + m_sendCoarseLocationsMethod = d; } public void SendCoarseLocationsDefault(UUID sceneId, ScenePresence p, List coarseLocations, List avatarUUIDs) @@ -2646,7 +2646,7 @@ namespace OpenSim.Region.Framework.Scenes #region Significant Movement Method /// - /// This checks for a significant movement and sends a courselocationchange update + /// This checks for a significant movement and sends a coarselocationchange update /// protected void CheckForSignificantMovement() { diff --git a/OpenSim/Region/Framework/Scenes/SimStatsReporter.cs b/OpenSim/Region/Framework/Scenes/SimStatsReporter.cs index 2addb5bab8..b9d615e804 100644 --- a/OpenSim/Region/Framework/Scenes/SimStatsReporter.cs +++ b/OpenSim/Region/Framework/Scenes/SimStatsReporter.cs @@ -245,11 +245,13 @@ namespace OpenSim.Region.Framework.Scenes = new Stat( "SlowFrames", "Slow Frames", + "Number of frames where frame time has been significantly longer than the desired frame time.", " frames", "scene", m_scene.Name, - StatVerbosity.Info, - "Number of frames where frame time has been significantly longer than the desired frame time."); + StatType.Push, + null, + StatVerbosity.Info); StatsManager.RegisterStat(SlowFramesStat); } diff --git a/OpenSim/Region/Framework/Scenes/Tests/SceneTests.cs b/OpenSim/Region/Framework/Scenes/Tests/SceneTests.cs index d722a0990a..ac3da1e2cc 100644 --- a/OpenSim/Region/Framework/Scenes/Tests/SceneTests.cs +++ b/OpenSim/Region/Framework/Scenes/Tests/SceneTests.cs @@ -65,5 +65,22 @@ namespace OpenSim.Region.Framework.Scenes.Tests Assert.That(scene.Frame, Is.EqualTo(1)); } + + [Test] + public void TestShutdownScene() + { + TestHelpers.InMethod(); + + Scene scene = new SceneHelpers().SetupScene(); + scene.Close(); + + Assert.That(scene.ShuttingDown, Is.True); + Assert.That(scene.Active, Is.False); + + // Trying to update a shutdown scene should result in no update + scene.Update(1); + + Assert.That(scene.Frame, Is.EqualTo(0)); + } } } \ No newline at end of file diff --git a/OpenSim/Region/OptionalModules/Agent/InternetRelayClientView/Server/IRCClientView.cs b/OpenSim/Region/OptionalModules/Agent/InternetRelayClientView/Server/IRCClientView.cs index e93bd7cb1d..781539acc1 100644 --- a/OpenSim/Region/OptionalModules/Agent/InternetRelayClientView/Server/IRCClientView.cs +++ b/OpenSim/Region/OptionalModules/Agent/InternetRelayClientView/Server/IRCClientView.cs @@ -954,7 +954,8 @@ namespace OpenSim.Region.OptionalModules.Agent.InternetRelayClientView.Server } - public void SendChatMessage(string message, byte type, Vector3 fromPos, string fromName, UUID fromAgentID, byte source, byte audible) + public void SendChatMessage( + string message, byte type, Vector3 fromPos, string fromName, UUID fromAgentID, UUID ownerID, byte source, byte audible) { if (audible > 0 && message.Length > 0) IRC_SendChannelPrivmsg(fromName, message); diff --git a/OpenSim/Region/OptionalModules/Asset/AssetInfoModule.cs b/OpenSim/Region/OptionalModules/Asset/AssetInfoModule.cs index 41ec14f224..7639c6ce45 100644 --- a/OpenSim/Region/OptionalModules/Asset/AssetInfoModule.cs +++ b/OpenSim/Region/OptionalModules/Asset/AssetInfoModule.cs @@ -127,6 +127,9 @@ namespace OpenSim.Region.OptionalModules.Asset } string fileName = rawAssetId; + + if (!ConsoleUtil.CheckFileDoesNotExist(MainConsole.Instance, fileName)) + return; using (FileStream fs = new FileStream(fileName, FileMode.CreateNew)) { diff --git a/OpenSim/Region/OptionalModules/Avatar/Concierge/ConciergeModule.cs b/OpenSim/Region/OptionalModules/Avatar/Concierge/ConciergeModule.cs index e22618dff3..5c3be29e9a 100644 --- a/OpenSim/Region/OptionalModules/Avatar/Concierge/ConciergeModule.cs +++ b/OpenSim/Region/OptionalModules/Avatar/Concierge/ConciergeModule.cs @@ -546,8 +546,9 @@ namespace OpenSim.Region.OptionalModules.Avatar.Concierge c.SenderUUID = UUID.Zero; c.Scene = agent.Scene; - agent.ControllingClient.SendChatMessage(msg, (byte) ChatTypeEnum.Say, PosOfGod, m_whoami, UUID.Zero, - (byte)ChatSourceType.Object, (byte)ChatAudibleLevel.Fully); + agent.ControllingClient.SendChatMessage( + msg, (byte) ChatTypeEnum.Say, PosOfGod, m_whoami, UUID.Zero, UUID.Zero, + (byte)ChatSourceType.Object, (byte)ChatAudibleLevel.Fully); } private static void checkStringParameters(XmlRpcRequest request, string[] param) diff --git a/OpenSim/Region/OptionalModules/World/NPC/NPCAvatar.cs b/OpenSim/Region/OptionalModules/World/NPC/NPCAvatar.cs index ffd4222935..5ea2bcd908 100644 --- a/OpenSim/Region/OptionalModules/World/NPC/NPCAvatar.cs +++ b/OpenSim/Region/OptionalModules/World/NPC/NPCAvatar.cs @@ -603,13 +603,15 @@ namespace OpenSim.Region.OptionalModules.World.NPC { } - public virtual void SendChatMessage(string message, byte type, Vector3 fromPos, string fromName, - UUID fromAgentID, byte source, byte audible) + public virtual void SendChatMessage( + string message, byte type, Vector3 fromPos, string fromName, + UUID fromAgentID, UUID ownerID, byte source, byte audible) { } - public virtual void SendChatMessage(byte[] message, byte type, Vector3 fromPos, string fromName, - UUID fromAgentID, byte source, byte audible) + public virtual void SendChatMessage( + byte[] message, byte type, Vector3 fromPos, string fromName, + UUID fromAgentID, UUID ownerID, byte source, byte audible) { } diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs b/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs index 623ac8ff16..a041ba8e0a 100644 --- a/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs @@ -105,7 +105,7 @@ public class BSCharacter : BSPhysObject shapeData.Position = _position; shapeData.Rotation = _orientation; shapeData.Velocity = _velocity; - shapeData.Size = Scale; + shapeData.Size = Scale; // capsule is a native shape but scale is not just <1,1,1> shapeData.Scale = Scale; shapeData.Mass = _mass; shapeData.Buoyancy = _buoyancy; @@ -144,7 +144,9 @@ public class BSCharacter : BSPhysObject ForcePosition = _position; // Set the velocity and compute the proper friction ForceVelocity = _velocity; + BulletSimAPI.SetRestitution2(BSBody.ptr, PhysicsScene.Params.avatarRestitution); + BulletSimAPI.SetMargin2(BSShape.ptr, PhysicsScene.Params.collisionMargin); BulletSimAPI.SetLocalScaling2(BSShape.ptr, Scale); BulletSimAPI.SetContactProcessingThreshold2(BSBody.ptr, PhysicsScene.Params.contactProcessingThreshold); if (PhysicsScene.Params.ccdMotionThreshold > 0f) @@ -156,16 +158,20 @@ public class BSCharacter : BSPhysObject OMV.Vector3 localInertia = BulletSimAPI.CalculateLocalInertia2(BSShape.ptr, MassRaw); BulletSimAPI.SetMassProps2(BSBody.ptr, MassRaw, localInertia); + // Make so capsule does not fall over + BulletSimAPI.SetAngularFactorV2(BSBody.ptr, OMV.Vector3.Zero); + BulletSimAPI.AddToCollisionFlags2(BSBody.ptr, CollisionFlags.CF_CHARACTER_OBJECT); BulletSimAPI.AddObjectToWorld2(PhysicsScene.World.ptr, BSBody.ptr); - BulletSimAPI.ForceActivationState2(BSBody.ptr, ActivationState.ACTIVE_TAG); + // BulletSimAPI.ForceActivationState2(BSBody.ptr, ActivationState.ACTIVE_TAG); + BulletSimAPI.ForceActivationState2(BSBody.ptr, ActivationState.DISABLE_DEACTIVATION); BulletSimAPI.UpdateSingleAabb2(PhysicsScene.World.ptr, BSBody.ptr); // Do this after the object has been added to the world BulletSimAPI.SetCollisionFilterMask2(BSBody.ptr, - (uint)CollisionFilterGroups.AvatarFilter, + (uint)CollisionFilterGroups.AvatarFilter, (uint)CollisionFilterGroups.AvatarMask); } @@ -175,11 +181,13 @@ public class BSCharacter : BSPhysObject } // No one calls this method so I don't know what it could possibly mean public override bool Stopped { get { return false; } } + public override OMV.Vector3 Size { get { // Avatar capsule size is kept in the scale parameter. - return _size; + // return _size; + return new OMV.Vector3(Scale.X * 2f, Scale.Y * 2f, Scale.Z); } set { @@ -199,7 +207,9 @@ public class BSCharacter : BSPhysObject } } + public override OMV.Vector3 Scale { get; set; } + public override PrimitiveBaseShape Shape { set { BaseShape = value; } @@ -264,12 +274,12 @@ public class BSCharacter : BSPhysObject // 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. + // Check for being below terrain or on water. // Returns 'true' of the position was made sane by some action. private bool PositionSanityCheck() { bool ret = false; - + // If below the ground, move the avatar up float terrainHeight = PhysicsScene.TerrainManager.GetTerrainHeightAtXYZ(_position); if (Position.Z < terrainHeight) @@ -335,7 +345,7 @@ public class BSCharacter : BSPhysObject } // Avatars don't do vehicles - public override int VehicleType { get { return 0; } set { return; } } + public override int VehicleType { get { return (int)Vehicle.TYPE_NONE; } set { return; } } public override void VehicleFloatParam(int param, float value) { } public override void VehicleVectorParam(int param, OMV.Vector3 value) {} public override void VehicleRotationParam(int param, OMV.Quaternion rotation) { } @@ -413,7 +423,7 @@ public class BSCharacter : BSPhysObject }); } } - // Go directly to Bullet to get/set the value. + // Go directly to Bullet to get/set the value. public override OMV.Quaternion ForceOrientation { get @@ -478,7 +488,7 @@ public class BSCharacter : BSPhysObject set { _collidingObj = value; } } public override bool FloatOnWater { - set { + set { _floatOnWater = value; PhysicsScene.TaintedObject("BSCharacter.setFloatOnWater", delegate() { @@ -588,9 +598,8 @@ public class BSCharacter : BSPhysObject newScale.X = PhysicsScene.Params.avatarCapsuleRadius; newScale.Y = PhysicsScene.Params.avatarCapsuleRadius; - // From the total height, remote the capsule half spheres that are at each end - newScale.Z = (size.Z * 2f) - Math.Min(newScale.X, newScale.Y); - // newScale.Z = (size.Z * 2f); + // From the total height, remove the capsule half spheres that are at each end + newScale.Z = size.Z- (newScale.X + newScale.Y); Scale = newScale; } @@ -636,7 +645,7 @@ public class BSCharacter : BSPhysObject BulletSimAPI.SetLinearVelocity2(BSBody.ptr, avVel); } - // Tell the linkset about this + // Tell the linkset about value changes Linkset.UpdateProperties(this); // Avatars don't report their changes the usual way. Changes are checked for in the heartbeat loop. diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSConstraint.cs b/OpenSim/Region/Physics/BulletSPlugin/BSConstraint.cs index a20be3a285..f017cdd669 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSConstraint.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSConstraint.cs @@ -34,6 +34,8 @@ namespace OpenSim.Region.Physics.BulletSPlugin public abstract class BSConstraint : IDisposable { + private static string LogHeader = "[BULLETSIM CONSTRAINT]"; + protected BulletSim m_world; protected BulletBody m_body1; protected BulletBody m_body2; @@ -53,7 +55,7 @@ public abstract class BSConstraint : IDisposable { 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, + BSScene.DetailLogZero, m_body1.ID, m_body1.ptr.ToString("X"), m_body2.ID, m_body2.ptr.ToString("X"), success); @@ -124,7 +126,7 @@ public abstract class BSConstraint : IDisposable } else { - m_world.physicsScene.Logger.ErrorFormat("[BULLETSIM CONSTRAINT] CalculateTransforms failed. A={0}, B={1}", Body1.ID, Body2.ID); + m_world.physicsScene.Logger.ErrorFormat("{0} CalculateTransforms failed. A={1}, B={2}", LogHeader, Body1.ID, Body2.ID); } } return ret; diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs b/OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs index 56342b88f8..117c8787d1 100644 --- a/OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs @@ -23,7 +23,7 @@ * 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. - */ + * /* RA: June 14, 2011. Copied from ODEDynamics.cs and converted to * call the BulletSim system. @@ -352,7 +352,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin // m_bankingMix = 1; // m_bankingTimescale = 1; // m_referenceFrame = Quaternion.Identity; - m_flags |= (VehicleFlag.NO_DEFLECTION_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); @@ -382,7 +382,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin // m_bankingTimescale = 1; // m_referenceFrame = Quaternion.Identity; m_flags &= ~(VehicleFlag.HOVER_TERRAIN_ONLY - | VehicleFlag.HOVER_GLOBAL_HEIGHT + | VehicleFlag.HOVER_GLOBAL_HEIGHT | VehicleFlag.LIMIT_ROLL_ONLY | VehicleFlag.HOVER_UP_ONLY); m_flags |= (VehicleFlag.NO_DEFLECTION_UP @@ -458,14 +458,15 @@ namespace OpenSim.Region.Physics.BulletSPlugin // Do any updating needed for a vehicle public void Refresh() { - if (!IsActive) + if (!IsActive) return; // Set the prim's inertia to zero. The vehicle code handles that and this // 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); + // comment out for DEBUG test + // BulletSimAPI.SetMassProps2(Prim.BSBody.ptr, Prim.MassRaw, inertia); + // BulletSimAPI.UpdateInertiaTensor2(Prim.BSBody.ptr); } // One step of the vehicle properties for the next 'pTimestep' seconds. @@ -791,7 +792,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin // Sum velocities m_lastAngularVelocity = m_angularMotorVelocity + vertattr; // + bank + deflection - + if ((m_flags & (VehicleFlag.NO_DEFLECTION_UP)) != 0) { m_lastAngularVelocity.X = 0; diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSLinkset.cs b/OpenSim/Region/Physics/BulletSPlugin/BSLinkset.cs index 43b12623cc..c984824278 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSLinkset.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSLinkset.cs @@ -32,10 +32,27 @@ using OMV = OpenMetaverse; namespace OpenSim.Region.Physics.BulletSPlugin { -public class BSLinkset +public abstract class BSLinkset { // private static string LogHeader = "[BULLETSIM LINKSET]"; + // Create the correct type of linkset for this child + public static BSLinkset Factory(BSScene physScene, BSPhysObject parent) + { + BSLinkset ret = null; + /* + if (parent.IsPhysical) + ret = new BSLinksetConstraints(physScene, parent); + else + ret = new BSLinksetManual(physScene, parent); + */ + + // at the moment, there is only one + ret = new BSLinksetConstraints(physScene, parent); + + return ret; + } + public BSPhysObject LinksetRoot { get; protected set; } public BSScene PhysicsScene { get; private set; } @@ -52,16 +69,16 @@ public class BSLinkset // the physical 'taint' children separately. // After taint processing and before the simulation step, these // two lists must be the same. - private HashSet m_children; - private HashSet m_taintChildren; + protected HashSet m_children; + protected 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 // to the physical representation is done via the tainting mechenism. - private object m_linksetActivityLock = new Object(); + protected object m_linksetActivityLock = new Object(); // We keep the prim's mass in the linkset structure since it could be dependent on other prims - private float m_mass; + protected float m_mass; public float LinksetMass { get @@ -81,7 +98,7 @@ public class BSLinkset get { return ComputeLinksetGeometricCenter(); } } - public BSLinkset(BSScene scene, BSPhysObject parent) + protected void Initialize(BSScene scene, BSPhysObject parent) { // A simple linkset of one (no children) LinksetID = m_nextLinksetID++; @@ -128,7 +145,7 @@ public class BSLinkset } // The child is down to a linkset of just itself - return new BSLinkset(PhysicsScene, child); + return BSLinkset.Factory(PhysicsScene, child); } // Return 'true' if the passed object is the root object of this linkset @@ -148,6 +165,9 @@ public class BSLinkset bool ret = false; lock (m_linksetActivityLock) { + if (m_children.Contains(child)) + ret = true; + /* foreach (BSPhysObject bp in m_children) { if (child.LocalID == bp.LocalID) @@ -156,6 +176,7 @@ public class BSLinkset break; } } + */ } return ret; } @@ -163,24 +184,7 @@ public class BSLinkset // 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); - } + public abstract void Refresh(BSPhysObject requestor, bool inTaintTime); // The object is going dynamic (physical). Do any setup necessary // for a dynamic linkset. @@ -188,102 +192,35 @@ public class BSLinkset // has not yet been fully constructed. // Return 'true' if any properties updated on the passed object. // Called at taint-time! - public bool MakeDynamic(BSPhysObject child) - { - // What is done for each object in BSPrim is what we want. - return false; - } + public abstract bool MakeDynamic(BSPhysObject child); // The object is going static (non-physical). Do any setup necessary // for a static linkset. // Return 'true' if any properties updated on the passed object. // Called at taint-time! - public bool MakeStatic(BSPhysObject child) - { - // What is done for each object in BSPrim is what we want. - return false; - } + public abstract bool MakeStatic(BSPhysObject child); - // 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 when a parameter update comes from the physics engine for any object + // of the linkset is received. // Called at taint-time!! - public void UpdateProperties(BSPhysObject physObject) - { - // The root local properties have been updated. Apply to the children if appropriate. - if (IsRoot(physObject) && HasAnyChildren) - { - if (!physObject.IsPhysical) - { - // TODO: implement software linkset update for static object linksets - } - } - } + public abstract void UpdateProperties(BSPhysObject physObject); // Routine used when rebuilding the body of the root of the linkset // Destroy all the constraints have have been made to root. // This is called when the root body is changing. - // Returns 'true' of something eas actually removed and would need restoring + // Returns 'true' of something was actually removed and would need restoring // Called at taint-time!! - public bool RemoveBodyDependencies(BSPrim child) - { - bool ret = false; - - lock (m_linksetActivityLock) - { - if (IsRoot(child)) - { - // If the one with the dependency is root, must undo all children - DetailLog("{0},BSLinkset.RemoveBodyDependencies,removeChildrenForRoot,rID={1},rBody={2}", - child.LocalID, LinksetRoot.LocalID, LinksetRoot.BSBody.ptr.ToString("X")); - - ret = PhysicallyUnlinkAllChildrenFromRoot(LinksetRoot); - } - else - { - DetailLog("{0},BSLinkset.RemoveBodyDependencies,removeSingleChild,rID={1},rBody={2},cID={3},cBody={4}", - child.LocalID, - LinksetRoot.LocalID, LinksetRoot.BSBody.ptr.ToString("X"), - child.LocalID, child.BSBody.ptr.ToString("X")); - // ret = PhysicallyUnlinkAChildFromRoot(LinksetRoot, child); - // Despite the function name, this removes any link to the specified object. - ret = PhysicallyUnlinkAllChildrenFromRoot(child); - } - } - return ret; - } + public abstract bool RemoveBodyDependencies(BSPrim child); // Companion to RemoveBodyDependencies(). If RemoveBodyDependencies() returns 'true', - // this routine will restore the removed constraints. + // this routine will restore the removed constraints. // Called at taint-time!! - public void RestoreBodyDependencies(BSPrim child) - { - lock (m_linksetActivityLock) - { - if (IsRoot(child)) - { - DetailLog("{0},BSLinkset.RestoreBodyDependencies,restoreChildrenForRoot,rID={1},numChild={2}", - child.LocalID, LinksetRoot.LocalID, m_taintChildren.Count); - foreach (BSPhysObject bpo in m_taintChildren) - { - PhysicallyLinkAChildToRoot(LinksetRoot, bpo); - } - } - else - { - DetailLog("{0},BSLinkset.RestoreBodyDependencies,restoreSingleChild,rID={1},rBody={2},cID={3},cBody={4}", - LinksetRoot.LocalID, - LinksetRoot.LocalID, LinksetRoot.BSBody.ptr.ToString("X"), - child.LocalID, child.BSBody.ptr.ToString("X")); - PhysicallyLinkAChildToRoot(LinksetRoot, child); - } - } - } + public abstract void RestoreBodyDependencies(BSPrim child); // ================================================================ // Below this point is internal magic - private float ComputeLinksetMass() + protected virtual float ComputeLinksetMass() { float mass; lock (m_linksetActivityLock) @@ -297,7 +234,7 @@ public class BSLinkset return mass; } - private OMV.Vector3 ComputeLinksetCenterOfMass() + protected virtual OMV.Vector3 ComputeLinksetCenterOfMass() { OMV.Vector3 com; lock (m_linksetActivityLock) @@ -317,7 +254,7 @@ public class BSLinkset return com; } - private OMV.Vector3 ComputeLinksetGeometricCenter() + protected virtual OMV.Vector3 ComputeLinksetGeometricCenter() { OMV.Vector3 com; lock (m_linksetActivityLock) @@ -336,236 +273,21 @@ public class BSLinkset // I am the root of a linkset and a new child is being added // Called while LinkActivity is locked. - private void AddChildToLinkset(BSPhysObject child) - { - if (!HasChild(child)) - { - m_children.Add(child); - - BSPhysObject rootx = LinksetRoot; // capture the root as of now - BSPhysObject childx = child; - - DetailLog("{0},AddChildToLinkset,call,child={1}", LinksetRoot.LocalID, child.LocalID); - - PhysicsScene.TaintedObject("AddChildToLinkset", delegate() - { - 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; - } + protected abstract void AddChildToLinkset(BSPhysObject child); // Forcefully removing a child from a linkset. // This is not being called by the child so we have to make sure the child doesn't think // it's still connected to the linkset. // Normal OpenSimulator operation will never do this because other SceneObjectPart information // also has to be updated (like pointer to prim's parent). - private void RemoveChildFromOtherLinkset(BSPhysObject pchild) - { - pchild.Linkset = new BSLinkset(PhysicsScene, pchild); - RemoveChildFromLinkset(pchild); - } + protected abstract void RemoveChildFromOtherLinkset(BSPhysObject pchild); // I am the root of a linkset and one of my children is being removed. // Safe to call even if the child is not really in my linkset. - private void RemoveChildFromLinkset(BSPhysObject child) - { - if (m_children.Remove(child)) - { - BSPhysObject rootx = LinksetRoot; // capture the root and body as of now - BSPhysObject childx = child; - - DetailLog("{0},RemoveChildFromLinkset,call,rID={1},rBody={2},cID={3},cBody={4}", - childx.LocalID, - rootx.LocalID, rootx.BSBody.ptr.ToString("X"), - childx.LocalID, childx.BSBody.ptr.ToString("X")); - - PhysicsScene.TaintedObject("RemoveChildFromLinkset", delegate() - { - m_taintChildren.Remove(child); - PhysicallyUnlinkAChildFromRoot(rootx, childx); - RecomputeLinksetConstraintVariables(); - }); - - } - else - { - // This will happen if we remove the root of the linkset first. Non-fatal occurance. - // PhysicsScene.Logger.ErrorFormat("{0}: Asked to remove child from linkset that was not in linkset", LogHeader); - } - return; - } - - // Create a constraint between me (root of linkset) and the passed prim (the child). - // Called at taint time! - private void PhysicallyLinkAChildToRoot(BSPhysObject rootPrim, BSPhysObject childPrim) - { - // Zero motion for children so they don't interpolate - childPrim.ZeroMotion(); - - // Relative position normalized to the root prim - // Essentually a vector pointing from center of rootPrim to center of childPrim - OMV.Vector3 childRelativePosition = childPrim.Position - rootPrim.Position; - - // real world coordinate of midpoint between the two objects - OMV.Vector3 midPoint = rootPrim.Position + (childRelativePosition / 2); - - DetailLog("{0},BSLinkset.PhysicallyLinkAChildToRoot,taint,root={1},rBody={2},child={3},cBody={4},rLoc={5},cLoc={6},midLoc={7}", - rootPrim.LocalID, - 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 - - BS6DofConstraint constrain = new BS6DofConstraint( - 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 - * of the objects. - * Code left as a warning to future programmers. - // ================================================================================== - // relative position normalized to the root prim - OMV.Quaternion invThisOrientation = OMV.Quaternion.Inverse(rootPrim.Orientation); - OMV.Vector3 childRelativePosition = (childPrim.Position - rootPrim.Position) * invThisOrientation; - - // relative rotation of the child to the parent - OMV.Quaternion childRelativeRotation = invThisOrientation * childPrim.Orientation; - OMV.Quaternion inverseChildRelativeRotation = OMV.Quaternion.Inverse(childRelativeRotation); - - // create a constraint that allows no freedom of movement between the two objects - // http://bulletphysics.org/Bullet/phpBB3/viewtopic.php?t=4818 - 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, - OMV.Quaternion.Inverse(rootPrim.Orientation), - OMV.Vector3.Zero, - OMV.Quaternion.Inverse(childPrim.Orientation), - // A point half way between the parent and child - // childRelativePosition/2, - // childRelativeRotation, - // childRelativePosition/2, - // inverseChildRelativeRotation, - true, - true - ); - // ================================================================================== - */ - - PhysicsScene.Constraints.AddConstraint(constrain); - - // zero linear and angular limits makes the objects unable to move in relation to each other - constrain.SetLinearLimits(OMV.Vector3.Zero, OMV.Vector3.Zero); - constrain.SetAngularLimits(OMV.Vector3.Zero, OMV.Vector3.Zero); - - // tweek the constraint to increase stability - constrain.UseFrameOffset(PhysicsScene.BoolNumeric(PhysicsScene.Params.linkConstraintUseFrameOffset)); - constrain.TranslationalLimitMotor(PhysicsScene.BoolNumeric(PhysicsScene.Params.linkConstraintEnableTransMotor), - PhysicsScene.Params.linkConstraintTransMotorMaxVel, - PhysicsScene.Params.linkConstraintTransMotorMaxForce); - constrain.SetCFMAndERP(PhysicsScene.Params.linkConstraintCFM, PhysicsScene.Params.linkConstraintERP); - if (PhysicsScene.Params.linkConstraintSolverIterations != 0f) - { - constrain.SetSolverIterations(PhysicsScene.Params.linkConstraintSolverIterations); - } - } - - // 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 bool PhysicallyUnlinkAChildFromRoot(BSPhysObject rootPrim, BSPhysObject childPrim) - { - bool ret = false; - DetailLog("{0},BSLinkset.PhysicallyUnlinkAChildFromRoot,taint,root={1},rBody={2},child={3},cBody={4}", - rootPrim.LocalID, - 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 - if (PhysicsScene.Constraints.RemoveAndDestroyConstraint(rootPrim.BSBody, childPrim.BSBody)) - { - // Make the child refresh its location - BulletSimAPI.PushUpdate2(childPrim.BSBody.ptr); - ret = true; - } - - return ret; - } - - // Remove linkage between myself and any possible children I might have. - // Called at taint time! - private bool PhysicallyUnlinkAllChildrenFromRoot(BSPhysObject rootPrim) - { - DetailLog("{0},BSLinkset.PhysicallyUnlinkAllChildren,taint", rootPrim.LocalID); - bool ret = false; - - 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 - // from a linkset to make sure the constraints know about the new mass and - // geometry. - // Must only be called at taint time!! - private void RecomputeLinksetConstraintVariables() - { - float linksetMass = LinksetMass; - foreach (BSPhysObject child in m_taintChildren) - { - BSConstraint constrain; - if (PhysicsScene.Constraints.TryGetConstraint(LinksetRoot.BSBody, child.BSBody, out constrain)) - { - // DetailLog("{0},BSLinkset.RecomputeLinksetConstraintVariables,taint,child={1},mass={2},A={3},B={4}", - // LinksetRoot.LocalID, child.LocalID, linksetMass, constrain.Body1.ID, constrain.Body2.ID); - constrain.RecomputeConstraintVariables(linksetMass); - } - else - { - // Non-fatal error that happens when children are being added to the linkset but - // their constraints have not been created yet. - break; - } - } - - // If the whole linkset is not here, doesn't make sense to recompute linkset wide values - if (m_children.Count == m_taintChildren.Count) - { - // 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); - 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.DumpAllInfo2(PhysicsScene.World.ptr); // DEBUG DEBUG DEBUG - } - return; - } - + protected abstract void RemoveChildFromLinkset(BSPhysObject child); // Invoke the detailed logger and output something if it's enabled. - private void DetailLog(string msg, params Object[] args) + protected void DetailLog(string msg, params Object[] args) { if (PhysicsScene.PhysicsLogging.Enabled) PhysicsScene.DetailLog(msg, args); diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSLinksetConstraints.cs b/OpenSim/Region/Physics/BulletSPlugin/BSLinksetConstraints.cs new file mode 100755 index 0000000000..8a750b52b7 --- /dev/null +++ b/OpenSim/Region/Physics/BulletSPlugin/BSLinksetConstraints.cs @@ -0,0 +1,385 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyrightD + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +using System; +using System.Collections.Generic; +using System.Text; + +using OMV = OpenMetaverse; + +namespace OpenSim.Region.Physics.BulletSPlugin +{ +public class BSLinksetConstraints : BSLinkset +{ + // private static string LogHeader = "[BULLETSIM LINKSET CONSTRAINTS]"; + + public BSLinksetConstraints(BSScene scene, BSPhysObject parent) + { + base.Initialize(scene, parent); + } + + // 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 override void Refresh(BSPhysObject requestor, bool inTaintTime) + { + // If there are no children or not root, I am not the one that recomputes the constraints + if (!HasAnyChildren || !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 + // has not yet been fully constructed. + // Return 'true' if any properties updated on the passed object. + // Called at taint-time! + public override bool MakeDynamic(BSPhysObject child) + { + // What is done for each object in BSPrim is what we want. + return false; + } + + // The object is going static (non-physical). Do any setup necessary + // for a static linkset. + // Return 'true' if any properties updated on the passed object. + // Called at taint-time! + public override bool MakeStatic(BSPhysObject child) + { + // What is done for each object in BSPrim is what we want. + return false; + } + + // Called at taint-time!! + public override void UpdateProperties(BSPhysObject updated) + { + // Nothing to do for constraints on property updates + } + + // Routine used when rebuilding the body of the root of the linkset + // Destroy all the constraints have have been made to root. + // This is called when the root body is changing. + // Returns 'true' of something eas actually removed and would need restoring + // Called at taint-time!! + public override bool RemoveBodyDependencies(BSPrim child) + { + bool ret = false; + + lock (m_linksetActivityLock) + { + if (IsRoot(child)) + { + // If the one with the dependency is root, must undo all children + DetailLog("{0},BSLinkset.RemoveBodyDependencies,removeChildrenForRoot,rID={1},rBody={2}", + child.LocalID, LinksetRoot.LocalID, LinksetRoot.BSBody.ptr.ToString("X")); + + ret = PhysicallyUnlinkAllChildrenFromRoot(LinksetRoot); + } + else + { + DetailLog("{0},BSLinkset.RemoveBodyDependencies,removeSingleChild,rID={1},rBody={2},cID={3},cBody={4}", + child.LocalID, + LinksetRoot.LocalID, LinksetRoot.BSBody.ptr.ToString("X"), + child.LocalID, child.BSBody.ptr.ToString("X")); + // ret = PhysicallyUnlinkAChildFromRoot(LinksetRoot, child); + // Despite the function name, this removes any link to the specified object. + ret = PhysicallyUnlinkAllChildrenFromRoot(child); + } + } + return ret; + } + + // Companion to RemoveBodyDependencies(). If RemoveBodyDependencies() returns 'true', + // this routine will restore the removed constraints. + // Called at taint-time!! + public override void RestoreBodyDependencies(BSPrim child) + { + lock (m_linksetActivityLock) + { + if (IsRoot(child)) + { + DetailLog("{0},BSLinkset.RestoreBodyDependencies,restoreChildrenForRoot,rID={1},numChild={2}", + child.LocalID, LinksetRoot.LocalID, m_taintChildren.Count); + foreach (BSPhysObject bpo in m_taintChildren) + { + PhysicallyLinkAChildToRoot(LinksetRoot, bpo); + } + } + else + { + DetailLog("{0},BSLinkset.RestoreBodyDependencies,restoreSingleChild,rID={1},rBody={2},cID={3},cBody={4}", + LinksetRoot.LocalID, + LinksetRoot.LocalID, LinksetRoot.BSBody.ptr.ToString("X"), + child.LocalID, child.BSBody.ptr.ToString("X")); + PhysicallyLinkAChildToRoot(LinksetRoot, child); + } + } + } + + // ================================================================ + // Below this point is internal magic + + // I am the root of a linkset and a new child is being added + // Called while LinkActivity is locked. + protected override void AddChildToLinkset(BSPhysObject child) + { + if (!HasChild(child)) + { + m_children.Add(child); + + BSPhysObject rootx = LinksetRoot; // capture the root as of now + BSPhysObject childx = child; + + DetailLog("{0},AddChildToLinkset,call,child={1}", LinksetRoot.LocalID, child.LocalID); + + PhysicsScene.TaintedObject("AddChildToLinkset", delegate() + { + 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; + } + + // Forcefully removing a child from a linkset. + // This is not being called by the child so we have to make sure the child doesn't think + // it's still connected to the linkset. + // Normal OpenSimulator operation will never do this because other SceneObjectPart information + // also has to be updated (like pointer to prim's parent). + protected override void RemoveChildFromOtherLinkset(BSPhysObject pchild) + { + pchild.Linkset = BSLinkset.Factory(PhysicsScene, pchild); + RemoveChildFromLinkset(pchild); + } + + // I am the root of a linkset and one of my children is being removed. + // Safe to call even if the child is not really in my linkset. + protected override void RemoveChildFromLinkset(BSPhysObject child) + { + if (m_children.Remove(child)) + { + BSPhysObject rootx = LinksetRoot; // capture the root and body as of now + BSPhysObject childx = child; + + DetailLog("{0},RemoveChildFromLinkset,call,rID={1},rBody={2},cID={3},cBody={4}", + childx.LocalID, + rootx.LocalID, rootx.BSBody.ptr.ToString("X"), + childx.LocalID, childx.BSBody.ptr.ToString("X")); + + PhysicsScene.TaintedObject("RemoveChildFromLinkset", delegate() + { + m_taintChildren.Remove(child); + PhysicallyUnlinkAChildFromRoot(rootx, childx); + RecomputeLinksetConstraintVariables(); + }); + + } + else + { + // This will happen if we remove the root of the linkset first. Non-fatal occurance. + // PhysicsScene.Logger.ErrorFormat("{0}: Asked to remove child from linkset that was not in linkset", LogHeader); + } + return; + } + + // Create a constraint between me (root of linkset) and the passed prim (the child). + // Called at taint time! + private void PhysicallyLinkAChildToRoot(BSPhysObject rootPrim, BSPhysObject childPrim) + { + // Zero motion for children so they don't interpolate + childPrim.ZeroMotion(); + + // Relative position normalized to the root prim + // Essentually a vector pointing from center of rootPrim to center of childPrim + OMV.Vector3 childRelativePosition = childPrim.Position - rootPrim.Position; + + // real world coordinate of midpoint between the two objects + OMV.Vector3 midPoint = rootPrim.Position + (childRelativePosition / 2); + + DetailLog("{0},BSLinkset.PhysicallyLinkAChildToRoot,taint,root={1},rBody={2},child={3},cBody={4},rLoc={5},cLoc={6},midLoc={7}", + rootPrim.LocalID, + 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 + + BS6DofConstraint constrain = new BS6DofConstraint( + 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 + * of the objects. + * Code left as a warning to future programmers. + // ================================================================================== + // relative position normalized to the root prim + OMV.Quaternion invThisOrientation = OMV.Quaternion.Inverse(rootPrim.Orientation); + OMV.Vector3 childRelativePosition = (childPrim.Position - rootPrim.Position) * invThisOrientation; + + // relative rotation of the child to the parent + OMV.Quaternion childRelativeRotation = invThisOrientation * childPrim.Orientation; + OMV.Quaternion inverseChildRelativeRotation = OMV.Quaternion.Inverse(childRelativeRotation); + + // create a constraint that allows no freedom of movement between the two objects + // http://bulletphysics.org/Bullet/phpBB3/viewtopic.php?t=4818 + 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, + OMV.Quaternion.Inverse(rootPrim.Orientation), + OMV.Vector3.Zero, + OMV.Quaternion.Inverse(childPrim.Orientation), + // A point half way between the parent and child + // childRelativePosition/2, + // childRelativeRotation, + // childRelativePosition/2, + // inverseChildRelativeRotation, + true, + true + ); + // ================================================================================== + */ + + PhysicsScene.Constraints.AddConstraint(constrain); + + // zero linear and angular limits makes the objects unable to move in relation to each other + constrain.SetLinearLimits(OMV.Vector3.Zero, OMV.Vector3.Zero); + constrain.SetAngularLimits(OMV.Vector3.Zero, OMV.Vector3.Zero); + + // tweek the constraint to increase stability + constrain.UseFrameOffset(PhysicsScene.BoolNumeric(PhysicsScene.Params.linkConstraintUseFrameOffset)); + constrain.TranslationalLimitMotor(PhysicsScene.BoolNumeric(PhysicsScene.Params.linkConstraintEnableTransMotor), + PhysicsScene.Params.linkConstraintTransMotorMaxVel, + PhysicsScene.Params.linkConstraintTransMotorMaxForce); + constrain.SetCFMAndERP(PhysicsScene.Params.linkConstraintCFM, PhysicsScene.Params.linkConstraintERP); + if (PhysicsScene.Params.linkConstraintSolverIterations != 0f) + { + constrain.SetSolverIterations(PhysicsScene.Params.linkConstraintSolverIterations); + } + } + + // 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 bool PhysicallyUnlinkAChildFromRoot(BSPhysObject rootPrim, BSPhysObject childPrim) + { + bool ret = false; + DetailLog("{0},BSLinkset.PhysicallyUnlinkAChildFromRoot,taint,root={1},rBody={2},child={3},cBody={4}", + rootPrim.LocalID, + 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 + if (PhysicsScene.Constraints.RemoveAndDestroyConstraint(rootPrim.BSBody, childPrim.BSBody)) + { + // Make the child refresh its location + BulletSimAPI.PushUpdate2(childPrim.BSBody.ptr); + ret = true; + } + + return ret; + } + + // Remove linkage between myself and any possible children I might have. + // Called at taint time! + private bool PhysicallyUnlinkAllChildrenFromRoot(BSPhysObject rootPrim) + { + DetailLog("{0},BSLinkset.PhysicallyUnlinkAllChildren,taint", rootPrim.LocalID); + bool ret = false; + + 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 + // from a linkset to make sure the constraints know about the new mass and + // geometry. + // Must only be called at taint time!! + private void RecomputeLinksetConstraintVariables() + { + float linksetMass = LinksetMass; + foreach (BSPhysObject child in m_taintChildren) + { + BSConstraint constrain; + if (PhysicsScene.Constraints.TryGetConstraint(LinksetRoot.BSBody, child.BSBody, out constrain)) + { + // DetailLog("{0},BSLinkset.RecomputeLinksetConstraintVariables,taint,child={1},mass={2},A={3},B={4}", + // LinksetRoot.LocalID, child.LocalID, linksetMass, constrain.Body1.ID, constrain.Body2.ID); + constrain.RecomputeConstraintVariables(linksetMass); + } + else + { + // Non-fatal error that happens when children are being added to the linkset but + // their constraints have not been created yet. + break; + } + } + + // If the whole linkset is not here, doesn't make sense to recompute linkset wide values + if (m_children.Count == m_taintChildren.Count) + { + // 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); + 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.DumpAllInfo2(PhysicsScene.World.ptr); // DEBUG DEBUG DEBUG + } + return; + } +} +} diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSPhysObject.cs b/OpenSim/Region/Physics/BulletSPlugin/BSPhysObject.cs index ead6a08f43..538f9056a5 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSPhysObject.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSPhysObject.cs @@ -46,7 +46,7 @@ public abstract class BSPhysObject : PhysicsActor PhysObjectName = name; TypeName = typeName; - Linkset = new BSLinkset(PhysicsScene, this); + Linkset = BSLinkset.Factory(PhysicsScene, this); LastAssetBuildFailed = false; CollisionCollection = new CollisionEventUpdate(); @@ -78,7 +78,7 @@ public abstract class BSPhysObject : PhysicsActor public PrimitiveBaseShape BaseShape { get; protected set; } // When the physical properties are updated, an EntityProperty holds the update values. - // Keep the current and last EntityProperties to enable computation of differences + // Keep the current and last EntityProperties to enable computation of differences // between the current update and the previous values. public EntityProperties CurrentEntityProperties { get; set; } public EntityProperties LastEntityProperties { get; set; } @@ -213,7 +213,7 @@ public abstract class BSPhysObject : PhysicsActor UnSubscribeEvents(); } } - public override void UnSubscribeEvents() { + public override void UnSubscribeEvents() { // DetailLog("{0},{1}.UnSubscribeEvents,unsubscribing", LocalID, TypeName); SubscribedEventsMs = 0; PhysicsScene.TaintedObject(TypeName+".UnSubscribeEvents", delegate() @@ -222,7 +222,7 @@ public abstract class BSPhysObject : PhysicsActor }); } // Return 'true' if the simulator wants collision events - public override bool SubscribedEvents() { + public override bool SubscribedEvents() { return (SubscribedEventsMs > 0); } diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs b/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs index aeeb4dd27b..38ab3de65d 100644 --- a/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs @@ -173,6 +173,7 @@ public sealed class BSPrim : BSPhysObject } public override bool ForceBodyShapeRebuild(bool inTaintTime) { + LastAssetBuildFailed = false; BSScene.TaintCallback rebuildOperation = delegate() { _mass = CalculateMass(); // changing the shape changes the mass @@ -295,7 +296,7 @@ public sealed class BSPrim : BSPhysObject 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? /* @@ -398,7 +399,7 @@ public sealed class BSPrim : BSPhysObject { // 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. - this._vehicle.ProcessTypeChange(type); + _vehicle.ProcessTypeChange(type); }); } } @@ -510,7 +511,7 @@ public sealed class BSPrim : BSPhysObject }); } } - // Go directly to Bullet to get/set the value. + // Go directly to Bullet to get/set the value. public override OMV.Quaternion ForceOrientation { get @@ -768,7 +769,7 @@ public sealed class BSPrim : BSPhysObject } } public override bool FloatOnWater { - set { + set { _floatOnWater = value; PhysicsScene.TaintedObject("BSPrim.setFloatOnWater", delegate() { @@ -971,7 +972,7 @@ public sealed class BSPrim : BSPhysObject if (hollowAmount > 0.0) { hollowVolume *= hollowAmount; - + switch (BaseShape.HollowShape) { case HollowShape.Square: @@ -1245,13 +1246,14 @@ public sealed class BSPrim : BSPhysObject FillShapeInfo(out shapeData); // If this prim is part of a linkset, we must remove and restore the physical - // links of the body is rebuilt. + // links if the body is rebuilt. bool needToRestoreLinkset = false; // Create the correct physical representation for this type of object. // Updates BSBody and BSShape with the new information. // Ignore 'forceRebuild'. This routine makes the right choices and changes of necessary. - PhysicsScene.Shapes.GetBodyAndShape(false, PhysicsScene.World, this, shapeData, BaseShape, + // Returns 'true' if either the body or the shape was changed. + PhysicsScene.Shapes.GetBodyAndShape(false, PhysicsScene.World, this, shapeData, BaseShape, null, delegate(BulletBody dBody) { // Called if the current prim body is about to be destroyed. @@ -1354,7 +1356,7 @@ public sealed class BSPrim : BSPhysObject DetailLog("{0},BSPrim.UpdateProperties,call,pos={1},orient={2},vel={3},accel={4},rotVel={5}", LocalID, _position, _orientation, _velocity, _acceleration, _rotationalVelocity); - // BulletSimAPI.DumpRigidBody2(Scene.World.Ptr, BSBody.Ptr); + // BulletSimAPI.DumpRigidBody2(PhysicsScene.World.ptr, BSBody.ptr); // DEBUG DEBUG DEBUG base.RequestPhysicsterseUpdate(); } @@ -1367,8 +1369,8 @@ public sealed class BSPrim : BSPhysObject entprop.Acceleration, entprop.RotationalVelocity); } */ - // The linkset implimentation might want to know about this. + // The linkset implimentation might want to know about this. Linkset.UpdateProperties(this); } } diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs b/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs index 6621d39f2c..db0c99e8d1 100644 --- a/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs @@ -320,7 +320,7 @@ public class BSScene : PhysicsScene, IPhysicsParameters { m_log.Debug("[BULLETS UNMANAGED]:" + msg); } - + // Called directly from unmanaged code so don't do much private void BulletLoggerPhysLog(string msg) { @@ -545,7 +545,7 @@ public class BSScene : PhysicsScene, IPhysicsParameters } // This is a kludge to get avatar movement updates. - // The simulator expects collisions for avatars even if there are have been no collisions. + // The simulator expects collisions for avatars even if there are have been no collisions. // The event 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) @@ -692,7 +692,6 @@ 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) @@ -711,11 +710,12 @@ public class BSScene : PhysicsScene, IPhysicsParameters { try { - DetailLog("{0},BSScene.ProcessTaints,doTaint,id={1}", DetailLogZero, oneCallback.ident); // DEBUG DEBUG DEBUG + DetailLog("{0},BSScene.ProcessTaints,doTaint,id={1}", DetailLogZero, oneCallback.ident); oneCallback.callback(); } catch (Exception e) { + DetailLog("{0},BSScene.ProcessTaints,doTaintException,id={1}", DetailLogZero, oneCallback.ident); // DEBUG DEBUG DEBUG m_log.ErrorFormat("{0}: ProcessTaints: {1}: Exception: {2}", LogHeader, oneCallback.ident, e); } } @@ -1333,7 +1333,7 @@ public class BSScene : PhysicsScene, IPhysicsParameters // 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 + // Used to fill in the LocalID when there isn't one. It's the correct number of characters. public const string DetailLogZero = "0000000000"; } diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSShapeCollection.cs b/OpenSim/Region/Physics/BulletSPlugin/BSShapeCollection.cs index d3ba2732a8..30fa50a6fd 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; } @@ -89,7 +89,7 @@ public class BSShapeCollection : IDisposable // higher level dependencies on the shape or body. Mostly used for LinkSets to // remove the physical constraints before the body is destroyed. // Called at taint-time!! - public bool GetBodyAndShape(bool forceRebuild, BulletSim sim, BSPhysObject prim, + public bool GetBodyAndShape(bool forceRebuild, BulletSim sim, BSPhysObject prim, ShapeData shapeData, PrimitiveBaseShape pbs, ShapeDestructionCallback shapeCallback, BodyDestructionCallback bodyCallback) { @@ -105,7 +105,7 @@ 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, + bool newBody = CreateBody((newGeom || forceRebuild), prim, PhysicsScene.World, prim.BSShape, shapeData, bodyCallback); ret = newGeom || newBody; } @@ -325,7 +325,7 @@ public class BSShapeCollection : IDisposable // Info in prim.BSShape is updated to the new shape. // Returns 'true' if the geometry was rebuilt. // Called at taint-time! - private bool CreateGeom(bool forceRebuild, BSPhysObject prim, ShapeData shapeData, + private bool CreateGeom(bool forceRebuild, BSPhysObject prim, ShapeData shapeData, PrimitiveBaseShape pbs, ShapeDestructionCallback shapeCallback) { bool ret = false; @@ -335,9 +335,10 @@ public class BSShapeCollection : IDisposable if (shapeData.Type == ShapeData.PhysicsShapeType.SHAPE_AVATAR) { // an avatar capsule is close to a native shape (it is not shared) - ret = GetReferenceToNativeShape(prim, shapeData, ShapeData.PhysicsShapeType.SHAPE_AVATAR, + ret = GetReferenceToNativeShape(prim, shapeData, ShapeData.PhysicsShapeType.SHAPE_AVATAR, ShapeData.FixedShapeKey.KEY_CAPSULE, shapeCallback); DetailLog("{0},BSShapeCollection.CreateGeom,avatarCapsule,shape={1}", prim.LocalID, prim.BSShape); + ret = true; haveShape = true; } // If the prim attributes are simple, this could be a simple Bullet native shape @@ -362,7 +363,7 @@ public class BSShapeCollection : IDisposable || prim.BSShape.type != ShapeData.PhysicsShapeType.SHAPE_SPHERE ) { - ret = GetReferenceToNativeShape(prim, shapeData, ShapeData.PhysicsShapeType.SHAPE_SPHERE, + ret = GetReferenceToNativeShape(prim, shapeData, ShapeData.PhysicsShapeType.SHAPE_SPHERE, ShapeData.FixedShapeKey.KEY_SPHERE, shapeCallback); DetailLog("{0},BSShapeCollection.CreateGeom,sphere,force={1},shape={2}", prim.LocalID, forceRebuild, prim.BSShape); @@ -376,7 +377,7 @@ public class BSShapeCollection : IDisposable || prim.BSShape.type != ShapeData.PhysicsShapeType.SHAPE_BOX ) { - ret = GetReferenceToNativeShape( prim, shapeData, ShapeData.PhysicsShapeType.SHAPE_BOX, + ret = GetReferenceToNativeShape( prim, shapeData, ShapeData.PhysicsShapeType.SHAPE_BOX, ShapeData.FixedShapeKey.KEY_BOX, shapeCallback); DetailLog("{0},BSShapeCollection.CreateGeom,box,force={1},shape={2}", prim.LocalID, forceRebuild, prim.BSShape); @@ -411,39 +412,49 @@ public class BSShapeCollection : IDisposable ShapeData.PhysicsShapeType shapeType, ShapeData.FixedShapeKey shapeKey, ShapeDestructionCallback shapeCallback) { + // release any previous shape + DereferenceShape(prim.BSShape, true, shapeCallback); shapeData.Type = shapeType; // Bullet native objects are scaled by the Bullet engine so pass the size in prim.Scale = shapeData.Size; shapeData.Scale = shapeData.Size; - // release any previous shape - DereferenceShape(prim.BSShape, true, shapeCallback); - BulletShape newShape = BuildPhysicalNativeShape(shapeType, shapeData, shapeKey); // Don't need to do a 'ReferenceShape()' here because native shapes are not shared. - DetailLog("{0},BSShapeCollection.AddNativeShapeToPrim,create,newshape={1},scale={2}", + DetailLog("{0},BSShapeCollection.AddNativeShapeToPrim,create,newshape={1},scale={2}", shapeData.ID, newShape, shapeData.Scale); prim.BSShape = newShape; return true; } - private BulletShape BuildPhysicalNativeShape(ShapeData.PhysicsShapeType shapeType, + private BulletShape BuildPhysicalNativeShape(ShapeData.PhysicsShapeType shapeType, ShapeData shapeData, ShapeData.FixedShapeKey shapeKey) { BulletShape newShape; + // Need to make sure the passed shape information is for the native type. + ShapeData nativeShapeData = shapeData; + nativeShapeData.Type = shapeType; + nativeShapeData.MeshKey = (ulong)shapeKey; + nativeShapeData.HullKey = (ulong)shapeKey; if (shapeType == ShapeData.PhysicsShapeType.SHAPE_AVATAR) { newShape = new BulletShape( - BulletSimAPI.BuildCapsuleShape2(PhysicsScene.World.ptr, 1.0f, 1.0f, shapeData.Scale), - shapeType); + BulletSimAPI.BuildCapsuleShape2(PhysicsScene.World.ptr, 1f, 1f, nativeShapeData.Scale) + , shapeType); + DetailLog("{0},BSShapeCollection.BuiletPhysicalNativeShape,capsule,scale={1}", nativeShapeData.ID, nativeShapeData.Scale); } else { - newShape = new BulletShape(BulletSimAPI.BuildNativeShape2(PhysicsScene.World.ptr, shapeData), shapeType); + newShape = new BulletShape(BulletSimAPI.BuildNativeShape2(PhysicsScene.World.ptr, nativeShapeData), shapeType); + } + if (newShape.ptr == IntPtr.Zero) + { + PhysicsScene.Logger.ErrorFormat("{0} BuildPhysicalNativeShape failed. ID={1}, shape={2}", + LogHeader, nativeShapeData.ID, nativeShapeData.Type); } newShape.shapeKey = (System.UInt64)shapeKey; newShape.isNativeShape = true; @@ -698,19 +709,26 @@ public class BSShapeCollection : IDisposable return ComputeShapeKey(shapeData, pbs, out lod); } + // The creation of a mesh or hull can fail if an underlying asset is not available. + // There are two cases: 1) the asset is not in the cache and it needs to be fetched; + // and 2) the asset cannot be converted (like decompressing JPEG2000s). + // The first case causes the asset to be fetched. The second case just requires + // us to not loop forever. + // Called after creating a physical mesh or hull. If the physical shape was created, + // just return. private BulletShape VerifyMeshCreated(BulletShape newShape, BSPhysObject prim, ShapeData shapeData, PrimitiveBaseShape pbs) { // If the shape was successfully created, nothing more to do if (newShape.ptr != IntPtr.Zero) return newShape; - // The most common reason for failure is that an underlying asset is not available - // If this mesh has an underlying asset and we have not failed getting it before, fetch the asset if (pbs.SculptEntry && !prim.LastAssetBuildFailed && pbs.SculptTexture != OMV.UUID.Zero) { prim.LastAssetBuildFailed = true; BSPhysObject xprim = prim; + DetailLog("{0},BSShapeCollection.VerifyMeshCreated,fetchAsset,lID={1},lastFailed={2}", + LogHeader, shapeData.ID.ToString("X"), prim.LastAssetBuildFailed); Util.FireAndForget(delegate { RequestAssetDelegate assetProvider = PhysicsScene.RequestAssetMethod; @@ -724,20 +742,28 @@ public class BSShapeCollection : IDisposable if (yprim.BaseShape.SculptTexture.ToString() != asset.ID) return; - yprim.BaseShape.SculptData = new byte[asset.Data.Length]; - asset.Data.CopyTo(yprim.BaseShape.SculptData, 0); + yprim.BaseShape.SculptData = asset.Data; // This will cause the prim to see that the filler shape is not the right // one and try again to build the object. + // No race condition with the native sphere setting since the rebuild is at taint time. yprim.ForceBodyShapeRebuild(false); }); } }); } + else + { + if (prim.LastAssetBuildFailed) + { + PhysicsScene.Logger.ErrorFormat("{0} Mesh failed to fetch asset. lID={1}, texture={2}", + LogHeader, shapeData.ID, pbs.SculptTexture); + } + } // While we figure out the real problem, stick a simple native shape on the object. BulletShape fillinShape = - BuildPhysicalNativeShape(ShapeData.PhysicsShapeType.SHAPE_SPHERE, shapeData, ShapeData.FixedShapeKey.KEY_SPHERE); + BuildPhysicalNativeShape(ShapeData.PhysicsShapeType.SHAPE_BOX, shapeData, ShapeData.FixedShapeKey.KEY_BOX); return fillinShape; } @@ -746,7 +772,7 @@ public class BSShapeCollection : IDisposable // Updates prim.BSBody with the information about the new body if one is created. // Returns 'true' if an object was actually created. // Called at taint-time. - private bool CreateBody(bool forceRebuild, BSPhysObject prim, BulletSim sim, BulletShape shape, + private bool CreateBody(bool forceRebuild, BSPhysObject prim, BulletSim sim, BulletShape shape, ShapeData shapeData, BodyDestructionCallback bodyCallback) { bool ret = false; @@ -765,7 +791,6 @@ public class BSShapeCollection : IDisposable // If the collisionObject is not the correct type for solidness, rebuild what's there mustRebuild = true; } - } if (mustRebuild || forceRebuild) diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSTerrainManager.cs b/OpenSim/Region/Physics/BulletSPlugin/BSTerrainManager.cs index 41065344e2..880859a12d 100755 --- a/OpenSim/Region/Physics/BulletSPlugin/BSTerrainManager.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSTerrainManager.cs @@ -201,9 +201,7 @@ public class BSTerrainManager // If called with a mapInfo in m_heightMaps and there is an existing terrain body, a new // terrain shape is created and added to the body. // This call is most often used to update the heightMap and parameters of the terrain. - // 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. + // (The above does suggest that some simplification/refactoring is in order.) private void UpdateOrCreateTerrain(uint id, float[] heightMap, Vector3 minCoords, Vector3 maxCoords, bool inTaintTime) { DetailLog("{0},BSTerrainManager.UpdateOrCreateTerrain,call,minC={1},maxC={2},inTaintTime={3}", @@ -335,8 +333,8 @@ public class BSTerrainManager // Make sure the new shape is processed. // BulletSimAPI.Activate2(mapInfo.terrainBody.ptr, true); - BulletSimAPI.ForceActivationState2(mapInfo.terrainBody.ptr, ActivationState.ISLAND_SLEEPING); - // BulletSimAPI.ForceActivationState2(mapInfo.terrainBody.ptr, ActivationState.DISABLE_SIMULATION); + // BulletSimAPI.ForceActivationState2(mapInfo.terrainBody.ptr, ActivationState.ISLAND_SLEEPING); + BulletSimAPI.ForceActivationState2(mapInfo.terrainBody.ptr, ActivationState.DISABLE_SIMULATION); m_terrainModified = true; }; diff --git a/OpenSim/Region/RegionCombinerModule/RegionCombinerModule.cs b/OpenSim/Region/RegionCombinerModule/RegionCombinerModule.cs index 190fca0253..905540d36e 100644 --- a/OpenSim/Region/RegionCombinerModule/RegionCombinerModule.cs +++ b/OpenSim/Region/RegionCombinerModule/RegionCombinerModule.cs @@ -719,21 +719,21 @@ namespace OpenSim.Region.RegionCombinerModule rootConn.ClientEventForwarder = new RegionCombinerClientEventForwarder(rootConn); // Sets up the CoarseLocationUpdate forwarder for this root region - scene.EventManager.OnNewPresence += SetCourseLocationDelegate; + scene.EventManager.OnNewPresence += SetCoarseLocationDelegate; // Adds this root region to a dictionary of regions that are connectable m_regions.Add(scene.RegionInfo.originRegionID, rootConn); } } - private void SetCourseLocationDelegate(ScenePresence presence) + private void SetCoarseLocationDelegate(ScenePresence presence) { - presence.SetSendCourseLocationMethod(SendCourseLocationUpdates); + presence.SetSendCoarseLocationMethod(SendCoarseLocationUpdates); } // This delegate was refactored for non-combined regions. // This combined region version will not use the pre-compiled lists of locations and ids - private void SendCourseLocationUpdates(UUID sceneId, ScenePresence presence, List coarseLocations, List avatarUUIDs) + private void SendCoarseLocationUpdates(UUID sceneId, ScenePresence presence, List coarseLocations, List avatarUUIDs) { RegionConnections connectiondata = null; lock (m_regions) @@ -756,18 +756,18 @@ namespace OpenSim.Region.RegionCombinerModule } }); - DistributeCourseLocationUpdates(CoarseLocations, AvatarUUIDs, connectiondata, presence); + DistributeCoarseLocationUpdates(CoarseLocations, AvatarUUIDs, connectiondata, presence); } - private void DistributeCourseLocationUpdates(List locations, List uuids, + private void DistributeCoarseLocationUpdates(List locations, List uuids, RegionConnections connectiondata, ScenePresence rootPresence) { RegionData[] rdata = connectiondata.ConnectedRegions.ToArray(); //List clients = new List(); - Dictionary updates = new Dictionary(); + Dictionary updates = new Dictionary(); // Root Region entry - RegionCourseLocationStruct rootupdatedata = new RegionCourseLocationStruct(); + RegionCoarseLocationStruct rootupdatedata = new RegionCoarseLocationStruct(); rootupdatedata.Locations = new List(); rootupdatedata.Uuids = new List(); rootupdatedata.Offset = Vector2.Zero; @@ -781,7 +781,7 @@ namespace OpenSim.Region.RegionCombinerModule foreach (RegionData regiondata in rdata) { Vector2 offset = new Vector2(regiondata.Offset.X, regiondata.Offset.Y); - RegionCourseLocationStruct updatedata = new RegionCourseLocationStruct(); + RegionCoarseLocationStruct updatedata = new RegionCoarseLocationStruct(); updatedata.Locations = new List(); updatedata.Uuids = new List(); updatedata.Offset = offset; @@ -807,7 +807,7 @@ namespace OpenSim.Region.RegionCombinerModule if (!updates.ContainsKey(offset)) { // This shouldn't happen - RegionCourseLocationStruct updatedata = new RegionCourseLocationStruct(); + RegionCoarseLocationStruct updatedata = new RegionCoarseLocationStruct(); updatedata.Locations = new List(); updatedata.Uuids = new List(); updatedata.Offset = offset; diff --git a/OpenSim/Region/RegionCombinerModule/RegionCourseLocation.cs b/OpenSim/Region/RegionCombinerModule/RegionCourseLocation.cs index 53a678f82f..224ac9947c 100644 --- a/OpenSim/Region/RegionCombinerModule/RegionCourseLocation.cs +++ b/OpenSim/Region/RegionCombinerModule/RegionCourseLocation.cs @@ -33,7 +33,7 @@ using OpenSim.Framework; namespace OpenSim.Region.RegionCombinerModule { - struct RegionCourseLocationStruct + struct RegionCoarseLocationStruct { public List Locations; public List Uuids; diff --git a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs index cf801baecf..fa57845c7d 100644 --- a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs +++ b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs @@ -4388,7 +4388,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api { AnimationSet currentAnims = presence.Animator.Animations; string currentAnimationState = String.Empty; - if (animationstateNames.TryGetValue(currentAnims.DefaultAnimation.AnimID, out currentAnimationState)) + if (animationstateNames.TryGetValue(currentAnims.ImplicitDefaultAnimation.AnimID, out currentAnimationState)) return currentAnimationState; } } @@ -5705,7 +5705,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api flags |= ScriptBaseClass.AGENT_SITTING; } - if (agent.Animator.Animations.DefaultAnimation.AnimID + if (agent.Animator.Animations.ImplicitDefaultAnimation.AnimID == DefaultAvatarAnimations.AnimsUUID["SIT_GROUND_CONSTRAINED"]) { flags |= ScriptBaseClass.AGENT_SITTING; @@ -7892,7 +7892,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api { LSL_Vector lower; LSL_Vector upper; - if (presence.Animator.Animations.DefaultAnimation.AnimID + if (presence.Animator.Animations.ImplicitDefaultAnimation.AnimID == DefaultAvatarAnimations.AnimsUUID["SIT_GROUND_CONSTRAINED"]) { // This is for ground sitting avatars @@ -10685,12 +10685,12 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api internal void Deprecated(string command) { - throw new Exception("Command deprecated: " + command); + throw new ScriptException("Command deprecated: " + command); } internal void LSLError(string msg) { - throw new Exception("LSL Runtime Error: " + msg); + throw new ScriptException("LSL Runtime Error: " + msg); } public delegate void AssetRequestCallback(UUID assetID, AssetBase asset); diff --git a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/MOD_Api.cs b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/MOD_Api.cs index 6809c09d05..8f348332b6 100644 --- a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/MOD_Api.cs +++ b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/MOD_Api.cs @@ -95,13 +95,13 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api internal void MODError(string msg) { - throw new Exception("MOD Runtime Error: " + msg); + throw new ScriptException("MOD Runtime Error: " + msg); } - // - //Dumps an error message on the debug console. - // - + /// + /// Dumps an error message on the debug console. + /// + /// internal void MODShoutError(string message) { if (message.Length > 1023) @@ -359,20 +359,18 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api result[i] = (string)(LSL_String)plist[i]; else if (plist[i] is LSL_Integer) result[i] = (int)(LSL_Integer)plist[i]; + // The int check exists because of the many plain old int script constants in ScriptBase which + // are not LSL_Integers. + else if (plist[i] is int) + result[i] = plist[i]; else if (plist[i] is LSL_Float) result[i] = (float)(LSL_Float)plist[i]; else if (plist[i] is LSL_Key) result[i] = new UUID((LSL_Key)plist[i]); else if (plist[i] is LSL_Rotation) - { - result[i] = (OpenMetaverse.Quaternion)( - (LSL_Rotation)plist[i]); - } + result[i] = (Quaternion)((LSL_Rotation)plist[i]); else if (plist[i] is LSL_Vector) - { - result[i] = (OpenMetaverse.Vector3)( - (LSL_Vector)plist[i]); - } + result[i] = (Vector3)((LSL_Vector)plist[i]); else MODError(String.Format("{0}: unknown LSL list element type", fname)); } diff --git a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/OSSL_Api.cs b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/OSSL_Api.cs index 29bc163a19..828288d02e 100644 --- a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/OSSL_Api.cs +++ b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/OSSL_Api.cs @@ -210,7 +210,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api internal void OSSLError(string msg) { - throw new Exception("OSSL Runtime Error: " + msg); + throw new ScriptException("OSSL Runtime Error: " + msg); } /// @@ -1780,18 +1780,24 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api protected string LoadNotecard(string notecardNameOrUuid) { UUID assetID = CacheNotecard(notecardNameOrUuid); - StringBuilder notecardData = new StringBuilder(); - for (int count = 0; count < NotecardCache.GetLines(assetID); count++) + if (assetID != UUID.Zero) { - string line = NotecardCache.GetLine(assetID, count) + "\n"; - -// m_log.DebugFormat("[OSSL]: From notecard {0} loading line {1}", notecardNameOrUuid, line); - - notecardData.Append(line); + StringBuilder notecardData = new StringBuilder(); + + for (int count = 0; count < NotecardCache.GetLines(assetID); count++) + { + string line = NotecardCache.GetLine(assetID, count) + "\n"; + + // m_log.DebugFormat("[OSSL]: From notecard {0} loading line {1}", notecardNameOrUuid, line); + + notecardData.Append(line); + } + + return notecardData.ToString(); } - return notecardData.ToString(); + return null; } /// @@ -2340,11 +2346,12 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api appearance = new AvatarAppearance(); appearance.Unpack(appearanceOsd); } + else + { + OSSLError(string.Format("osNpcCreate: Notecard reference '{0}' not found.", notecard)); + } } - if (appearance == null) - return new LSL_Key(UUID.Zero.ToString()); - UUID ownerID = UUID.Zero; if (owned) ownerID = m_host.OwnerID; @@ -2407,6 +2414,10 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api return; string appearanceSerialized = LoadNotecard(notecard); + + if (appearanceSerialized == null) + OSSLError(string.Format("osNpcCreate: Notecard reference '{0}' not found.", notecard)); + OSDMap appearanceOsd = (OSDMap)OSDParser.DeserializeLLSDXml(appearanceSerialized); // OSD a = OSDParser.DeserializeLLSDXml(appearanceSerialized); // Console.WriteLine("appearanceSerialized {0}", appearanceSerialized); @@ -3636,5 +3647,68 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api DropAttachmentAt(false, pos, rot); } + + public LSL_Integer osListenRegex(int channelID, string name, string ID, string msg, int regexBitfield) + { + CheckThreatLevel(ThreatLevel.Low, "osListenRegex"); + m_host.AddScriptLPS(1); + UUID keyID; + UUID.TryParse(ID, out keyID); + + // if we want the name to be used as a regular expression, ensure it is valid first. + if ((regexBitfield & ScriptBaseClass.OS_LISTEN_REGEX_NAME) == ScriptBaseClass.OS_LISTEN_REGEX_NAME) + { + try + { + Regex.IsMatch("", name); + } + catch (Exception) + { + OSSLShoutError("Name regex is invalid."); + return -1; + } + } + + // if we want the msg to be used as a regular expression, ensure it is valid first. + if ((regexBitfield & ScriptBaseClass.OS_LISTEN_REGEX_MESSAGE) == ScriptBaseClass.OS_LISTEN_REGEX_MESSAGE) + { + try + { + Regex.IsMatch("", msg); + } + catch (Exception) + { + OSSLShoutError("Message regex is invalid."); + return -1; + } + } + + IWorldComm wComm = m_ScriptEngine.World.RequestModuleInterface(); + return (wComm == null) ? -1 : wComm.Listen( + m_host.LocalId, + m_item.ItemID, + m_host.UUID, + channelID, + name, + keyID, + msg, + regexBitfield + ); + } + + public LSL_Integer osRegexIsMatch(string input, string pattern) + { + CheckThreatLevel(ThreatLevel.Low, "osRegexIsMatch"); + m_host.AddScriptLPS(1); + try + { + return Regex.IsMatch(input, pattern) ? 1 : 0; + } + catch (Exception) + { + OSSLShoutError("Possible invalid regular expression detected."); + return 0; + } + } } -} \ 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 93188c9ec8..cdd9ea85a8 100644 --- a/OpenSim/Region/ScriptEngine/Shared/Api/Interface/IOSSL_Api.cs +++ b/OpenSim/Region/ScriptEngine/Shared/Api/Interface/IOSSL_Api.cs @@ -418,5 +418,29 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api.Interfaces /// /// void osForceDropAttachmentAt(vector pos, rotation rot); + + /// + /// Identical to llListen except for a bitfield which indicates which + /// string parameters should be parsed as regex patterns. + /// + /// + /// + /// + /// + /// + /// OS_LISTEN_REGEX_NAME + /// OS_LISTEN_REGEX_MESSAGE + /// + /// + LSL_Integer osListenRegex(int channelID, string name, string ID, + string msg, int regexBitfield); + + /// + /// Wraps to bool Regex.IsMatch(string input, string pattern) + /// + /// string to test for match + /// string to use as pattern + /// boolean + LSL_Integer osRegexIsMatch(string input, string pattern); } } diff --git a/OpenSim/Region/ScriptEngine/Shared/Api/Runtime/LSL_Constants.cs b/OpenSim/Region/ScriptEngine/Shared/Api/Runtime/LSL_Constants.cs index 62bd6b81b9..880841b5d9 100644 --- a/OpenSim/Region/ScriptEngine/Shared/Api/Runtime/LSL_Constants.cs +++ b/OpenSim/Region/ScriptEngine/Shared/Api/Runtime/LSL_Constants.cs @@ -716,5 +716,15 @@ namespace OpenSim.Region.ScriptEngine.Shared.ScriptBase public static readonly LSLInteger RCERR_UNKNOWN = -1; public static readonly LSLInteger RCERR_SIM_PERF_LOW = -2; public static readonly LSLInteger RCERR_CAST_TIME_EXCEEDED = 3; + + /// + /// process name parameter as regex + /// + public const int OS_LISTEN_REGEX_NAME = 0x1; + + /// + /// process message parameter as regex + /// + public const int OS_LISTEN_REGEX_MESSAGE = 0x2; } } diff --git a/OpenSim/Region/ScriptEngine/Shared/Api/Runtime/OSSL_Stub.cs b/OpenSim/Region/ScriptEngine/Shared/Api/Runtime/OSSL_Stub.cs index dee1b28cca..afa9ae0431 100644 --- a/OpenSim/Region/ScriptEngine/Shared/Api/Runtime/OSSL_Stub.cs +++ b/OpenSim/Region/ScriptEngine/Shared/Api/Runtime/OSSL_Stub.cs @@ -992,5 +992,15 @@ namespace OpenSim.Region.ScriptEngine.Shared.ScriptBase { m_OSSL_Functions.osForceDropAttachmentAt(pos, rot); } + + public LSL_Integer osListenRegex(int channelID, string name, string ID, string msg, int regexBitfield) + { + return m_OSSL_Functions.osListenRegex(channelID, name, ID, msg, regexBitfield); + } + + public LSL_Integer osRegexIsMatch(string input, string pattern) + { + return m_OSSL_Functions.osRegexIsMatch(input, pattern); + } } } diff --git a/OpenSim/Region/ScriptEngine/Shared/ScriptException.cs b/OpenSim/Region/ScriptEngine/Shared/ScriptException.cs new file mode 100644 index 0000000000..ae67fc5585 --- /dev/null +++ b/OpenSim/Region/ScriptEngine/Shared/ScriptException.cs @@ -0,0 +1,40 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +using System; + +namespace OpenSim.Region.ScriptEngine.Shared +{ + public class ScriptException : Exception + { + public ScriptException() : base() {} + + public ScriptException(string message) : base(message) {} + + public ScriptException(string message, Exception innerException) : base(message, innerException) {} + } +} \ No newline at end of file diff --git a/OpenSim/Region/ScriptEngine/Shared/Tests/OSSL_ApiAppearanceTest.cs b/OpenSim/Region/ScriptEngine/Shared/Tests/OSSL_ApiAppearanceTest.cs index c8718d9603..c40179456d 100644 --- a/OpenSim/Region/ScriptEngine/Shared/Tests/OSSL_ApiAppearanceTest.cs +++ b/OpenSim/Region/ScriptEngine/Shared/Tests/OSSL_ApiAppearanceTest.cs @@ -75,76 +75,6 @@ namespace OpenSim.Region.ScriptEngine.Shared.Tests m_engine.AddRegion(m_scene); } - /// - /// Test creation of an NPC where the appearance data comes from a notecard - /// - [Test] - public void TestOsNpcCreateUsingAppearanceFromNotecard() - { - TestHelpers.InMethod(); -// log4net.Config.XmlConfigurator.Configure(); - - // Store an avatar with a different height from default in a notecard. - UUID userId = TestHelpers.ParseTail(0x1); - float newHeight = 1.9f; - - ScenePresence sp = SceneHelpers.AddScenePresence(m_scene, userId); - sp.Appearance.AvatarHeight = newHeight; - SceneObjectGroup so = SceneHelpers.CreateSceneObject(1, userId, 0x10); - SceneObjectPart part = so.RootPart; - m_scene.AddSceneObject(so); - - OSSL_Api osslApi = new OSSL_Api(); - osslApi.Initialize(m_engine, part, null); - - string notecardName = "appearanceNc"; - osslApi.osOwnerSaveAppearance(notecardName); - - // Try creating a bot using the appearance in the notecard. - string npcRaw = osslApi.osNpcCreate("Jane", "Doe", new LSL_Types.Vector3(128, 128, 128), notecardName); - Assert.That(npcRaw, Is.Not.Null); - - UUID npcId = new UUID(npcRaw); - ScenePresence npc = m_scene.GetScenePresence(npcId); - Assert.That(npc, Is.Not.Null); - Assert.That(npc.Appearance.AvatarHeight, Is.EqualTo(newHeight)); - } - - /// - /// Test creation of an NPC where the appearance data comes from an avatar already in the region. - /// - [Test] - public void TestOsNpcCreateUsingAppearanceFromAvatar() - { - TestHelpers.InMethod(); -// TestHelpers.EnableLogging(); - - // Store an avatar with a different height from default in a notecard. - UUID userId = TestHelpers.ParseTail(0x1); - float newHeight = 1.9f; - - ScenePresence sp = SceneHelpers.AddScenePresence(m_scene, userId); - sp.Appearance.AvatarHeight = newHeight; - SceneObjectGroup so = SceneHelpers.CreateSceneObject(1, userId, 0x10); - SceneObjectPart part = so.RootPart; - m_scene.AddSceneObject(so); - - OSSL_Api osslApi = new OSSL_Api(); - osslApi.Initialize(m_engine, part, null); - - string notecardName = "appearanceNc"; - osslApi.osOwnerSaveAppearance(notecardName); - - // Try creating a bot using the existing avatar's appearance - string npcRaw = osslApi.osNpcCreate("Jane", "Doe", new LSL_Types.Vector3(128, 128, 128), sp.UUID.ToString()); - Assert.That(npcRaw, Is.Not.Null); - - UUID npcId = new UUID(npcRaw); - ScenePresence npc = m_scene.GetScenePresence(npcId); - Assert.That(npc, Is.Not.Null); - Assert.That(npc.Appearance.AvatarHeight, Is.EqualTo(newHeight)); - } - [Test] public void TestOsOwnerSaveAppearance() { diff --git a/OpenSim/Region/ScriptEngine/Shared/Tests/OSSL_ApiNpcTests.cs b/OpenSim/Region/ScriptEngine/Shared/Tests/OSSL_ApiNpcTests.cs index 25679a65c0..b49bcc2000 100644 --- a/OpenSim/Region/ScriptEngine/Shared/Tests/OSSL_ApiNpcTests.cs +++ b/OpenSim/Region/ScriptEngine/Shared/Tests/OSSL_ApiNpcTests.cs @@ -36,6 +36,7 @@ using OpenMetaverse; using OpenMetaverse.Assets; using OpenMetaverse.StructuredData; using OpenSim.Framework; +using OpenSim.Region.CoreModules.Avatar.Attachments; using OpenSim.Region.CoreModules.Avatar.AvatarFactory; using OpenSim.Region.OptionalModules.World.NPC; using OpenSim.Region.Framework.Scenes; @@ -71,13 +72,193 @@ namespace OpenSim.Region.ScriptEngine.Shared.Tests config.Set("Enabled", "true"); m_scene = new SceneHelpers().SetupScene(); - SceneHelpers.SetupSceneModules(m_scene, initConfigSource, new AvatarFactoryModule(), new NPCModule()); + SceneHelpers.SetupSceneModules( + m_scene, initConfigSource, new AvatarFactoryModule(), new AttachmentsModule(), new NPCModule()); m_engine = new XEngine.XEngine(); m_engine.Initialise(initConfigSource); m_engine.AddRegion(m_scene); } + /// + /// Test creation of an NPC where the appearance data comes from a notecard + /// + [Test] + public void TestOsNpcCreateUsingAppearanceFromNotecard() + { + TestHelpers.InMethod(); + + // Store an avatar with a different height from default in a notecard. + UUID userId = TestHelpers.ParseTail(0x1); + float newHeight = 1.9f; + + ScenePresence sp = SceneHelpers.AddScenePresence(m_scene, userId); + sp.Appearance.AvatarHeight = newHeight; + SceneObjectGroup so = SceneHelpers.CreateSceneObject(1, userId, 0x10); + SceneObjectPart part = so.RootPart; + m_scene.AddSceneObject(so); + + OSSL_Api osslApi = new OSSL_Api(); + osslApi.Initialize(m_engine, part, null); + + string notecardName = "appearanceNc"; + osslApi.osOwnerSaveAppearance(notecardName); + + // Try creating a bot using the appearance in the notecard. + string npcRaw = osslApi.osNpcCreate("Jane", "Doe", new LSL_Types.Vector3(128, 128, 128), notecardName); + Assert.That(npcRaw, Is.Not.Null); + + UUID npcId = new UUID(npcRaw); + ScenePresence npc = m_scene.GetScenePresence(npcId); + Assert.That(npc, Is.Not.Null); + Assert.That(npc.Appearance.AvatarHeight, Is.EqualTo(newHeight)); + } + + [Test] + public void TestOsNpcCreateNotExistingNotecard() + { + TestHelpers.InMethod(); + + UUID userId = TestHelpers.ParseTail(0x1); + + SceneObjectGroup so = SceneHelpers.CreateSceneObject(1, userId, 0x10); + m_scene.AddSceneObject(so); + + OSSL_Api osslApi = new OSSL_Api(); + osslApi.Initialize(m_engine, so.RootPart, null); + + string npcRaw; + bool gotExpectedException = false; + try + { + npcRaw + = osslApi.osNpcCreate("Jane", "Doe", new LSL_Types.Vector3(128, 128, 128), "not existing notecard name"); + } + catch (ScriptException) + { + gotExpectedException = true; + } + + Assert.That(gotExpectedException, Is.True); + } + + /// + /// Test creation of an NPC where the appearance data comes from an avatar already in the region. + /// + [Test] + public void TestOsNpcCreateUsingAppearanceFromAvatar() + { + TestHelpers.InMethod(); +// TestHelpers.EnableLogging(); + + // Store an avatar with a different height from default in a notecard. + UUID userId = TestHelpers.ParseTail(0x1); + float newHeight = 1.9f; + + ScenePresence sp = SceneHelpers.AddScenePresence(m_scene, userId); + sp.Appearance.AvatarHeight = newHeight; + SceneObjectGroup so = SceneHelpers.CreateSceneObject(1, userId, 0x10); + SceneObjectPart part = so.RootPart; + m_scene.AddSceneObject(so); + + OSSL_Api osslApi = new OSSL_Api(); + osslApi.Initialize(m_engine, part, null); + + string notecardName = "appearanceNc"; + osslApi.osOwnerSaveAppearance(notecardName); + + // Try creating a bot using the existing avatar's appearance + string npcRaw = osslApi.osNpcCreate("Jane", "Doe", new LSL_Types.Vector3(128, 128, 128), sp.UUID.ToString()); + Assert.That(npcRaw, Is.Not.Null); + + UUID npcId = new UUID(npcRaw); + ScenePresence npc = m_scene.GetScenePresence(npcId); + Assert.That(npc, Is.Not.Null); + Assert.That(npc.Appearance.AvatarHeight, Is.EqualTo(newHeight)); + } + + [Test] + public void TestOsNpcLoadAppearance() + { + TestHelpers.InMethod(); + + // Store an avatar with a different height from default in a notecard. + UUID userId = TestHelpers.ParseTail(0x1); + float firstHeight = 1.9f; + float secondHeight = 2.1f; + string firstAppearanceNcName = "appearanceNc1"; + string secondAppearanceNcName = "appearanceNc2"; + + ScenePresence sp = SceneHelpers.AddScenePresence(m_scene, userId); + sp.Appearance.AvatarHeight = firstHeight; + SceneObjectGroup so = SceneHelpers.CreateSceneObject(1, userId, 0x10); + SceneObjectPart part = so.RootPart; + m_scene.AddSceneObject(so); + + OSSL_Api osslApi = new OSSL_Api(); + osslApi.Initialize(m_engine, part, null); + + osslApi.osOwnerSaveAppearance(firstAppearanceNcName); + + string npcRaw + = osslApi.osNpcCreate("Jane", "Doe", new LSL_Types.Vector3(128, 128, 128), firstAppearanceNcName); + + // Create a second appearance notecard with a different height + sp.Appearance.AvatarHeight = secondHeight; + osslApi.osOwnerSaveAppearance(secondAppearanceNcName); + + osslApi.osNpcLoadAppearance(npcRaw, secondAppearanceNcName); + + UUID npcId = new UUID(npcRaw); + ScenePresence npc = m_scene.GetScenePresence(npcId); + Assert.That(npc, Is.Not.Null); + Assert.That(npc.Appearance.AvatarHeight, Is.EqualTo(secondHeight)); + } + + [Test] + public void TestOsNpcLoadAppearanceNotExistingNotecard() + { + TestHelpers.InMethod(); + + // Store an avatar with a different height from default in a notecard. + UUID userId = TestHelpers.ParseTail(0x1); + float firstHeight = 1.9f; + float secondHeight = 2.1f; + string firstAppearanceNcName = "appearanceNc1"; + string secondAppearanceNcName = "appearanceNc2"; + + ScenePresence sp = SceneHelpers.AddScenePresence(m_scene, userId); + sp.Appearance.AvatarHeight = firstHeight; + SceneObjectGroup so = SceneHelpers.CreateSceneObject(1, userId, 0x10); + SceneObjectPart part = so.RootPart; + m_scene.AddSceneObject(so); + + OSSL_Api osslApi = new OSSL_Api(); + osslApi.Initialize(m_engine, part, null); + + osslApi.osOwnerSaveAppearance(firstAppearanceNcName); + + string npcRaw + = osslApi.osNpcCreate("Jane", "Doe", new LSL_Types.Vector3(128, 128, 128), firstAppearanceNcName); + + bool gotExpectedException = false; + try + { + osslApi.osNpcLoadAppearance(npcRaw, secondAppearanceNcName); + } + catch (ScriptException) + { + gotExpectedException = true; + } + + Assert.That(gotExpectedException, Is.True); + + UUID npcId = new UUID(npcRaw); + ScenePresence npc = m_scene.GetScenePresence(npcId); + Assert.That(npc, Is.Not.Null); + Assert.That(npc.Appearance.AvatarHeight, Is.EqualTo(firstHeight)); + } + /// /// Test removal of an owned NPC. /// @@ -85,7 +266,6 @@ namespace OpenSim.Region.ScriptEngine.Shared.Tests public void TestOsNpcRemoveOwned() { TestHelpers.InMethod(); -// log4net.Config.XmlConfigurator.Configure(); // Store an avatar with a different height from default in a notecard. UUID userId = TestHelpers.ParseTail(0x1); diff --git a/OpenSim/Server/Handlers/Asset/AssetServerConnector.cs b/OpenSim/Server/Handlers/Asset/AssetServerConnector.cs index 46b0c672e2..4123f491bb 100644 --- a/OpenSim/Server/Handlers/Asset/AssetServerConnector.cs +++ b/OpenSim/Server/Handlers/Asset/AssetServerConnector.cs @@ -141,6 +141,9 @@ namespace OpenSim.Server.Handlers.Asset } string fileName = rawAssetId; + + if (!ConsoleUtil.CheckFileDoesNotExist(MainConsole.Instance, fileName)) + return; using (FileStream fs = new FileStream(fileName, FileMode.CreateNew)) { diff --git a/OpenSim/Tests/Common/Mock/TestClient.cs b/OpenSim/Tests/Common/Mock/TestClient.cs index bb8b9358c0..dde37ab7a3 100644 --- a/OpenSim/Tests/Common/Mock/TestClient.cs +++ b/OpenSim/Tests/Common/Mock/TestClient.cs @@ -528,13 +528,9 @@ namespace OpenSim.Tests.Common.Mock { } - public virtual void SendChatMessage(string message, byte type, Vector3 fromPos, string fromName, - UUID fromAgentID, byte source, byte audible) - { - } - - public virtual void SendChatMessage(byte[] message, byte type, Vector3 fromPos, string fromName, - UUID fromAgentID, byte source, byte audible) + public virtual void SendChatMessage( + string message, byte type, Vector3 fromPos, string fromName, + UUID fromAgentID, UUID ownerID, byte source, byte audible) { } diff --git a/bin/assets/CollisionSoundsAssetSet/CollisionSoundsAssetSet.xml b/bin/assets/CollisionSoundsAssetSet/CollisionSoundsAssetSet.xml index 7498ae0af8..b570c554be 100644 --- a/bin/assets/CollisionSoundsAssetSet/CollisionSoundsAssetSet.xml +++ b/bin/assets/CollisionSoundsAssetSet/CollisionSoundsAssetSet.xml @@ -303,37 +303,37 @@
- +
- +
- +
- +
- +
- + diff --git a/bin/lib32/BulletSim.dll b/bin/lib32/BulletSim.dll index f976efe72b..fbc83e64c7 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 a00bc3ab01..65d3805bb3 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 acaa9c875f..936368a4d9 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 996ea21576..82fbad6966 100755 Binary files a/bin/lib64/libBulletSim.so and b/bin/lib64/libBulletSim.so differ