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
0.7.5-pf-bulletsim
Justin Clark-Casey (justincc) 2013-01-04 20:34:39 +00:00
parent d87d9af1a5
commit ae355720ab
3 changed files with 103 additions and 67 deletions

View File

@ -647,6 +647,18 @@ namespace OpenSim.Region.Framework.Scenes
/// </remarks> /// </remarks>
public UUID FromFolderID { get; set; } public UUID FromFolderID { get; set; }
/// <summary>
/// IDs of all avatars sat on this scene object.
/// </summary>
/// <remarks>
/// 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.
/// </remarks>
protected internal List<UUID> m_sittingAvatars = new List<UUID>();
#endregion #endregion
// ~SceneObjectGroup() // ~SceneObjectGroup()
@ -3563,6 +3575,20 @@ namespace OpenSim.Region.Framework.Scenes
return count; return count;
} }
/// <summary>
/// Get a copy of the list of sitting avatars on all prims of this object.
/// </summary>
/// <remarks>
/// 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.
/// </remarks>
/// <returns>A list of the sitting avatars. Returns an empty list if there are no sitting avatars.</returns>
public List<UUID> GetSittingAvatars()
{
lock (m_sittingAvatars)
return new List<UUID>(m_sittingAvatars);
}
/// <summary> /// <summary>
/// Gets the number of sitting avatars. /// Gets the number of sitting avatars.
/// </summary> /// </summary>
@ -3570,11 +3596,8 @@ namespace OpenSim.Region.Framework.Scenes
/// <returns></returns> /// <returns></returns>
public int GetSittingAvatarsCount() public int GetSittingAvatarsCount()
{ {
int count = 0; lock (m_sittingAvatars)
return m_sittingAvatars.Count;
Array.ForEach<SceneObjectPart>(m_parts.GetArray(), p => count += p.GetSittingAvatarsCount());
return count;
} }
public override string ToString() public override string ToString()
@ -3583,7 +3606,7 @@ namespace OpenSim.Region.Framework.Scenes
} }
#region ISceneObject #region ISceneObject
public virtual ISceneObject CloneForNewScene() public virtual ISceneObject CloneForNewScene()
{ {
SceneObjectGroup sog = Copy(false); SceneObjectGroup sog = Copy(false);

View File

@ -1256,7 +1256,7 @@ namespace OpenSim.Region.Framework.Scenes
public UUID SitTargetAvatar { get; set; } public UUID SitTargetAvatar { get; set; }
/// <summary> /// <summary>
/// IDs of all avatars start on this object part. /// IDs of all avatars sat on this part.
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// We need to track this so that we can stop sat upon prims from being attached. /// 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
/// <param name='avatarId'></param> /// <param name='avatarId'></param>
protected internal bool AddSittingAvatar(UUID avatarId) protected internal bool AddSittingAvatar(UUID avatarId)
{ {
if (IsSitTargetSet && SitTargetAvatar == UUID.Zero) lock (ParentGroup.m_sittingAvatars)
SitTargetAvatar = avatarId;
HashSet<UUID> sittingAvatars = m_sittingAvatars;
if (sittingAvatars == null)
sittingAvatars = new HashSet<UUID>();
lock (sittingAvatars)
{ {
m_sittingAvatars = sittingAvatars; if (IsSitTargetSet && SitTargetAvatar == UUID.Zero)
return m_sittingAvatars.Add(avatarId); SitTargetAvatar = avatarId;
if (m_sittingAvatars == null)
m_sittingAvatars = new HashSet<UUID>();
if (m_sittingAvatars.Add(avatarId))
{
ParentGroup.m_sittingAvatars.Add(avatarId);
return true;
}
return false;
} }
} }
@ -4529,27 +4533,26 @@ namespace OpenSim.Region.Framework.Scenes
/// <param name='avatarId'></param> /// <param name='avatarId'></param>
protected internal bool RemoveSittingAvatar(UUID avatarId) protected internal bool RemoveSittingAvatar(UUID avatarId)
{ {
if (SitTargetAvatar == avatarId) lock (ParentGroup.m_sittingAvatars)
SitTargetAvatar = UUID.Zero;
HashSet<UUID> sittingAvatars = m_sittingAvatars;
// This can occur under a race condition where another thread
if (sittingAvatars == null)
return false;
lock (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; m_sittingAvatars = null;
ParentGroup.m_sittingAvatars.Remove(avatarId);
return true; return true;
} }
}
return false; return false;
}
} }
/// <summary> /// <summary>
@ -4559,16 +4562,12 @@ namespace OpenSim.Region.Framework.Scenes
/// <returns>A hashset of the sitting avatars. Returns null if there are no sitting avatars.</returns> /// <returns>A hashset of the sitting avatars. Returns null if there are no sitting avatars.</returns>
public HashSet<UUID> GetSittingAvatars() public HashSet<UUID> GetSittingAvatars()
{ {
HashSet<UUID> sittingAvatars = m_sittingAvatars; lock (ParentGroup.m_sittingAvatars)
if (sittingAvatars == null)
{ {
return null; if (m_sittingAvatars == null)
} return null;
else else
{ return new HashSet<UUID>(m_sittingAvatars);
lock (sittingAvatars)
return new HashSet<UUID>(sittingAvatars);
} }
} }
@ -4579,13 +4578,13 @@ namespace OpenSim.Region.Framework.Scenes
/// <returns></returns> /// <returns></returns>
public int GetSittingAvatarsCount() public int GetSittingAvatarsCount()
{ {
HashSet<UUID> sittingAvatars = m_sittingAvatars; lock (ParentGroup.m_sittingAvatars)
{
if (sittingAvatars == null) if (m_sittingAvatars == null)
return 0; return 0;
else
lock (sittingAvatars) return m_sittingAvatars.Count;
return sittingAvatars.Count; }
} }
} }
} }

View File

@ -3738,33 +3738,47 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
public LSL_String llGetLinkKey(int linknum) public LSL_String llGetLinkKey(int linknum)
{ {
m_host.AddScriptLPS(1); m_host.AddScriptLPS(1);
List<UUID> keytable = new List<UUID>();
// 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 < 0)
if (linknum > m_host.ParentGroup.PrimCount && linknum <= totalprims)
{ {
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) int actualPrimCount = m_host.ParentGroup.PrimCount;
{ List<UUID> sittingAvatarIds = m_host.ParentGroup.GetSittingAvatars();
return m_host.UUID.ToString(); int adjustedPrimCount = actualPrimCount + sittingAvatarIds.Count;
}
SceneObjectPart part = m_host.ParentGroup.GetLinkNumPart(linknum); // Special case for a single prim. In this case the linknum is zero. However, this will not match a single
if (part != null) // 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 else
{ {
return UUID.Zero.ToString(); return ScriptBaseClass.NULL_KEY;
} }
} }