From ae355720abb70f0f27b3dcc7b711c76483ac0d07 Mon Sep 17 00:00:00 2001 From: "Justin Clark-Casey (justincc)" Date: Fri, 4 Jan 2013 20:34:39 +0000 Subject: [PATCH] Fix llGetLinkKey() to return the last sat avatar as the last link number. As per http://wiki.secondlife.com/wiki/LlGetLinkKey This is done by keeping a scene-object wide list of sitters. This also fixes bugs in this function where linknums 0 and 1 weren't treated properly if there were sitting avatars on a single prim. This also fixes a minor race condition for multiple concurrent sitters on a prim with no current sitters by locking on the object-wide list rather than individual sop lists Addresses http://opensimulator.org/mantis/view.php?id=6477 --- .../Framework/Scenes/SceneObjectGroup.cs | 35 ++++++-- .../Framework/Scenes/SceneObjectPart.cs | 85 +++++++++---------- .../Shared/Api/Implementation/LSL_Api.cs | 50 +++++++---- 3 files changed, 103 insertions(+), 67 deletions(-) diff --git a/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs b/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs index 35e7c4565c..15795e5a64 100644 --- a/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs +++ b/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs @@ -647,6 +647,18 @@ namespace OpenSim.Region.Framework.Scenes /// public UUID FromFolderID { get; set; } + /// + /// IDs of all avatars sat on this scene object. + /// + /// + /// We need this so that we can maintain a linkset wide ordering of avatars sat on different parts. + /// This must be locked before it is read or written. + /// SceneObjectPart sitting avatar add/remove code also locks on this object to avoid race conditions. + /// No avatar should appear more than once in this list. + /// Do not manipulate this list directly - use the Add/Remove sitting avatar methods on SceneObjectPart. + /// + protected internal List m_sittingAvatars = new List(); + #endregion // ~SceneObjectGroup() @@ -3563,6 +3575,20 @@ namespace OpenSim.Region.Framework.Scenes return count; } + /// + /// Get a copy of the list of sitting avatars on all prims of this object. + /// + /// + /// This is sorted by the order in which avatars sat down. If an avatar stands up then all avatars that sat + /// down after it move one place down the list. + /// + /// A list of the sitting avatars. Returns an empty list if there are no sitting avatars. + public List GetSittingAvatars() + { + lock (m_sittingAvatars) + return new List(m_sittingAvatars); + } + /// /// Gets the number of sitting avatars. /// @@ -3570,11 +3596,8 @@ namespace OpenSim.Region.Framework.Scenes /// public int GetSittingAvatarsCount() { - int count = 0; - - Array.ForEach(m_parts.GetArray(), p => count += p.GetSittingAvatarsCount()); - - return count; + lock (m_sittingAvatars) + return m_sittingAvatars.Count; } public override string ToString() @@ -3583,7 +3606,7 @@ namespace OpenSim.Region.Framework.Scenes } #region ISceneObject - + public virtual ISceneObject CloneForNewScene() { SceneObjectGroup sog = Copy(false); diff --git a/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs b/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs index 7a97e5fa9c..232861e166 100644 --- a/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs +++ b/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs @@ -1256,7 +1256,7 @@ namespace OpenSim.Region.Framework.Scenes public UUID SitTargetAvatar { get; set; } /// - /// IDs of all avatars start on this object part. + /// IDs of all avatars sat on this part. /// /// /// We need to track this so that we can stop sat upon prims from being attached. @@ -4504,18 +4504,22 @@ namespace OpenSim.Region.Framework.Scenes /// protected internal bool AddSittingAvatar(UUID avatarId) { - if (IsSitTargetSet && SitTargetAvatar == UUID.Zero) - SitTargetAvatar = avatarId; - - HashSet sittingAvatars = m_sittingAvatars; - - if (sittingAvatars == null) - sittingAvatars = new HashSet(); - - lock (sittingAvatars) + lock (ParentGroup.m_sittingAvatars) { - m_sittingAvatars = sittingAvatars; - return m_sittingAvatars.Add(avatarId); + if (IsSitTargetSet && SitTargetAvatar == UUID.Zero) + SitTargetAvatar = avatarId; + + if (m_sittingAvatars == null) + m_sittingAvatars = new HashSet(); + + if (m_sittingAvatars.Add(avatarId)) + { + ParentGroup.m_sittingAvatars.Add(avatarId); + + return true; + } + + return false; } } @@ -4529,27 +4533,26 @@ namespace OpenSim.Region.Framework.Scenes /// protected internal bool RemoveSittingAvatar(UUID avatarId) { - if (SitTargetAvatar == avatarId) - SitTargetAvatar = UUID.Zero; - - HashSet sittingAvatars = m_sittingAvatars; - - // This can occur under a race condition where another thread - if (sittingAvatars == null) - return false; - - lock (sittingAvatars) + lock (ParentGroup.m_sittingAvatars) { - if (sittingAvatars.Remove(avatarId)) + if (SitTargetAvatar == avatarId) + SitTargetAvatar = UUID.Zero; + + if (m_sittingAvatars == null) + return false; + + if (m_sittingAvatars.Remove(avatarId)) { - if (sittingAvatars.Count == 0) + if (m_sittingAvatars.Count == 0) m_sittingAvatars = null; + ParentGroup.m_sittingAvatars.Remove(avatarId); + return true; } - } - return false; + return false; + } } /// @@ -4559,16 +4562,12 @@ namespace OpenSim.Region.Framework.Scenes /// A hashset of the sitting avatars. Returns null if there are no sitting avatars. public HashSet GetSittingAvatars() { - HashSet sittingAvatars = m_sittingAvatars; - - if (sittingAvatars == null) + lock (ParentGroup.m_sittingAvatars) { - return null; - } - else - { - lock (sittingAvatars) - return new HashSet(sittingAvatars); + if (m_sittingAvatars == null) + return null; + else + return new HashSet(m_sittingAvatars); } } @@ -4579,13 +4578,13 @@ namespace OpenSim.Region.Framework.Scenes /// public int GetSittingAvatarsCount() { - HashSet sittingAvatars = m_sittingAvatars; - - if (sittingAvatars == null) - return 0; - - lock (sittingAvatars) - return sittingAvatars.Count; + lock (ParentGroup.m_sittingAvatars) + { + if (m_sittingAvatars == null) + return 0; + else + return m_sittingAvatars.Count; + } } } -} +} \ No newline at end of file diff --git a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs index 75749a9971..f31bbff6f1 100644 --- a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs +++ b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs @@ -3738,33 +3738,47 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api public LSL_String llGetLinkKey(int linknum) { m_host.AddScriptLPS(1); - List keytable = new List(); - // parse for sitting avatare-uuids - World.ForEachRootScenePresence(delegate(ScenePresence presence) - { - if (presence.ParentID != 0 && m_host.ParentGroup.ContainsPart(presence.ParentID)) - keytable.Add(presence.UUID); - }); - int totalprims = m_host.ParentGroup.PrimCount + keytable.Count; - if (linknum > m_host.ParentGroup.PrimCount && linknum <= totalprims) + if (linknum < 0) { - return keytable[totalprims - linknum].ToString(); + if (linknum == ScriptBaseClass.LINK_THIS) + return m_host.UUID.ToString(); + else + return ScriptBaseClass.NULL_KEY; } - if (linknum == 1 && m_host.ParentGroup.PrimCount == 1 && keytable.Count == 1) - { - return m_host.UUID.ToString(); - } + int actualPrimCount = m_host.ParentGroup.PrimCount; + List sittingAvatarIds = m_host.ParentGroup.GetSittingAvatars(); + int adjustedPrimCount = actualPrimCount + sittingAvatarIds.Count; - SceneObjectPart part = m_host.ParentGroup.GetLinkNumPart(linknum); - if (part != null) + // Special case for a single prim. In this case the linknum is zero. However, this will not match a single + // prim that has any avatars sat upon it (in which case the root prim is link 1). + if (linknum == 0) { - return part.UUID.ToString(); + if (actualPrimCount == 1 && sittingAvatarIds.Count == 0) + return m_host.UUID.ToString(); + + return ScriptBaseClass.NULL_KEY; + } + // Special case to handle a single prim with sitting avatars. GetLinkPart() would only match zero but + // here we must match 1 (ScriptBaseClass.LINK_ROOT). + else if (linknum == 1 && actualPrimCount == 1) + { + if (sittingAvatarIds.Count > 0) + return m_host.ParentGroup.RootPart.UUID.ToString(); + else + return ScriptBaseClass.NULL_KEY; + } + else if (linknum <= adjustedPrimCount) + { + if (linknum <= actualPrimCount) + return m_host.ParentGroup.GetLinkNumPart(linknum).UUID.ToString(); + else + return sittingAvatarIds[linknum - actualPrimCount - 1].ToString(); } else { - return UUID.Zero.ToString(); + return ScriptBaseClass.NULL_KEY; } }