Thank you, dslake, for a patch that converts many of the linear searches

in SceneGraph to fast dictionary lookups.
Includes a regression fix for attachments by myself.
Fixes Mantis #3312
0.6.5-rc1
Melanie Thielker 2009-04-12 15:18:04 +00:00
parent 87ce5ea0eb
commit cc86df4a3f
2 changed files with 204 additions and 263 deletions

View File

@ -233,12 +233,6 @@ namespace OpenSim.Region.Framework.Scenes
get { return m_defaultScriptEngine; }
}
// Local reference to the objects in the scene (which are held in the scenegraph)
// public Dictionary<UUID, SceneObjectGroup> Objects
// {
// get { return m_sceneGraph.SceneObjects; }
// }
// Reference to all of the agents in the scene (root and child)
protected Dictionary<UUID, ScenePresence> m_scenePresences
{
@ -246,12 +240,6 @@ namespace OpenSim.Region.Framework.Scenes
set { m_sceneGraph.ScenePresences = value; }
}
// protected Dictionary<UUID, SceneObjectGroup> m_sceneObjects
// {
// get { return m_sceneGraph.SceneObjects; }
// set { m_sceneGraph.SceneObjects = value; }
// }
public EntityManager Entities
{
get { return m_sceneGraph.Entities; }
@ -3492,7 +3480,6 @@ namespace OpenSim.Region.Framework.Scenes
protected internal void jointMoved(PhysicsJoint joint)
{
// m_parentScene.PhysicsScene.DumpJointInfo(); // non-thread-locked version; we should already be in a lock (OdeLock) when this callback is invoked
// FIXME: this causes a sequential lookup of all objects in the scene; use a dictionary
SceneObjectPart jointProxyObject = GetSceneObjectPart(joint.ObjectNameInScene);
if (jointProxyObject == null)
{
@ -3555,7 +3542,6 @@ namespace OpenSim.Region.Framework.Scenes
protected internal void jointDeactivated(PhysicsJoint joint)
{
//m_log.Debug("[NINJA] SceneGraph.jointDeactivated, joint:" + joint.ObjectNameInScene);
// FIXME: this causes a sequential lookup of all objects in the scene; use a dictionary
SceneObjectPart jointProxyObject = GetSceneObjectPart(joint.ObjectNameInScene);
if (jointProxyObject == null)
{
@ -3580,7 +3566,6 @@ namespace OpenSim.Region.Framework.Scenes
// from within the OdePhysicsScene.
public void jointErrorMessage(PhysicsJoint joint, string message)
{
// FIXME: this causes a sequential lookup of all objects in the scene; use a dictionary
if (joint != null)
{
if (joint.ErrorMessageCount > PhysicsJoint.maxErrorMessages)

View File

@ -77,7 +77,7 @@ namespace OpenSim.Region.Framework.Scenes
protected RegionInfo m_regInfo;
protected Scene m_parentScene;
protected List<EntityBase> m_updateList = new List<EntityBase>();
protected Dictionary<UUID, EntityBase> m_updateList = new Dictionary<UUID, EntityBase>();
protected int m_numRootAgents = 0;
protected int m_numPrim = 0;
protected int m_numChildAgents = 0;
@ -90,6 +90,10 @@ namespace OpenSim.Region.Framework.Scenes
protected internal PhysicsScene _PhyScene;
protected internal Dictionary<uint, SceneObjectGroup> SceneObjectGroupsByLocalID = new Dictionary<uint, SceneObjectGroup>();
protected internal Dictionary<UUID, SceneObjectGroup> SceneObjectGroupsByFullID = new Dictionary<UUID, SceneObjectGroup>();
private readonly Object m_dictionary_lock = new Object();
#endregion
protected internal SceneGraph(Scene parent, RegionInfo regInfo)
@ -126,6 +130,11 @@ namespace OpenSim.Region.Framework.Scenes
{
ScenePresences.Clear();
}
lock (m_dictionary_lock)
{
SceneObjectGroupsByFullID.Clear();
SceneObjectGroupsByLocalID.Clear();
}
Entities.Clear();
}
@ -301,6 +310,17 @@ namespace OpenSim.Region.Framework.Scenes
if (OnObjectCreate != null)
OnObjectCreate(sceneObject);
lock (m_dictionary_lock)
{
SceneObjectGroupsByFullID[sceneObject.UUID] = sceneObject;
SceneObjectGroupsByLocalID[sceneObject.LocalId] = sceneObject;
foreach (SceneObjectPart part in sceneObject.Children.Values)
{
SceneObjectGroupsByFullID[part.UUID] = sceneObject;
SceneObjectGroupsByLocalID[part.LocalId] = sceneObject;
}
}
return true;
}
}
@ -329,6 +349,11 @@ namespace OpenSim.Region.Framework.Scenes
if (OnObjectRemove != null)
OnObjectRemove(Entities[uuid]);
lock (m_dictionary_lock)
{
SceneObjectGroupsByFullID.Remove(uuid);
SceneObjectGroupsByLocalID.Remove(((SceneObjectGroup)Entities[uuid]).LocalId);
}
Entities.Remove(uuid);
//SceneObjectGroup part;
//((part.RootPart.Flags & PrimFlags.Physics) == PrimFlags.Physics)
@ -349,10 +374,7 @@ namespace OpenSim.Region.Framework.Scenes
{
lock (m_updateList)
{
if (!m_updateList.Contains(obj))
{
m_updateList.Add(obj);
}
m_updateList[obj.UUID] = obj;
}
}
@ -361,26 +383,28 @@ namespace OpenSim.Region.Framework.Scenes
/// </summary>
protected internal void ProcessUpdates()
{
Dictionary<UUID, EntityBase> updates;
// Some updates add more updates to the updateList.
// Get the current list of updates and clear the list before iterating
lock (m_updateList)
{
for (int i = 0; i < m_updateList.Count; i++)
updates = new Dictionary<UUID, EntityBase>(m_updateList);
m_updateList.Clear();
}
// Go through all timers
foreach (KeyValuePair<UUID, EntityBase> kvp in updates)
{
EntityBase entity = m_updateList[i];
// Don't abort the whole update if one entity happens to give us an exception.
try
{
m_updateList[i].Update();
kvp.Value.Update();
}
catch (Exception e)
{
m_log.ErrorFormat(
"[INNER SCENE]: Failed to update {0}, {1} - {2}", entity.Name, entity.UUID, e);
"[INNER SCENE]: Failed to update {0}, {1} - {2}", kvp.Value.Name, kvp.Value.UUID, e);
}
}
m_updateList.Clear();
}
}
protected internal void AddPhysicalPrim(int number)
@ -405,40 +429,22 @@ namespace OpenSim.Region.Framework.Scenes
public void DropObject(uint objectLocalID, IClientAPI remoteClient)
{
List<EntityBase> EntityList = GetEntities();
foreach (EntityBase obj in EntityList)
SceneObjectGroup group = GetGroupByPrim(objectLocalID);
if (group != null)
{
if (obj is SceneObjectGroup)
{
if (((SceneObjectGroup)obj).LocalId == objectLocalID)
{
SceneObjectGroup group = (SceneObjectGroup)obj;
m_parentScene.DetachSingleAttachmentToGround(group.UUID,remoteClient);
}
}
m_parentScene.DetachSingleAttachmentToGround(group.UUID, remoteClient);
}
}
protected internal void DetachObject(uint objectLocalID, IClientAPI remoteClient)
{
List<EntityBase> EntityList = GetEntities();
foreach (EntityBase obj in EntityList)
SceneObjectGroup group = GetGroupByPrim(objectLocalID);
if( group != null )
{
if (obj is SceneObjectGroup)
{
if (((SceneObjectGroup)obj).LocalId == objectLocalID)
{
SceneObjectGroup group = (SceneObjectGroup)obj;
//group.DetachToGround();
m_parentScene.DetachSingleAttachmentToInv(group.GetFromAssetID(),remoteClient);
}
}
}
}
protected internal void HandleUndo(IClientAPI remoteClient, UUID primId)
{
@ -453,22 +459,13 @@ namespace OpenSim.Region.Framework.Scenes
protected internal void HandleObjectGroupUpdate(
IClientAPI remoteClient, UUID GroupID, uint objectLocalID, UUID Garbage)
{
List<EntityBase> EntityList = GetEntities();
foreach (EntityBase obj in EntityList)
SceneObjectGroup group = GetGroupByPrim(objectLocalID);
if( group != null )
{
if (obj is SceneObjectGroup)
{
if (((SceneObjectGroup)obj).LocalId == objectLocalID)
{
SceneObjectGroup group = (SceneObjectGroup)obj;
if (group.OwnerID == remoteClient.AgentId)
group.SetGroup(GroupID, remoteClient);
}
}
}
}
/// <summary>
/// Event Handling routine for Attach Object
@ -527,19 +524,26 @@ namespace OpenSim.Region.Framework.Scenes
if (itemID == UUID.Zero) // If this happened, someone made a mistake....
return;
List<EntityBase> EntityList = GetEntities();
// We can NOT use the dictionries here, as we are looking
// for an entity by the fromAssetID, which is NOT the prim UUID
//
List<EntityBase> detachEntities = GetEntities();
SceneObjectGroup group;
foreach (EntityBase obj in EntityList)
foreach (EntityBase entity in detachEntities)
{
if (obj is SceneObjectGroup)
if (entity is SceneObjectGroup)
{
if (((SceneObjectGroup)obj).GetFromAssetID() == itemID)
group = (SceneObjectGroup)entity;
if (group.GetFromAssetID() == itemID)
{
SceneObjectGroup group = (SceneObjectGroup)obj;
group.DetachToInventoryPrep();
m_log.Debug("[DETACH]: Saving attachpoint: " + ((uint)group.GetAttachmentPoint()).ToString());
m_parentScene.updateKnownAsset(remoteClient, group, group.GetFromAssetID(), group.OwnerID);
m_log.Debug("[DETACH]: Saving attachpoint: " +
((uint)group.GetAttachmentPoint()).ToString());
m_parentScene.updateKnownAsset(remoteClient, group,
group.GetFromAssetID(), group.OwnerID);
m_parentScene.DeleteSceneObject(group, false);
return;
}
}
}
@ -548,33 +552,24 @@ namespace OpenSim.Region.Framework.Scenes
protected internal void AttachObject(
IClientAPI remoteClient, uint objectLocalID, uint AttachmentPt, Quaternion rot, Vector3 attachPos, bool silent)
{
List<EntityBase> EntityList = GetEntities();
foreach (EntityBase obj in EntityList)
SceneObjectGroup group = GetGroupByPrim(objectLocalID);
if( group != null )
{
if (obj is SceneObjectGroup)
{
if (((SceneObjectGroup)obj).LocalId == objectLocalID)
{
SceneObjectGroup group = (SceneObjectGroup)obj;
if (m_parentScene.Permissions.CanTakeObject(obj.UUID, remoteClient.AgentId))
if (m_parentScene.Permissions.CanTakeObject(group.UUID, remoteClient.AgentId))
{
// If the attachment point isn't the same as the one previously used
// set it's offset position = 0 so that it appears on the attachment point
// and not in a weird location somewhere unknown.
if (AttachmentPt != 0 && AttachmentPt != (uint)group.GetAttachmentPoint())
{
attachPos = Vector3.Zero;
}
// AttachmentPt 0 means the client chose to 'wear' the attachment.
if (AttachmentPt == 0)
{
// Check object for stored attachment point
AttachmentPt = (uint)group.GetAttachmentPoint();
}
// if we still didn't find a suitable attachment point.......
@ -613,9 +608,6 @@ namespace OpenSim.Region.Framework.Scenes
{
remoteClient.SendAgentAlertMessage("You don't have sufficient permissions to attach this object", false);
}
}
}
}
}
@ -864,6 +856,15 @@ namespace OpenSim.Region.Framework.Scenes
return Entities[localID] as SceneObjectGroup;
//m_log.DebugFormat("Entered GetGroupByPrim with localID {0}", localID);
SceneObjectGroup sog;
lock (SceneObjectGroupsByLocalID)
{
if (SceneObjectGroupsByLocalID.TryGetValue(localID, out sog))
{
return sog;
}
}
List<EntityBase> EntityList = GetEntities();
foreach (EntityBase ent in EntityList)
{
@ -871,7 +872,14 @@ namespace OpenSim.Region.Framework.Scenes
if (ent is SceneObjectGroup)
{
if (((SceneObjectGroup)ent).HasChildPrim(localID))
return (SceneObjectGroup)ent;
{
sog = (SceneObjectGroup)ent;
lock (SceneObjectGroupsByLocalID)
{
SceneObjectGroupsByLocalID[localID] = sog;
}
return sog;
}
}
}
return null;
@ -884,6 +892,15 @@ namespace OpenSim.Region.Framework.Scenes
/// <returns>null if no scene object group containing that prim is found</returns>
private SceneObjectGroup GetGroupByPrim(UUID fullID)
{
SceneObjectGroup sog;
lock (SceneObjectGroupsByFullID)
{
if (SceneObjectGroupsByFullID.TryGetValue(fullID, out sog))
{
return sog;
}
}
List<EntityBase> EntityList = GetEntities();
foreach (EntityBase ent in EntityList)
@ -891,7 +908,14 @@ namespace OpenSim.Region.Framework.Scenes
if (ent is SceneObjectGroup)
{
if (((SceneObjectGroup)ent).HasChildPrim(fullID))
return (SceneObjectGroup)ent;
{
sog = (SceneObjectGroup)ent;
lock (SceneObjectGroupsByFullID)
{
SceneObjectGroupsByFullID[fullID] = sog;
}
return sog;
}
}
}
return null;
@ -930,11 +954,9 @@ namespace OpenSim.Region.Framework.Scenes
protected internal SceneObjectPart GetSceneObjectPart(uint localID)
{
SceneObjectGroup group = GetGroupByPrim(localID);
if (group != null)
return group.GetChildPart(localID);
else
if (group == null)
return null;
return group.GetChildPart(localID);
}
/// <summary>
@ -972,11 +994,9 @@ namespace OpenSim.Region.Framework.Scenes
protected internal SceneObjectPart GetSceneObjectPart(UUID fullID)
{
SceneObjectGroup group = GetGroupByPrim(fullID);
if (group != null)
return group.GetChildPart(fullID);
else
if (group == null)
return null;
return group.GetChildPart(fullID);
}
protected internal bool TryGetAvatar(UUID avatarId, out ScenePresence avatar)
@ -1503,43 +1523,25 @@ namespace OpenSim.Region.Framework.Scenes
/// <param name="client"></param>
/// <param name="parentPrim"></param>
/// <param name="childPrims"></param>
protected internal void LinkObjects(IClientAPI client, uint parentPrim, List<uint> childPrims)
protected internal void LinkObjects(IClientAPI client, uint parentPrimId, List<uint> childPrimIds)
{
List<EntityBase> EntityList = GetEntities();
SceneObjectGroup parentGroup = GetGroupByPrim(parentPrimId);
SceneObjectGroup parenPrim = null;
foreach (EntityBase ent in EntityList)
{
if (ent is SceneObjectGroup)
{
if (((SceneObjectGroup)ent).LocalId == parentPrim)
{
parenPrim = (SceneObjectGroup)ent;
break;
}
}
}
List<SceneObjectGroup> children = new List<SceneObjectGroup>();
if (parenPrim != null)
List<SceneObjectGroup> childGroups = new List<SceneObjectGroup>();
if (parentGroup != null)
{
// We do this in reverse to get the link order of the prims correct
for (int i = childPrims.Count - 1; i >= 0; i--)
for (int i = childPrimIds.Count - 1; i >= 0; i--)
{
foreach (EntityBase ent in EntityList)
{
if (ent is SceneObjectGroup)
{
if (((SceneObjectGroup)ent).LocalId == childPrims[i])
SceneObjectGroup child = GetGroupByPrim(childPrimIds[i]);
if( child != null )
{
// Make sure no child prim is set for sale
// So that, on delink, no prims are unwittingly
// left for sale and sold off
((SceneObjectGroup)ent).RootPart.ObjectSaleType = 0;
((SceneObjectGroup)ent).RootPart.SalePrice = 10;
children.Add((SceneObjectGroup)ent);
}
}
child.RootPart.ObjectSaleType = 0;
child.RootPart.SalePrice = 10;
childGroups.Add(child);
}
}
}
@ -1548,29 +1550,29 @@ namespace OpenSim.Region.Framework.Scenes
return; // parent is null so not in this region
}
foreach (SceneObjectGroup sceneObj in children)
foreach (SceneObjectGroup child in childGroups)
{
parenPrim.LinkToGroup(sceneObj);
parentGroup.LinkToGroup(child);
// this is here so physics gets updated!
// Don't remove! Bad juju! Stay away! or fix physics!
sceneObj.AbsolutePosition = sceneObj.AbsolutePosition;
child.AbsolutePosition = child.AbsolutePosition;
}
// We need to explicitly resend the newly link prim's object properties since no other actions
// occur on link to invoke this elsewhere (such as object selection)
parenPrim.RootPart.AddFlag(PrimFlags.CreateSelected);
parenPrim.TriggerScriptChangedEvent(Changed.LINK);
parentGroup.RootPart.AddFlag(PrimFlags.CreateSelected);
parentGroup.TriggerScriptChangedEvent(Changed.LINK);
if (client != null)
{
parenPrim.GetProperties(client);
parentGroup.GetProperties(client);
}
else
{
foreach (ScenePresence p in GetScenePresences())
{
parenPrim.GetProperties(p.ControllingClient);
parentGroup.GetProperties(p.ControllingClient);
}
}
}
@ -1586,58 +1588,34 @@ namespace OpenSim.Region.Framework.Scenes
protected internal void DelinkObjects(List<uint> primIds, bool sendEvents)
{
SceneObjectGroup parenPrim = null;
// Need a list of the SceneObjectGroup local ids
// XXX I'm anticipating that building this dictionary once is more efficient than
// repeated scanning of the Entity.Values for a large number of primIds. However, it might
// be more efficient yet to keep this dictionary permanently on hand.
Dictionary<uint, SceneObjectGroup> sceneObjects = new Dictionary<uint, SceneObjectGroup>();
List<EntityBase> EntityList = GetEntities();
foreach (EntityBase ent in EntityList)
{
if (ent is SceneObjectGroup)
{
SceneObjectGroup obj = (SceneObjectGroup)ent;
// Nasty one. Can't unlink anything in the sim
// If a duplicate local ID sneaks in
// So, check it here!
//
if (!sceneObjects.ContainsKey(obj.LocalId))
sceneObjects.Add(obj.LocalId, obj);
}
}
SceneObjectGroup parentPrim = null;
// Find the root prim among the prim ids we've been given
for (int i = 0; i < primIds.Count; i++)
{
if (sceneObjects.ContainsKey(primIds[i]))
// Get the group for this prim and check that it is the parent
parentPrim = GetGroupByPrim(primIds[i]);
if (parentPrim != null && parentPrim.LocalId == primIds[i])
{
parenPrim = sceneObjects[primIds[i]];
primIds.RemoveAt(i);
break;
}
}
if (parenPrim != null)
if (parentPrim != null)
{
foreach (uint childPrimId in primIds)
{
parenPrim.DelinkFromGroup(childPrimId, sendEvents);
parentPrim.DelinkFromGroup(childPrimId, sendEvents);
}
if (parenPrim.Children.Count == 1)
if (parentPrim.Children.Count == 1)
{
// The link set has been completely torn down
// This is the case if you select a link set and delink
//
parenPrim.RootPart.LinkNum = 0;
parentPrim.RootPart.LinkNum = 0;
if (sendEvents)
parenPrim.TriggerScriptChangedEvent(Changed.LINK);
parentPrim.TriggerScriptChangedEvent(Changed.LINK);
}
else
{
@ -1645,7 +1623,7 @@ namespace OpenSim.Region.Framework.Scenes
// when a subset of a link set's prims are selected
// and the root prim is part of that selection
//
List<SceneObjectPart> parts = new List<SceneObjectPart>(parenPrim.Children.Values);
List<SceneObjectPart> parts = new List<SceneObjectPart>(parentPrim.Children.Values);
List<uint> unlink_ids = new List<uint>();
foreach (SceneObjectPart unlink_part in parts)
@ -1662,9 +1640,9 @@ namespace OpenSim.Region.Framework.Scenes
DelinkObjects(unlink_ids, false);
// Send event to root prim, then we're done with it
parenPrim.TriggerScriptChangedEvent(Changed.LINK);
parentPrim.TriggerScriptChangedEvent(Changed.LINK);
unlink_ids.Remove(parenPrim.RootPart.LocalId);
unlink_ids.Remove(parentPrim.RootPart.LocalId);
foreach (uint localId in unlink_ids)
{
@ -1683,7 +1661,7 @@ namespace OpenSim.Region.Framework.Scenes
// The selected prims were all child prims. Edit linked parts
// without the root prim selected will get us here
//
List<SceneObjectGroup> parents = new List<SceneObjectGroup>();
List<SceneObjectGroup> parentGroups = new List<SceneObjectGroup>();
// If the first scan failed, we need to do a /deep/ scan of the linkages. This is /really/ slow
// We know that this is not the root prim now essentially, so we don't have to worry about remapping
@ -1691,18 +1669,11 @@ namespace OpenSim.Region.Framework.Scenes
bool delinkedSomething = false;
for (int i = 0; i < primIds.Count; i++)
{
foreach (SceneObjectGroup grp in sceneObjects.Values)
{
SceneObjectPart gPart = grp.GetChildPart(primIds[i]);
if (gPart != null)
{
grp.DelinkFromGroup(primIds[i]);
SceneObjectGroup parent = GetGroupByPrim(primIds[i]);
parent.DelinkFromGroup(primIds[i]);
delinkedSomething = true;
if (!parents.Contains(grp))
parents.Add(grp);
}
}
if (!parentGroups.Contains(parent))
parentGroups.Add(parent);
}
if (!delinkedSomething)
{
@ -1712,7 +1683,7 @@ namespace OpenSim.Region.Framework.Scenes
}
else
{
foreach (SceneObjectGroup g in parents)
foreach (SceneObjectGroup g in parentGroups)
{
g.TriggerScriptChangedEvent(Changed.LINK);
}
@ -1790,30 +1761,15 @@ namespace OpenSim.Region.Framework.Scenes
/// <param name="originalPrim"></param>
/// <param name="offset"></param>
/// <param name="flags"></param>
protected internal SceneObjectGroup DuplicateObject(uint originalPrim, Vector3 offset, uint flags, UUID AgentID, UUID GroupID, Quaternion rot)
protected internal SceneObjectGroup DuplicateObject(uint originalPrimID, Vector3 offset, uint flags, UUID AgentID, UUID GroupID, Quaternion rot)
{
//m_log.DebugFormat("[SCENE]: Duplication of object {0} at offset {1} requested by agent {2}", originalPrim, offset, AgentID);
List<EntityBase> EntityList = GetEntities();
SceneObjectGroup originPrim = null;
foreach (EntityBase ent in EntityList)
SceneObjectGroup original = GetGroupByPrim(originalPrimID);
if (original != null)
{
if (ent is SceneObjectGroup)
if (m_parentScene.Permissions.CanDuplicateObject(original.Children.Count, original.UUID, AgentID, original.AbsolutePosition))
{
if (((SceneObjectGroup)ent).LocalId == originalPrim)
{
originPrim = (SceneObjectGroup)ent;
break;
}
}
}
if (originPrim != null)
{
if (m_parentScene.Permissions.CanDuplicateObject(originPrim.Children.Count, originPrim.UUID, AgentID, originPrim.AbsolutePosition))
{
SceneObjectGroup copy = originPrim.Copy(AgentID, GroupID, true);
SceneObjectGroup copy = original.Copy(AgentID, GroupID, true);
copy.AbsolutePosition = copy.AbsolutePosition + offset;
Entities.Add(copy);
@ -1840,7 +1796,7 @@ namespace OpenSim.Region.Framework.Scenes
copy.AbsolutePosition = copy.AbsolutePosition;
if (OnObjectDuplicate != null)
OnObjectDuplicate(originPrim, copy);
OnObjectDuplicate(original, copy);
return copy;
}