From cec112c87a0e9b259ddff215422bde3fc5dd4dc2 Mon Sep 17 00:00:00 2001 From: Justin Clarke Casey Date: Wed, 12 Nov 2008 20:16:46 +0000 Subject: * refactor: as per a recent opensim-dev thread, rename InnerScene to SceneGraph to make it more descriptive of its intended function --- OpenSim/Region/Environment/Scenes/SceneGraph.cs | 1724 +++++++++++++++++++++++ 1 file changed, 1724 insertions(+) create mode 100644 OpenSim/Region/Environment/Scenes/SceneGraph.cs (limited to 'OpenSim/Region/Environment/Scenes/SceneGraph.cs') diff --git a/OpenSim/Region/Environment/Scenes/SceneGraph.cs b/OpenSim/Region/Environment/Scenes/SceneGraph.cs new file mode 100644 index 0000000..ab0ec1f --- /dev/null +++ b/OpenSim/Region/Environment/Scenes/SceneGraph.cs @@ -0,0 +1,1724 @@ +/* + * 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 OpenSim 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.Reflection; +using OpenMetaverse; +using OpenMetaverse.Packets; +using log4net; +using OpenSim.Framework; +using OpenSim.Region.Environment.Types; +using OpenSim.Region.Physics.Manager; + +namespace OpenSim.Region.Environment.Scenes +{ + public delegate void PhysicsCrash(); + + /// + /// This class used to be called InnerScene and may not yet truly be a SceneGraph. The non scene graph components + /// should be migrated out over time. + /// + public class SceneGraph + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + #region Events + + protected internal event PhysicsCrash UnRecoverableError; + private PhysicsCrash handlerPhysicsCrash = null; + + #endregion + + #region Fields + + protected internal Dictionary ScenePresences = new Dictionary(); + // SceneObjects is not currently populated or used. + //public Dictionary SceneObjects; + protected internal Dictionary Entities = new Dictionary(); + protected internal Dictionary RestorePresences = new Dictionary(); + + protected internal BasicQuadTreeNode QuadTree; + + protected RegionInfo m_regInfo; + protected Scene m_parentScene; + protected List m_updateList = new List(); + protected int m_numRootAgents = 0; + protected int m_numPrim = 0; + protected int m_numChildAgents = 0; + protected int m_physicalPrim = 0; + + protected int m_activeScripts = 0; + protected int m_scriptLPS = 0; + + protected internal object m_syncRoot = new object(); + + protected internal PhysicsScene _PhyScene; + + #endregion + + protected internal SceneGraph(Scene parent, RegionInfo regInfo) + { + m_parentScene = parent; + m_regInfo = regInfo; + QuadTree = new BasicQuadTreeNode(null, "/0/", 0, 0, (short)Constants.RegionSize, (short)Constants.RegionSize); + QuadTree.Subdivide(); + QuadTree.Subdivide(); + } + + public PhysicsScene PhysicsScene + { + get { return _PhyScene; } + set + { + // If we're not doing the initial set + // Then we've got to remove the previous + // event handler + + if (_PhyScene != null) + _PhyScene.OnPhysicsCrash -= physicsBasedCrash; + + _PhyScene = value; + + if (_PhyScene != null) + _PhyScene.OnPhysicsCrash += physicsBasedCrash; + } + } + + protected internal void Close() + { + lock (ScenePresences) + { + ScenePresences.Clear(); + } + + //SceneObjects.Clear(); + lock (Entities) + { + Entities.Clear(); + } + } + + #region Update Methods + + protected internal void UpdatePreparePhysics() + { + // If we are using a threaded physics engine + // grab the latest scene from the engine before + // trying to process it. + + // PhysX does this (runs in the background). + + if (_PhyScene.IsThreaded) + { + _PhyScene.GetResults(); + } + } + + protected internal void UpdateEntities() + { + List updateEntities = GetEntities(); + + foreach (EntityBase entity in updateEntities) + { + entity.Update(); + } + } + + protected internal void UpdatePresences() + { + List updateScenePresences = GetScenePresences(); + foreach (ScenePresence pres in updateScenePresences) + { + pres.Update(); + } + } + + protected internal float UpdatePhysics(double elapsed) + { + lock (m_syncRoot) + { + return _PhyScene.Simulate((float)elapsed); + } + } + + protected internal void UpdateEntityMovement() + { + List moveEntities = GetEntities(); + + foreach (EntityBase entity in moveEntities) + { + //cfk. This throws occaisional exceptions on a heavily used region + //and I added this null check to try to preclude the exception. + if (entity != null) + entity.UpdateMovement(); + } + } + + #endregion + + #region Entity Methods + + /// + /// Add an object into the scene that has come from storage + /// + /// + /// + /// If true, changes to the object will be reflected in its persisted data + /// If false, the persisted data will not be changed even if the object in the scene is changed + /// + /// + /// If true, we won't persist this object until it changes + /// If false, we'll persist this object immediately + /// + /// + /// true if the object was added, false if an object with the same uuid was already in the scene + /// + protected internal bool AddRestoredSceneObject( + SceneObjectGroup sceneObject, bool attachToBackup, bool alreadyPersisted) + { + if (!alreadyPersisted) + { + sceneObject.ForceInventoryPersistence(); + sceneObject.HasGroupChanged = true; + } + + return AddSceneObject(sceneObject, attachToBackup); + } + + /// + /// Add a newly created object to the scene. This will both update the scene, and send information about the + /// new object to all clients interested in the scene. + /// + /// + /// + /// If true, the object is made persistent into the scene. + /// If false, the object will not persist over server restarts + /// + /// + /// true if the object was added, false if an object with the same uuid was already in the scene + /// + protected internal bool AddNewSceneObject(SceneObjectGroup sceneObject, bool attachToBackup) + { + // Ensure that we persist this new scene object + sceneObject.HasGroupChanged = true; + + return AddSceneObject(sceneObject, attachToBackup); + } + + /// + /// Add an object to the scene. This will both update the scene, and send information about the + /// new object to all clients interested in the scene. + /// + /// + /// + /// If true, the object is made persistent into the scene. + /// If false, the object will not persist over server restarts + /// + /// true if the object was added, false if an object with the same uuid was already in the scene + /// + protected bool AddSceneObject(SceneObjectGroup sceneObject, bool attachToBackup) + { + if (sceneObject == null || sceneObject.RootPart == null || sceneObject.RootPart.UUID == UUID.Zero) + return false; + + if (m_parentScene.m_clampPrimSize) + { + foreach (SceneObjectPart part in sceneObject.Children.Values) + { + Vector3 scale = part.Shape.Scale; + + if (scale.X > m_parentScene.m_maxNonphys) + scale.X = m_parentScene.m_maxNonphys; + if (scale.Y > m_parentScene.m_maxNonphys) + scale.Y = m_parentScene.m_maxNonphys; + if (scale.Z > m_parentScene.m_maxNonphys) + scale.Z = m_parentScene.m_maxNonphys; + + part.Shape.Scale = scale; + } + } + + sceneObject.AttachToScene(m_parentScene); + + lock (Entities) + { + if (!Entities.ContainsKey(sceneObject.UUID)) + { + // QuadTree.AddSceneObject(sceneObject); + Entities.Add(sceneObject.UUID, sceneObject); + m_numPrim += sceneObject.Children.Count; + + if (attachToBackup) + sceneObject.AttachToBackup(); + + return true; + } + + return false; + } + } + + /// + /// Delete an object from the scene + /// + /// + /// true if the object was deleted, false if there was no object to delete + protected internal bool DeleteSceneObject(UUID uuid, bool resultOfObjectLinked) + { + lock (Entities) + { + if (Entities.ContainsKey(uuid)) + { + if (!resultOfObjectLinked) + { + m_numPrim -= ((SceneObjectGroup)Entities[uuid]).Children.Count; + } + Entities.Remove(uuid); + + return true; + } + } + + return false; + } + + /// + /// Add an entity to the list of prims to process on the next update + /// + /// + /// A + /// + protected internal void AddToUpdateList(EntityBase obj) + { + lock (m_updateList) + { + if (!m_updateList.Contains(obj)) + { + m_updateList.Add(obj); + } + } + } + + /// + /// Process all pending updates + /// + protected internal void ProcessUpdates() + { + lock (m_updateList) + { + for (int i = 0; i < m_updateList.Count; i++) + { + 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(); + } + catch (Exception e) + { + m_log.ErrorFormat( + "[INNER SCENE]: Failed to update {0}, {1} - {2}", entity.Name, entity.UUID, e); + } + } + + m_updateList.Clear(); + } + } + + protected internal void AddPhysicalPrim(int number) + { + m_physicalPrim++; + } + + protected internal void RemovePhysicalPrim(int number) + { + m_physicalPrim--; + } + + protected internal void AddToScriptLPS(int number) + { + m_scriptLPS += number; + } + + protected internal void AddActiveScripts(int number) + { + m_activeScripts += number; + } + + protected internal void DropObject(uint objectLocalID, IClientAPI remoteClient) + { + List EntityList = GetEntities(); + + foreach (EntityBase obj in EntityList) + { + if (obj is SceneObjectGroup) + { + if (((SceneObjectGroup)obj).LocalId == objectLocalID) + { + SceneObjectGroup group = (SceneObjectGroup)obj; + + m_parentScene.DetachSingleAttachmentToGround(group.UUID,remoteClient); + } + } + } + } + + protected internal void DetachObject(uint objectLocalID, IClientAPI remoteClient) + { + List EntityList = GetEntities(); + + foreach (EntityBase obj in EntityList) + { + 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) + { + if (primId != UUID.Zero) + { + SceneObjectPart part = m_parentScene.GetSceneObjectPart(primId); + if (part != null) + part.Undo(); + } + } + + protected internal void HandleObjectGroupUpdate( + IClientAPI remoteClient, UUID GroupID, uint objectLocalID, UUID Garbage) + { + List EntityList = GetEntities(); + + foreach (EntityBase obj in EntityList) + { + if (obj is SceneObjectGroup) + { + if (((SceneObjectGroup)obj).LocalId == objectLocalID) + { + SceneObjectGroup group = (SceneObjectGroup)obj; + + if (m_parentScene.ExternalChecks.ExternalChecksCanEditObject(group.UUID, remoteClient.AgentId)) + group.SetGroup(GroupID, remoteClient); + else + remoteClient.SendAgentAlertMessage("You don't have permission to set the group", false); + } + } + } + } + + /// + /// Event Handling routine for Attach Object + /// + /// + /// + /// + /// + protected internal void AttachObject(IClientAPI remoteClient, uint objectLocalID, uint AttachmentPt, Quaternion rot, bool silent) + { + // If we can't take it, we can't attach it! + // + SceneObjectPart part = m_parentScene.GetSceneObjectPart(objectLocalID); + if (part == null) + return; + + if (!m_parentScene.ExternalChecks.ExternalChecksCanTakeObject( + part.UUID, remoteClient.AgentId)) + return; + + // Calls attach with a Zero position + // + AttachObject(remoteClient, objectLocalID, AttachmentPt, rot, Vector3.Zero, false); + } + + public SceneObjectGroup RezSingleAttachment( + IClientAPI remoteClient, UUID itemID, uint AttachmentPt) + { + SceneObjectGroup objatt = m_parentScene.RezObject(remoteClient, + itemID, Vector3.Zero, Vector3.Zero, UUID.Zero, (byte)1, true, + false, false, remoteClient.AgentId, true); + + + if (objatt != null) + { + bool tainted = false; + if (AttachmentPt != 0 && AttachmentPt != objatt.GetAttachmentPoint()) + tainted = true; + + AttachObject(remoteClient, objatt.LocalId, AttachmentPt, Quaternion.Identity, objatt.AbsolutePosition, false); + objatt.ScheduleGroupForFullUpdate(); + if (tainted) + objatt.HasGroupChanged = true; + } + return objatt; + } + + // What makes this method odd and unique is it tries to detach using an UUID.... Yay for standards. + // To LocalId or UUID, *THAT* is the question. How now Brown UUID?? + public void DetachSingleAttachmentToInv(UUID itemID, IClientAPI remoteClient) + { + if (itemID == UUID.Zero) // If this happened, someone made a mistake.... + return; + + List EntityList = GetEntities(); + + foreach (EntityBase obj in EntityList) + { + if (obj is SceneObjectGroup) + { + if (((SceneObjectGroup)obj).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_parentScene.DeleteSceneObject(group, false); + } + } + } + } + + protected internal void AttachObject( + IClientAPI remoteClient, uint objectLocalID, uint AttachmentPt, Quaternion rot, Vector3 attachPos, bool silent) + { + List EntityList = GetEntities(); + foreach (EntityBase obj in EntityList) + { + if (obj is SceneObjectGroup) + { + if (((SceneObjectGroup)obj).LocalId == objectLocalID) + { + SceneObjectGroup group = (SceneObjectGroup)obj; + if (m_parentScene.ExternalChecks.ExternalChecksCanTakeObject(obj.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....... + if (AttachmentPt == 0) + { + // Stick it on left hand with Zero Offset from the attachment point. + AttachmentPt = (uint)AttachmentPoint.LeftHand; + attachPos = Vector3.Zero; + } + + group.SetAttachmentPoint(Convert.ToByte(AttachmentPt)); + group.AbsolutePosition = attachPos; + + // Saves and gets assetID + UUID itemId; + if (group.GetFromAssetID() == UUID.Zero) + { + m_parentScene.attachObjectAssetStore(remoteClient, group, remoteClient.AgentId, out itemId); + } + else + { + itemId = group.GetFromAssetID(); + } + + m_parentScene.AttachObject(remoteClient, AttachmentPt, itemId, group); + + group.AttachToAgent(remoteClient.AgentId, AttachmentPt, attachPos, silent); + // In case it is later dropped again, don't let + // it get cleaned up + // + group.RootPart.RemFlag(PrimFlags.TemporaryOnRez); + group.HasGroupChanged = false; + } + else + { + remoteClient.SendAgentAlertMessage("You don't have sufficient permissions to attach this object", false); + } + + } + } + } + } + + protected internal ScenePresence CreateAndAddScenePresence(IClientAPI client, bool child, AvatarAppearance appearance) + { + ScenePresence newAvatar = null; + + newAvatar = new ScenePresence(client, m_parentScene, m_regInfo, appearance); + newAvatar.IsChildAgent = child; + + AddScenePresence(newAvatar); + + return newAvatar; + } + + /// + /// Add a presence to the scene + /// + /// + protected internal void AddScenePresence(ScenePresence presence) + { + bool child = presence.IsChildAgent; + + if (child) + { + m_numChildAgents++; + } + else + { + m_numRootAgents++; + presence.AddToPhysicalScene(); + } + + lock (Entities) + { + Entities[presence.UUID] = presence; + } + + lock (ScenePresences) + { + ScenePresences[presence.UUID] = presence; + } + } + + /// + /// Remove a presence from the scene + /// + protected internal void RemoveScenePresence(UUID agentID) + { + lock (Entities) + { + if (!Entities.Remove(agentID)) + { + m_log.WarnFormat("[SCENE] Tried to remove non-existent scene presence with agent ID {0} from scene Entities list", agentID); + } +// else +// { +// m_log.InfoFormat("[SCENE] Removed scene presence {0} from entities list", agentID); +// } + } + + lock (ScenePresences) + { + if (!ScenePresences.Remove(agentID)) + { + m_log.WarnFormat("[SCENE] Tried to remove non-existent scene presence with agent ID {0} from scene ScenePresences list", agentID); + } +// else +// { +// m_log.InfoFormat("[SCENE] Removed scene presence {0} from scene presences list", agentID); +// } + } + } + + protected internal void SwapRootChildAgent(bool direction_RC_CR_T_F) + { + if (direction_RC_CR_T_F) + { + m_numRootAgents--; + m_numChildAgents++; + } + else + { + m_numChildAgents--; + m_numRootAgents++; + } + } + + protected internal void removeUserCount(bool TypeRCTF) + { + if (TypeRCTF) + { + m_numRootAgents--; + } + else + { + m_numChildAgents--; + } + } + + public int GetChildAgentCount() + { + // some network situations come in where child agents get closed twice. + if (m_numChildAgents < 0) + { + m_numChildAgents = 0; + } + + return m_numChildAgents; + } + + public int GetRootAgentCount() + { + return m_numRootAgents; + } + + public int GetTotalObjectsCount() + { + return m_numPrim; + } + + public int GetActiveObjectsCount() + { + return m_physicalPrim; + } + + public int GetActiveScriptsCount() + { + return m_activeScripts; + } + + public int GetScriptLPS() + { + int returnval = m_scriptLPS; + m_scriptLPS = 0; + return returnval; + } + + #endregion + + #region Get Methods + + /// + /// Request a List of all scene presences in this scene. This is a new list, so no + /// locking is required to iterate over it. + /// + /// + protected internal List GetScenePresences() + { + return new List(ScenePresences.Values); + } + + protected internal List GetAvatars() + { + List result = + GetScenePresences(delegate(ScenePresence scenePresence) { return !scenePresence.IsChildAgent; }); + + return result; + } + + /// + /// Get the controlling client for the given avatar, if there is one. + /// + /// FIXME: The only user of the method right now is Caps.cs, in order to resolve a client API since it can't + /// use the ScenePresence. This could be better solved in a number of ways - we could establish an + /// OpenSim.Framework.IScenePresence, or move the caps code into a region package (which might be the more + /// suitable solution). + /// + /// + /// null if either the avatar wasn't in the scene, or they do not have a controlling client + protected internal IClientAPI GetControllingClient(UUID agentId) + { + ScenePresence presence = GetScenePresence(agentId); + + if (presence != null) + { + return presence.ControllingClient; + } + + return null; + } + + /// + /// Request a filtered list of m_scenePresences in this World + /// + /// + protected internal List GetScenePresences(FilterAvatarList filter) + { + // No locking of scene presences here since we're passing back a list... + + List result = new List(); + List ScenePresencesList = GetScenePresences(); + + foreach (ScenePresence avatar in ScenePresencesList) + { + if (filter(avatar)) + { + result.Add(avatar); + } + } + + return result; + } + + /// + /// Request a scene presence by UUID + /// + /// + /// null if the agent was not found + protected internal ScenePresence GetScenePresence(UUID agentID) + { + ScenePresence sp; + ScenePresences.TryGetValue(agentID, out sp); + + return sp; + } + + /// + /// Get a scene object group that contains the prim with the given local id + /// + /// + /// null if no scene object group containing that prim is found + private SceneObjectGroup GetGroupByPrim(uint localID) + { + //m_log.DebugFormat("Entered GetGroupByPrim with localID {0}", localID); + List EntityList = GetEntities(); + foreach (EntityBase ent in EntityList) + { + //m_log.DebugFormat("Looking at entity {0}", ent.UUID); + if (ent is SceneObjectGroup) + { + if (((SceneObjectGroup)ent).HasChildPrim(localID)) + return (SceneObjectGroup)ent; + } + } + return null; + } + + /// + /// Get a scene object group that contains the prim with the given uuid + /// + /// + /// null if no scene object group containing that prim is found + private SceneObjectGroup GetGroupByPrim(UUID fullID) + { + List EntityList = GetEntities(); + + foreach (EntityBase ent in EntityList) + { + if (ent is SceneObjectGroup) + { + if (((SceneObjectGroup)ent).HasChildPrim(fullID)) + return (SceneObjectGroup)ent; + } + } + return null; + } + + protected internal EntityIntersection GetClosestIntersectingPrim(Ray hray, bool frontFacesOnly, bool faceCenters) + { + // Primitive Ray Tracing + float closestDistance = 280f; + EntityIntersection returnResult = new EntityIntersection(); + List EntityList = GetEntities(); + foreach (EntityBase ent in EntityList) + { + if (ent is SceneObjectGroup) + { + SceneObjectGroup reportingG = (SceneObjectGroup)ent; + EntityIntersection result = reportingG.TestIntersection(hray, frontFacesOnly, faceCenters); + if (result.HitTF) + { + if (result.distance < closestDistance) + { + closestDistance = result.distance; + returnResult = result; + } + } + } + } + return returnResult; + } + + /// + /// Get a part contained in this scene. + /// + /// + /// null if the part was not found + protected internal SceneObjectPart GetSceneObjectPart(uint localID) + { + SceneObjectGroup group = GetGroupByPrim(localID); + + if (group != null) + return group.GetChildPart(localID); + else + return null; + } + + /// + /// Get a named prim contained in this scene (will return the first + /// found, if there are more than one prim with the same name) + /// + /// + /// null if the part was not found + protected internal SceneObjectPart GetSceneObjectPart(string name) + { + List EntityList = GetEntities(); + + foreach (EntityBase ent in EntityList) + { + if (ent is SceneObjectGroup) + { + foreach (SceneObjectPart p in ((SceneObjectGroup) ent).GetParts()) + { + if (p.Name==name) + { + return p; + } + } + } + } + return null; + } + + /// + /// Get a part contained in this scene. + /// + /// + /// null if the part was not found + protected internal SceneObjectPart GetSceneObjectPart(UUID fullID) + { + SceneObjectGroup group = GetGroupByPrim(fullID); + + if (group != null) + return group.GetChildPart(fullID); + else + return null; + } + + protected internal bool TryGetAvatar(UUID avatarId, out ScenePresence avatar) + { + ScenePresence presence; + if (ScenePresences.TryGetValue(avatarId, out presence)) + { + if (!presence.IsChildAgent) + { + avatar = presence; + return true; + } + else + { + m_log.WarnFormat( + "[INNER SCENE]: Requested avatar {0} could not be found in scene {1} since it is only registered as a child agent!", + avatarId, m_parentScene.RegionInfo.RegionName); + } + } + + avatar = null; + return false; + } + + protected internal bool TryGetAvatarByName(string avatarName, out ScenePresence avatar) + { + lock (ScenePresences) + { + foreach (ScenePresence presence in ScenePresences.Values) + { + if (!presence.IsChildAgent) + { + string name = presence.ControllingClient.Name; + + if (String.Compare(avatarName, name, true) == 0) + { + avatar = presence; + return true; + } + } + } + } + + avatar = null; + return false; + } + + /// + /// Returns a list of the entities in the scene. This is a new list so no locking is required to iterate over + /// it + /// + /// + protected internal List GetEntities() + { + lock (Entities) + { + return new List(Entities.Values); + } + } + + protected internal Dictionary GetTopScripts() + { + Dictionary topScripts = new Dictionary(); + + List EntityList = GetEntities(); + int limit = 0; + foreach (EntityBase ent in EntityList) + { + if (ent is SceneObjectGroup) + { + SceneObjectGroup grp = (SceneObjectGroup)ent; + if ((grp.RootPart.GetEffectiveObjectFlags() & (uint)PrimFlags.Scripted) != 0) + { + if (grp.scriptScore >= 0.01) + { + topScripts.Add(grp.LocalId, grp.scriptScore); + limit++; + if (limit >= 100) + { + break; + } + } + grp.scriptScore = 0; + } + } + } + + return topScripts; + } + + #endregion + + #region Other Methods + + protected internal void physicsBasedCrash() + { + handlerPhysicsCrash = UnRecoverableError; + if (handlerPhysicsCrash != null) + { + handlerPhysicsCrash(); + } + } + + protected internal UUID ConvertLocalIDToFullID(uint localID) + { + SceneObjectGroup group = GetGroupByPrim(localID); + if (group != null) + return group.GetPartsFullID(localID); + else + return UUID.Zero; + } + + protected internal void ForEachClient(Action action) + { + lock (ScenePresences) + { + foreach (ScenePresence presence in ScenePresences.Values) + { + action(presence.ControllingClient); + } + } + } + + #endregion + + #region Client Event handlers + + /// + /// + /// + /// + /// + /// + protected internal void UpdatePrimScale(uint localID, Vector3 scale, IClientAPI remoteClient) + { + SceneObjectGroup group = GetGroupByPrim(localID); + if (group != null) + { + if (m_parentScene.ExternalChecks.ExternalChecksCanEditObject(group.UUID, remoteClient.AgentId)) + { + group.Resize(scale, localID); + } + } + } + + protected internal void UpdatePrimGroupScale(uint localID, Vector3 scale, IClientAPI remoteClient) + { + SceneObjectGroup group = GetGroupByPrim(localID); + if (group != null) + { + if (m_parentScene.ExternalChecks.ExternalChecksCanEditObject(group.UUID, remoteClient.AgentId)) + { + group.GroupResize(scale, localID); + } + } + } + + /// + /// This handles the nifty little tool tip that you get when you drag your mouse over an object + /// Send to the Object Group to process. We don't know enough to service the request + /// + /// + /// + /// + /// + protected internal void RequestObjectPropertiesFamily( + IClientAPI remoteClient, UUID AgentID, uint RequestFlags, UUID ObjectID) + { + SceneObjectGroup group = GetGroupByPrim(ObjectID); + if (group != null) + { + group.ServiceObjectPropertiesFamilyRequest(remoteClient, AgentID, RequestFlags); + } + } + + /// + /// + /// + /// + /// + /// + protected internal void UpdatePrimSingleRotation(uint localID, Quaternion rot, IClientAPI remoteClient) + { + SceneObjectGroup group = GetGroupByPrim(localID); + if (group != null) + { + if (m_parentScene.ExternalChecks.ExternalChecksCanMoveObject(group.UUID, remoteClient.AgentId)) + { + group.UpdateSingleRotation(rot, localID); + } + } + } + + /// + /// + /// + /// + /// + /// + protected internal void UpdatePrimRotation(uint localID, Quaternion rot, IClientAPI remoteClient) + { + SceneObjectGroup group = GetGroupByPrim(localID); + if (group != null) + { + if (m_parentScene.ExternalChecks.ExternalChecksCanMoveObject(group.UUID, remoteClient.AgentId)) + { + group.UpdateGroupRotation(rot); + } + } + } + + /// + /// + /// + /// + /// + /// + /// + protected internal void UpdatePrimRotation(uint localID, Vector3 pos, Quaternion rot, IClientAPI remoteClient) + { + SceneObjectGroup group = GetGroupByPrim(localID); + if (group != null) + { + if (m_parentScene.ExternalChecks.ExternalChecksCanMoveObject(group.UUID, remoteClient.AgentId)) + { + group.UpdateGroupRotation(pos, rot); + } + } + } + + /// + /// Update the position of the given part + /// + /// + /// + /// + protected internal void UpdatePrimSinglePosition(uint localID, Vector3 pos, IClientAPI remoteClient) + { + SceneObjectGroup group = GetGroupByPrim(localID); + if (group != null) + { + if (m_parentScene.ExternalChecks.ExternalChecksCanMoveObject(group.UUID, remoteClient.AgentId) || group.IsAttachment) + { + group.UpdateSinglePosition(pos, localID); + } + } + } + + /// + /// Update the position of the given part + /// + /// + /// + /// + protected internal void UpdatePrimPosition(uint localID, Vector3 pos, IClientAPI remoteClient) + { + SceneObjectGroup group = GetGroupByPrim(localID); + if (group != null) + { + + // Vector3 oldPos = group.AbsolutePosition; + if (group.IsAttachment) + { + group.UpdateGroupPosition(pos); + } + else + { + if (m_parentScene.ExternalChecks.ExternalChecksCanMoveObject(group.UUID, remoteClient.AgentId) || group.IsAttachment) + { + group.UpdateGroupPosition(pos); + } + } + } + } + + /// + /// + /// + /// + /// + /// + protected internal void UpdatePrimTexture(uint localID, byte[] texture, IClientAPI remoteClient) + { + SceneObjectGroup group = GetGroupByPrim(localID); + if (group != null) + { + if (m_parentScene.ExternalChecks.ExternalChecksCanEditObject(group.UUID,remoteClient.AgentId)) + { + group.UpdateTextureEntry(localID, texture); + } + } + } + + /// + /// + /// + /// + /// + /// + protected internal void UpdatePrimFlags(uint localID, bool UsePhysics, bool IsTemporary, bool IsPhantom, IClientAPI remoteClient) + { + SceneObjectGroup group = GetGroupByPrim(localID); + if (group != null) + { + if (m_parentScene.ExternalChecks.ExternalChecksCanEditObject(group.UUID, remoteClient.AgentId)) + { + group.UpdatePrimFlags(localID, UsePhysics, IsTemporary, IsPhantom); + } + } + } + + /// + /// Move the given object + /// + /// + /// + /// + /// + protected internal void MoveObject(UUID objectID, Vector3 offset, Vector3 pos, IClientAPI remoteClient, List surfaceArgs) + { + SceneObjectGroup group = GetGroupByPrim(objectID); + if (group != null) + { + if (m_parentScene.ExternalChecks.ExternalChecksCanMoveObject(group.UUID, remoteClient.AgentId))// && PermissionsMngr.) + { + group.GrabMovement(offset, pos, remoteClient); + } + // This is outside the above permissions condition + // so that if the object is locked the client moving the object + // get's it's position on the simulator even if it was the same as before + // This keeps the moving user's client in sync with the rest of the world. + group.SendGroupTerseUpdate(); + } + } + + /// + /// + /// + /// + /// + protected internal void PrimName(IClientAPI remoteClient, uint primLocalID, string name) + { + SceneObjectGroup group = GetGroupByPrim(primLocalID); + if (group != null) + { + if (m_parentScene.ExternalChecks.ExternalChecksCanEditObject(group.UUID, remoteClient.AgentId)) + { + group.SetPartName(Util.CleanString(name), primLocalID); + group.HasGroupChanged = true; + } + } + } + + /// + /// + /// + /// + /// + protected internal void PrimDescription(IClientAPI remoteClient, uint primLocalID, string description) + { + SceneObjectGroup group = GetGroupByPrim(primLocalID); + if (group != null) + { + if (m_parentScene.ExternalChecks.ExternalChecksCanEditObject(group.UUID, remoteClient.AgentId)) + { + group.SetPartDescription(Util.CleanString(description), primLocalID); + group.HasGroupChanged = true; + } + } + } + + protected internal void PrimClickAction(IClientAPI remoteClient, uint primLocalID, string clickAction) + { + SceneObjectGroup group = GetGroupByPrim(primLocalID); + if (group != null) + { + if (m_parentScene.ExternalChecks.ExternalChecksCanEditObject(group.UUID, remoteClient.AgentId)) + { + SceneObjectPart part = m_parentScene.GetSceneObjectPart(primLocalID); + part.ClickAction = Convert.ToByte(clickAction); + group.HasGroupChanged = true; + } + } + } + + protected internal void PrimMaterial(IClientAPI remoteClient, uint primLocalID, string material) + { + SceneObjectGroup group = GetGroupByPrim(primLocalID); + if (group != null) + { + if (m_parentScene.ExternalChecks.ExternalChecksCanEditObject(group.UUID, remoteClient.AgentId)) + { + SceneObjectPart part = m_parentScene.GetSceneObjectPart(primLocalID); + part.Material = Convert.ToByte(material); + group.HasGroupChanged = true; + } + } + } + + protected internal void UpdateExtraParam(UUID agentID, uint primLocalID, ushort type, bool inUse, byte[] data) + { + SceneObjectGroup group = GetGroupByPrim(primLocalID); + + if (group != null) + { + if (m_parentScene.ExternalChecks.ExternalChecksCanEditObject(group.UUID,agentID)) + { + group.UpdateExtraParam(primLocalID, type, inUse, data); + } + } + } + + /// + /// + /// + /// + /// + protected internal void UpdatePrimShape(UUID agentID, uint primLocalID, UpdateShapeArgs shapeBlock) + { + SceneObjectGroup group = GetGroupByPrim(primLocalID); + if (group != null) + { + if (m_parentScene.ExternalChecks.ExternalChecksCanEditObject(group.GetPartsFullID(primLocalID), agentID)) + { + ObjectShapePacket.ObjectDataBlock shapeData = new ObjectShapePacket.ObjectDataBlock(); + shapeData.ObjectLocalID = shapeBlock.ObjectLocalID; + shapeData.PathBegin = shapeBlock.PathBegin; + shapeData.PathCurve = shapeBlock.PathCurve; + shapeData.PathEnd = shapeBlock.PathEnd; + shapeData.PathRadiusOffset = shapeBlock.PathRadiusOffset; + shapeData.PathRevolutions = shapeBlock.PathRevolutions; + shapeData.PathScaleX = shapeBlock.PathScaleX; + shapeData.PathScaleY = shapeBlock.PathScaleY; + shapeData.PathShearX = shapeBlock.PathShearX; + shapeData.PathShearY = shapeBlock.PathShearY; + shapeData.PathSkew = shapeBlock.PathSkew; + shapeData.PathTaperX = shapeBlock.PathTaperX; + shapeData.PathTaperY = shapeBlock.PathTaperY; + shapeData.PathTwist = shapeBlock.PathTwist; + shapeData.PathTwistBegin = shapeBlock.PathTwistBegin; + shapeData.ProfileBegin = shapeBlock.ProfileBegin; + shapeData.ProfileCurve = shapeBlock.ProfileCurve; + shapeData.ProfileEnd = shapeBlock.ProfileEnd; + shapeData.ProfileHollow = shapeBlock.ProfileHollow; + + group.UpdateShape(shapeData, primLocalID); + } + } + } + + /// + /// Initial method invoked when we receive a link objects request from the client. + /// + /// + /// + /// + protected internal void LinkObjects(IClientAPI client, uint parentPrim, List childPrims) + { + List EntityList = GetEntities(); + + SceneObjectGroup parenPrim = null; + foreach (EntityBase ent in EntityList) + { + if (ent is SceneObjectGroup) + { + if (((SceneObjectGroup)ent).LocalId == parentPrim) + { + parenPrim = (SceneObjectGroup)ent; + break; + } + } + } + + List children = new List(); + if (parenPrim != null) + { + // We do this in reverse to get the link order of the prims correct + for (int i = childPrims.Count - 1; i >= 0; i--) + { + foreach (EntityBase ent in EntityList) + { + if (ent is SceneObjectGroup) + { + if (((SceneObjectGroup)ent).LocalId == childPrims[i]) + { + // 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); + } + } + } + } + } + + foreach (SceneObjectGroup sceneObj in children) + { + parenPrim.LinkToGroup(sceneObj); + } + + // 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); + if (client != null) + parenPrim.GetProperties(client); + else + { + foreach (ScenePresence p in ScenePresences.Values) + { + parenPrim.GetProperties(p.ControllingClient); + } + } + } + + /// + /// Delink a linkset + /// + /// + protected internal void DelinkObjects(List primIds) + { + DelinkObjects(primIds, true); + } + + protected internal void DelinkObjects(List 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 sceneObjects = new Dictionary(); + + List EntityList = GetEntities(); + foreach (EntityBase ent in EntityList) + { + if (ent is SceneObjectGroup) + { + SceneObjectGroup obj = (SceneObjectGroup)ent; + sceneObjects.Add(obj.LocalId, obj); + + } + } + + // 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])) + { + parenPrim = sceneObjects[primIds[i]]; + primIds.RemoveAt(i); + break; + } + } + + if (parenPrim != null) + { + foreach (uint childPrimId in primIds) + { + parenPrim.DelinkFromGroup(childPrimId, sendEvents); + } + + if (parenPrim.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; + if (sendEvents) + parenPrim.TriggerScriptChangedEvent(Changed.LINK); + } + else + { + // The link set has prims remaining. This path is taken + // when a subset of a link set's prims are selected + // and the root prim is part of that selection + // + List parts = new List(parenPrim.Children.Values); + + List unlink_ids = new List(); + foreach (SceneObjectPart unlink_part in parts) + unlink_ids.Add(unlink_part.LocalId); + + // Tear down the remaining link set + // + if (unlink_ids.Count == 2) + { + DelinkObjects(unlink_ids, true); + return; + } + + DelinkObjects(unlink_ids, false); + + // Send event to root prim, then we're done with it + parenPrim.TriggerScriptChangedEvent(Changed.LINK); + + unlink_ids.Remove(parenPrim.RootPart.LocalId); + + foreach (uint localId in unlink_ids) + { + SceneObjectPart nr = GetSceneObjectPart(localId); + nr.UpdateFlag = 0; + } + + uint newRoot = unlink_ids[0]; + unlink_ids.Remove(newRoot); + + LinkObjects(null, newRoot, unlink_ids); + } + } + else + { + // The selected prims were all child prims. Edit linked parts + // without the root prim selected will get us here + // + List parents = new List(); + + // 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 + // which one is the root prim + 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]); + delinkedSomething = true; + if (!parents.Contains(grp)) + parents.Add(grp); + } + + } + } + if (!delinkedSomething) + { + m_log.InfoFormat("[SCENE]: " + + "DelinkObjects(): Could not find a root prim out of {0} as given to a delink request!", + primIds); + } + else + { + foreach (SceneObjectGroup g in parents) + { + g.TriggerScriptChangedEvent(Changed.LINK); + } + } + } + } + + protected internal void MakeObjectSearchable(IClientAPI remoteClient, bool IncludeInSearch, uint localID) + { + UUID user = remoteClient.AgentId; + UUID objid = UUID.Zero; + SceneObjectPart obj = null; + + List EntityList = GetEntities(); + foreach (EntityBase ent in EntityList) + { + if (ent is SceneObjectGroup) + { + foreach (KeyValuePair subent in ((SceneObjectGroup)ent).Children) + { + if (subent.Value.LocalId == localID) + { + objid = subent.Key; + obj = subent.Value; + } + } + } + } + + //Protip: In my day, we didn't call them searchable objects, we called them limited point-to-point joints + //aka ObjectFlags.JointWheel = IncludeInSearch + + //Permissions model: Object can be REMOVED from search IFF: + // * User owns object + //use CanEditObject + + //Object can be ADDED to search IFF: + // * User owns object + // * Asset/DRM permission bit "modify" is enabled + //use CanEditObjectPosition + + // libomv will complain about PrimFlags.JointWheel being + // deprecated, so we + #pragma warning disable 0612 + if (IncludeInSearch && m_parentScene.ExternalChecks.ExternalChecksCanEditObject(objid, user)) + { + obj.ParentGroup.RootPart.AddFlag(PrimFlags.JointWheel); + obj.ParentGroup.HasGroupChanged = true; + } + else if (!IncludeInSearch && m_parentScene.ExternalChecks.ExternalChecksCanMoveObject(objid,user)) + { + obj.ParentGroup.RootPart.RemFlag(PrimFlags.JointWheel); + obj.ParentGroup.HasGroupChanged = true; + } + #pragma warning restore 0612 + } + + /// + /// Duplicate the given object, Fire and Forget, No rotation, no return wrapper + /// + /// + /// + /// + protected internal void DuplicateObject(uint originalPrim, Vector3 offset, uint flags, UUID AgentID, UUID GroupID) + { + //m_log.DebugFormat("[SCENE]: Duplication of object {0} at offset {1} requested by agent {2}", originalPrim, offset, AgentID); + + // SceneObjectGroup dupe = DuplicateObject(originalPrim, offset, flags, AgentID, GroupID, Quaternion.Zero); + DuplicateObject(originalPrim, offset, flags, AgentID, GroupID, Quaternion.Identity); + } + /// + /// Duplicate the given object. + /// + /// + /// + /// + protected internal SceneObjectGroup DuplicateObject(uint originalPrim, 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 EntityList = GetEntities(); + + SceneObjectGroup originPrim = null; + foreach (EntityBase ent in EntityList) + { + if (ent is SceneObjectGroup) + { + if (((SceneObjectGroup)ent).LocalId == originalPrim) + { + originPrim = (SceneObjectGroup)ent; + break; + } + } + } + + if (originPrim != null) + { + if (m_parentScene.ExternalChecks.ExternalChecksCanDuplicateObject(originPrim.Children.Count, originPrim.UUID, AgentID, originPrim.AbsolutePosition)) + { + SceneObjectGroup copy = originPrim.Copy(AgentID, GroupID, true); + copy.AbsolutePosition = copy.AbsolutePosition + offset; + copy.ResetIDs(); + + lock (Entities) + { + Entities.Add(copy.UUID, copy); + } + + // Since we copy from a source group that is in selected + // state, but the copy is shown deselected in the viewer, + // We need to clear the selection flag here, else that + // prim never gets persisted at all. The client doesn't + // think it's selected, so it will never send a deselect... + copy.IsSelected = false; + + m_numPrim += copy.Children.Count; + + if (rot != Quaternion.Identity) + { + copy.UpdateGroupRotation(rot); + } + + copy.CreateScriptInstances(0, false, m_parentScene.DefaultScriptEngine, 0); + copy.HasGroupChanged = true; + copy.ScheduleGroupForFullUpdate(); + return copy; + } + } + else + { + m_log.WarnFormat("[SCENE]: Attempted to duplicate nonexistant prim id {0}", GroupID); + } + return null; + } + + /// + /// Calculates the distance between two Vector3s + /// + /// + /// + /// + protected internal float Vector3Distance(Vector3 v1, Vector3 v2) + { + // We don't really need the double floating point precision... + // so casting it to a single + + return + (float) + Math.Sqrt((v1.X - v2.X) * (v1.X - v2.X) + (v1.Y - v2.Y) * (v1.Y - v2.Y) + (v1.Z - v2.Z) * (v1.Z - v2.Z)); + } + + #endregion + } +} -- cgit v1.1