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;
}
}