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 --- .../Modules/ContentManagementSystem/CMModel.cs | 2 +- .../Archiver/ArchiveWriteRequestPreparation.cs | 2 +- .../Modules/World/Estate/EstateManagementModule.cs | 2 +- OpenSim/Region/Environment/Scenes/InnerScene.cs | 1719 ------------------- .../Region/Environment/Scenes/Scene.Inventory.cs | 7 +- OpenSim/Region/Environment/Scenes/Scene.cs | 194 +-- OpenSim/Region/Environment/Scenes/SceneGraph.cs | 1724 ++++++++++++++++++++ .../Region/Environment/Scenes/SceneObjectGroup.cs | 6 +- OpenSim/Region/Environment/Scenes/ScenePresence.cs | 2 +- OpenSim/Region/Physics/Manager/PhysicsScene.cs | 2 +- 10 files changed, 1836 insertions(+), 1824 deletions(-) delete mode 100644 OpenSim/Region/Environment/Scenes/InnerScene.cs create mode 100644 OpenSim/Region/Environment/Scenes/SceneGraph.cs (limited to 'OpenSim/Region') diff --git a/OpenSim/Region/Environment/Modules/ContentManagementSystem/CMModel.cs b/OpenSim/Region/Environment/Modules/ContentManagementSystem/CMModel.cs index e58d945..5229489 100644 --- a/OpenSim/Region/Environment/Modules/ContentManagementSystem/CMModel.cs +++ b/OpenSim/Region/Environment/Modules/ContentManagementSystem/CMModel.cs @@ -261,7 +261,7 @@ namespace OpenSim.Region.Environment.Modules.ContentManagement ((SceneObjectGroup)scene.Entities[uuid]).DetachFromBackup(); scene.PhysicsScene.RemovePrim(((SceneObjectGroup)scene.Entities[uuid]).RootPart.PhysActor); scene.SendKillObject(scene.Entities[uuid].LocalId); - scene.m_innerScene.DeleteSceneObject(uuid, false); + scene.m_sceneGraph.DeleteSceneObject(uuid, false); ((SceneObjectGroup)scene.Entities[uuid]).DeleteGroup(false); } catch(Exception e) diff --git a/OpenSim/Region/Environment/Modules/World/Archiver/ArchiveWriteRequestPreparation.cs b/OpenSim/Region/Environment/Modules/World/Archiver/ArchiveWriteRequestPreparation.cs index 0202841..bb3a18d 100644 --- a/OpenSim/Region/Environment/Modules/World/Archiver/ArchiveWriteRequestPreparation.cs +++ b/OpenSim/Region/Environment/Modules/World/Archiver/ArchiveWriteRequestPreparation.cs @@ -260,7 +260,7 @@ namespace OpenSim.Region.Environment.Modules.World.Archiver List sceneObjects = new List(); // Filter entities so that we only have scene objects. - // FIXME: Would be nicer to have this as a proper list in InnerScene, since lots of methods + // FIXME: Would be nicer to have this as a proper list in SceneGraph, since lots of methods // end up having to do this foreach (EntityBase entity in entities) { diff --git a/OpenSim/Region/Environment/Modules/World/Estate/EstateManagementModule.cs b/OpenSim/Region/Environment/Modules/World/Estate/EstateManagementModule.cs index c38d502..52175f4 100644 --- a/OpenSim/Region/Environment/Modules/World/Estate/EstateManagementModule.cs +++ b/OpenSim/Region/Environment/Modules/World/Estate/EstateManagementModule.cs @@ -490,7 +490,7 @@ namespace OpenSim.Region.Environment.Modules.World.Estate } else if (reportType == 0) { - SceneData = m_scene.m_innerScene.GetTopScripts(); + SceneData = m_scene.m_sceneGraph.GetTopScripts(); } List SceneReport = new List(); diff --git a/OpenSim/Region/Environment/Scenes/InnerScene.cs b/OpenSim/Region/Environment/Scenes/InnerScene.cs deleted file mode 100644 index c8f4a72..0000000 --- a/OpenSim/Region/Environment/Scenes/InnerScene.cs +++ /dev/null @@ -1,1719 +0,0 @@ -/* - * 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(); - - public class InnerScene - { - 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 InnerScene(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 - } -} diff --git a/OpenSim/Region/Environment/Scenes/Scene.Inventory.cs b/OpenSim/Region/Environment/Scenes/Scene.Inventory.cs index 7e32086..c6ad94c 100644 --- a/OpenSim/Region/Environment/Scenes/Scene.Inventory.cs +++ b/OpenSim/Region/Environment/Scenes/Scene.Inventory.cs @@ -2342,7 +2342,7 @@ namespace OpenSim.Region.Environment.Scenes public UUID RezSingleAttachment(IClientAPI remoteClient, UUID itemID, uint AttachmentPt) { - SceneObjectGroup att = m_innerScene.RezSingleAttachment(remoteClient, itemID, AttachmentPt); + SceneObjectGroup att = m_sceneGraph.RezSingleAttachment(remoteClient, itemID, AttachmentPt); if (att == null) { @@ -2375,7 +2375,7 @@ namespace OpenSim.Region.Environment.Scenes public void AttachObject(IClientAPI controllingClient, uint localID, uint attachPoint, Quaternion rot, Vector3 pos, bool silent) { - m_innerScene.AttachObject(controllingClient, localID, attachPoint, rot, pos, silent); + m_sceneGraph.AttachObject(controllingClient, localID, attachPoint, rot, pos, silent); } public void AttachObject(IClientAPI remoteClient, uint AttachmentPt, UUID itemID, SceneObjectGroup att) @@ -2455,7 +2455,8 @@ namespace OpenSim.Region.Environment.Scenes } } - m_innerScene.DetachSingleAttachmentToInv(itemID, remoteClient); + + m_sceneGraph.DetachSingleAttachmentToInv(itemID, remoteClient); } public void GetScriptRunning(IClientAPI controllingClient, UUID objectID, UUID itemID) diff --git a/OpenSim/Region/Environment/Scenes/Scene.cs b/OpenSim/Region/Environment/Scenes/Scene.cs index 0d0823e..e5d0c3f 100644 --- a/OpenSim/Region/Environment/Scenes/Scene.cs +++ b/OpenSim/Region/Environment/Scenes/Scene.cs @@ -71,7 +71,11 @@ namespace OpenSim.Region.Environment.Scenes protected List m_regionRestartNotifyList = new List(); protected List m_neighbours = new List(); - public InnerScene m_innerScene; + /// + /// The scene graph for this scene + /// + /// TODO: Possibly stop other classes being able to manipulate this directly. + public SceneGraph m_sceneGraph; private int m_timePhase = 24; @@ -179,14 +183,14 @@ namespace OpenSim.Region.Environment.Scenes // an instance to the physics plugin's Scene object. public PhysicsScene PhysicsScene { - set { m_innerScene.PhysicsScene = value; } - get { return (m_innerScene.PhysicsScene); } + set { m_sceneGraph.PhysicsScene = value; } + get { return (m_sceneGraph.PhysicsScene); } } // This gets locked so things stay thread safe. public object SyncRoot { - get { return m_innerScene.m_syncRoot; } + get { return m_sceneGraph.m_syncRoot; } } public float TimeDilation @@ -212,23 +216,23 @@ namespace OpenSim.Region.Environment.Scenes get { return m_defaultScriptEngine; } } - // Local reference to the objects in the scene (which are held in innerScene) + // Local reference to the objects in the scene (which are held in the scenegraph) // public Dictionary Objects // { - // get { return m_innerScene.SceneObjects; } + // get { return m_sceneGraph.SceneObjects; } // } // Reference to all of the agents in the scene (root and child) protected Dictionary m_scenePresences { - get { return m_innerScene.ScenePresences; } - set { m_innerScene.ScenePresences = value; } + get { return m_sceneGraph.ScenePresences; } + set { m_sceneGraph.ScenePresences = value; } } // protected Dictionary m_sceneObjects // { - // get { return m_innerScene.SceneObjects; } - // set { m_innerScene.SceneObjects = value; } + // get { return m_sceneGraph.SceneObjects; } + // set { m_sceneGraph.SceneObjects = value; } // } /// @@ -241,14 +245,14 @@ namespace OpenSim.Region.Environment.Scenes /// public Dictionary Entities { - get { return m_innerScene.Entities; } - set { m_innerScene.Entities = value; } + get { return m_sceneGraph.Entities; } + set { m_sceneGraph.Entities = value; } } public Dictionary m_restorePresences { - get { return m_innerScene.RestorePresences; } - set { m_innerScene.RestorePresences = value; } + get { return m_sceneGraph.RestorePresences; } + set { m_sceneGraph.RestorePresences = value; } } public int objectCapacity = 45000; @@ -296,15 +300,15 @@ namespace OpenSim.Region.Environment.Scenes EventManager.OnLandObjectRemoved += new EventManager.LandObjectRemoved(m_storageManager.DataStore.RemoveLandObject); - m_innerScene = new InnerScene(this, m_regInfo); + m_sceneGraph = new SceneGraph(this, m_regInfo); - // If the Inner scene has an Unrecoverable error, restart this sim. + // If the scene graph has an Unrecoverable error, restart this sim. // Currently the only thing that causes it to happen is two kinds of specific // Physics based crashes. // // Out of memory // Operating system has killed the plugin - m_innerScene.UnRecoverableError += RestartNow; + m_sceneGraph.UnRecoverableError += RestartNow; RegisterDefaultSceneEvents(); @@ -644,11 +648,13 @@ namespace OpenSim.Region.Environment.Scenes // Stop all client threads. ForEachScenePresence(delegate(ScenePresence avatar) { avatar.ControllingClient.Close(true); }); + // Stop updating the scene objects and agents. //m_heartbeatTimer.Close(); shuttingdown = true; - // close the inner scene - m_innerScene.Close(); + + m_sceneGraph.Close(); + // De-register with region communications (events cleanup) UnRegisterRegionWithComms(); @@ -726,7 +732,7 @@ namespace OpenSim.Region.Environment.Scenes //updateLock.WaitOne(); float physicsFPS = 0; //m_log.Info("sadfadf" + m_neighbours.Count.ToString()); - int agentsInScene = m_innerScene.GetRootAgentCount() + m_innerScene.GetChildAgentCount(); + int agentsInScene = m_sceneGraph.GetRootAgentCount() + m_sceneGraph.GetChildAgentCount(); if (agentsInScene > 21) { @@ -757,15 +763,15 @@ namespace OpenSim.Region.Environment.Scenes physicsMS2 = System.Environment.TickCount; if ((m_frame % m_update_physics == 0) && m_physics_enabled) - m_innerScene.UpdatePreparePhysics(); + m_sceneGraph.UpdatePreparePhysics(); physicsMS2 = System.Environment.TickCount - physicsMS2; if (m_frame % m_update_entitymovement == 0) - m_innerScene.UpdateEntityMovement(); + m_sceneGraph.UpdateEntityMovement(); physicsMS = System.Environment.TickCount; if ((m_frame % m_update_physics == 0) && m_physics_enabled) - physicsFPS = m_innerScene.UpdatePhysics( + physicsFPS = m_sceneGraph.UpdatePhysics( Math.Max(SinceLastFrame.TotalSeconds, m_timespan) ); if (m_frame % m_update_physics == 0 && SynchronizeScene != null) @@ -777,16 +783,16 @@ namespace OpenSim.Region.Environment.Scenes otherMS = System.Environment.TickCount; // run through all entities looking for updates (slow) if (m_frame % m_update_entities == 0) - m_innerScene.UpdateEntities(); + m_sceneGraph.UpdateEntities(); // run through entities that have scheduled themselves for // updates looking for updates(faster) if (m_frame % m_update_entitiesquick == 0) - m_innerScene.ProcessUpdates(); + m_sceneGraph.ProcessUpdates(); // Run through scenepresences looking for updates if (m_frame % m_update_presences == 0) - m_innerScene.UpdatePresences(); + m_sceneGraph.UpdatePresences(); // Delete temp-on-rez stuff if (m_frame % m_update_backup == 0) @@ -814,16 +820,16 @@ namespace OpenSim.Region.Environment.Scenes m_statsReporter.AddTimeDilation(m_timedilation); m_statsReporter.AddFPS(1); m_statsReporter.AddInPackets(0); - m_statsReporter.SetRootAgents(m_innerScene.GetRootAgentCount()); - m_statsReporter.SetChildAgents(m_innerScene.GetChildAgentCount()); - m_statsReporter.SetObjects(m_innerScene.GetTotalObjectsCount()); - m_statsReporter.SetActiveObjects(m_innerScene.GetActiveObjectsCount()); + m_statsReporter.SetRootAgents(m_sceneGraph.GetRootAgentCount()); + m_statsReporter.SetChildAgents(m_sceneGraph.GetChildAgentCount()); + m_statsReporter.SetObjects(m_sceneGraph.GetTotalObjectsCount()); + m_statsReporter.SetActiveObjects(m_sceneGraph.GetActiveObjectsCount()); frameMS = System.Environment.TickCount - frameMS; m_statsReporter.addFrameMS(frameMS); m_statsReporter.addPhysicsMS(physicsMS); m_statsReporter.addOtherMS(otherMS); - m_statsReporter.SetActiveScripts(m_innerScene.GetActiveScriptsCount()); - m_statsReporter.addScriptLines(m_innerScene.GetScriptLPS()); + m_statsReporter.SetActiveScripts(m_sceneGraph.GetActiveScriptsCount()); + m_statsReporter.addScriptLines(m_sceneGraph.GetScriptLPS()); } } catch (NotImplementedException) @@ -1163,7 +1169,7 @@ namespace OpenSim.Region.Environment.Scenes //AXdirection = new Vector3(direction.X, direction.Y, direction.Z); //testRay = new Ray(AXOrigin, AXdirection); - //rt = m_innerScene.GetClosestIntersectingPrim(testRay); + //rt = m_sceneGraph.GetClosestIntersectingPrim(testRay); //if (rt.HitTF) //{ @@ -1642,7 +1648,7 @@ namespace OpenSim.Region.Environment.Scenes // TODO: Raytrace better here - //EntityIntersection ei = m_innerScene.GetClosestIntersectingPrim(new Ray(AXOrigin, AXdirection)); + //EntityIntersection ei = m_sceneGraph.GetClosestIntersectingPrim(new Ray(AXOrigin, AXdirection)); Ray NewRay = new Ray(AXOrigin, AXdirection); // Ray Trace against target here @@ -1676,7 +1682,7 @@ namespace OpenSim.Region.Environment.Scenes { // We don't have a target here, so we're going to raytrace all the objects in the scene. - EntityIntersection ei = m_innerScene.GetClosestIntersectingPrim(new Ray(AXOrigin, AXdirection), true, false); + EntityIntersection ei = m_sceneGraph.GetClosestIntersectingPrim(new Ray(AXOrigin, AXdirection), true, false); // Un-comment the following line to print the raytrace results to the console. //m_log.Info("[RAYTRACERESULTS]: Hit:" + ei.HitTF.ToString() + " Point: " + ei.ipoint.ToString() + " Normal: " + ei.normal.ToString()); @@ -1790,7 +1796,7 @@ namespace OpenSim.Region.Environment.Scenes public bool AddRestoredSceneObject( SceneObjectGroup sceneObject, bool attachToBackup, bool alreadyPersisted) { - return m_innerScene.AddRestoredSceneObject(sceneObject, attachToBackup, alreadyPersisted); + return m_sceneGraph.AddRestoredSceneObject(sceneObject, attachToBackup, alreadyPersisted); } /// @@ -1803,7 +1809,7 @@ namespace OpenSim.Region.Environment.Scenes /// public bool AddNewSceneObject(SceneObjectGroup sceneObject, bool attachToBackup) { - return m_innerScene.AddNewSceneObject(sceneObject, attachToBackup); + return m_sceneGraph.AddNewSceneObject(sceneObject, attachToBackup); } /// @@ -1869,7 +1875,7 @@ namespace OpenSim.Region.Environment.Scenes /// true if the object was in the scene, false if it was not public bool UnlinkSceneObject(UUID uuid, bool resultOfLinkingObjects) { - if (m_innerScene.DeleteSceneObject(uuid, resultOfLinkingObjects)) + if (m_sceneGraph.DeleteSceneObject(uuid, resultOfLinkingObjects)) { if (!resultOfLinkingObjects) m_storageManager.DataStore.RemoveObject(uuid, @@ -2203,7 +2209,7 @@ namespace OpenSim.Region.Environment.Scenes presence.initializeScenePresence(client, RegionInfo, this); - m_innerScene.AddScenePresence(presence); + m_sceneGraph.AddScenePresence(presence); lock (m_restorePresences) { @@ -2228,40 +2234,40 @@ namespace OpenSim.Region.Environment.Scenes { client.OnRegionHandShakeReply += SendLayerData; client.OnAddPrim += AddNewPrim; - client.OnUpdatePrimGroupPosition += m_innerScene.UpdatePrimPosition; - client.OnUpdatePrimSinglePosition += m_innerScene.UpdatePrimSinglePosition; - client.OnUpdatePrimGroupRotation += m_innerScene.UpdatePrimRotation; - client.OnUpdatePrimGroupMouseRotation += m_innerScene.UpdatePrimRotation; - client.OnUpdatePrimSingleRotation += m_innerScene.UpdatePrimSingleRotation; - client.OnUpdatePrimScale += m_innerScene.UpdatePrimScale; - client.OnUpdatePrimGroupScale += m_innerScene.UpdatePrimGroupScale; - client.OnUpdateExtraParams += m_innerScene.UpdateExtraParam; - client.OnUpdatePrimShape += m_innerScene.UpdatePrimShape; + client.OnUpdatePrimGroupPosition += m_sceneGraph.UpdatePrimPosition; + client.OnUpdatePrimSinglePosition += m_sceneGraph.UpdatePrimSinglePosition; + client.OnUpdatePrimGroupRotation += m_sceneGraph.UpdatePrimRotation; + client.OnUpdatePrimGroupMouseRotation += m_sceneGraph.UpdatePrimRotation; + client.OnUpdatePrimSingleRotation += m_sceneGraph.UpdatePrimSingleRotation; + client.OnUpdatePrimScale += m_sceneGraph.UpdatePrimScale; + client.OnUpdatePrimGroupScale += m_sceneGraph.UpdatePrimGroupScale; + client.OnUpdateExtraParams += m_sceneGraph.UpdateExtraParam; + client.OnUpdatePrimShape += m_sceneGraph.UpdatePrimShape; //client.OnRequestMapBlocks += RequestMapBlocks; // handled in a module now. - client.OnUpdatePrimTexture += m_innerScene.UpdatePrimTexture; + client.OnUpdatePrimTexture += m_sceneGraph.UpdatePrimTexture; client.OnTeleportLocationRequest += RequestTeleportLocation; client.OnTeleportLandmarkRequest += RequestTeleportLandmark; client.OnObjectSelect += SelectPrim; client.OnObjectDeselect += DeselectPrim; - client.OnGrabUpdate += m_innerScene.MoveObject; + client.OnGrabUpdate += m_sceneGraph.MoveObject; client.OnDeRezObject += DeRezObject; client.OnRezObject += RezObject; client.OnRezSingleAttachmentFromInv += RezSingleAttachment; client.OnDetachAttachmentIntoInv += DetachSingleAttachmentToInv; - client.OnObjectAttach += m_innerScene.AttachObject; - client.OnObjectDetach += m_innerScene.DetachObject; - client.OnObjectDrop += m_innerScene.DropObject; + client.OnObjectAttach += m_sceneGraph.AttachObject; + client.OnObjectDetach += m_sceneGraph.DetachObject; + client.OnObjectDrop += m_sceneGraph.DropObject; client.OnNameFromUUIDRequest += CommsManager.HandleUUIDNameRequest; - client.OnObjectDescription += m_innerScene.PrimDescription; - client.OnObjectName += m_innerScene.PrimName; - client.OnObjectClickAction += m_innerScene.PrimClickAction; - client.OnObjectMaterial += m_innerScene.PrimMaterial; - client.OnLinkObjects += m_innerScene.LinkObjects; - client.OnDelinkObjects += m_innerScene.DelinkObjects; - client.OnObjectDuplicate += m_innerScene.DuplicateObject; + client.OnObjectDescription += m_sceneGraph.PrimDescription; + client.OnObjectName += m_sceneGraph.PrimName; + client.OnObjectClickAction += m_sceneGraph.PrimClickAction; + client.OnObjectMaterial += m_sceneGraph.PrimMaterial; + client.OnLinkObjects += m_sceneGraph.LinkObjects; + client.OnDelinkObjects += m_sceneGraph.DelinkObjects; + client.OnObjectDuplicate += m_sceneGraph.DuplicateObject; client.OnObjectDuplicateOnRay += doObjectDuplicateOnRay; - client.OnUpdatePrimFlags += m_innerScene.UpdatePrimFlags; - client.OnRequestObjectPropertiesFamily += m_innerScene.RequestObjectPropertiesFamily; + client.OnUpdatePrimFlags += m_sceneGraph.UpdatePrimFlags; + client.OnRequestObjectPropertiesFamily += m_sceneGraph.RequestObjectPropertiesFamily; client.OnRequestGodlikePowers += handleRequestGodlikePowers; client.OnGodKickUser += HandleGodlikeKickUser; client.OnObjectPermissions += HandleObjectPermissionsUpdate; @@ -2287,11 +2293,11 @@ namespace OpenSim.Region.Environment.Scenes client.OnMoneyTransferRequest += ProcessMoneyTransferRequest; client.OnParcelBuy += ProcessParcelBuy; client.OnAvatarPickerRequest += ProcessAvatarPickerRequest; - client.OnObjectIncludeInSearch += m_innerScene.MakeObjectSearchable; + client.OnObjectIncludeInSearch += m_sceneGraph.MakeObjectSearchable; client.OnTeleportHomeRequest += TeleportClientHome; client.OnSetStartLocationRequest += SetHomeRezPoint; - client.OnUndo += m_innerScene.HandleUndo; - client.OnObjectGroupRequest += m_innerScene.HandleObjectGroupUpdate; + client.OnUndo += m_sceneGraph.HandleUndo; + client.OnObjectGroupRequest += m_sceneGraph.HandleObjectGroupUpdate; client.OnParcelReturnObjectsRequest += LandChannel.ReturnObjectsInParcel; client.OnParcelSetOtherCleanTime += LandChannel.SetParcelOtherCleanTime; client.OnObjectSaleInfo += ObjectSaleInfo; @@ -2403,7 +2409,7 @@ namespace OpenSim.Region.Environment.Scenes // TODO: Raytrace better here - //EntityIntersection ei = m_innerScene.GetClosestIntersectingPrim(new Ray(AXOrigin, AXdirection)); + //EntityIntersection ei = m_sceneGraph.GetClosestIntersectingPrim(new Ray(AXOrigin, AXdirection)); Ray NewRay = new Ray(AXOrigin, AXdirection); // Ray Trace against target here @@ -2433,14 +2439,14 @@ namespace OpenSim.Region.Environment.Scenes { Quaternion worldRot = target2.GetWorldRotation(); - // SceneObjectGroup obj = m_innerScene.DuplicateObject(localID, pos, target.GetEffectiveObjectFlags(), AgentID, GroupID, worldRot); - m_innerScene.DuplicateObject(localID, pos, target.GetEffectiveObjectFlags(), AgentID, GroupID, worldRot); + // SceneObjectGroup obj = m_sceneGraph.DuplicateObject(localID, pos, target.GetEffectiveObjectFlags(), AgentID, GroupID, worldRot); + m_sceneGraph.DuplicateObject(localID, pos, target.GetEffectiveObjectFlags(), AgentID, GroupID, worldRot); //obj.Rotation = worldRot; //obj.UpdateGroupRotation(worldRot); } else { - m_innerScene.DuplicateObject(localID, pos, target.GetEffectiveObjectFlags(), AgentID, GroupID); + m_sceneGraph.DuplicateObject(localID, pos, target.GetEffectiveObjectFlags(), AgentID, GroupID); } } @@ -2486,7 +2492,7 @@ namespace OpenSim.Region.Environment.Scenes AvatarAppearance appearance = null; GetAvatarAppearance(client, out appearance); - ScenePresence avatar = m_innerScene.CreateAndAddScenePresence(client, child, appearance); + ScenePresence avatar = m_sceneGraph.CreateAndAddScenePresence(client, child, appearance); return avatar; } @@ -2538,11 +2544,11 @@ namespace OpenSim.Region.Environment.Scenes if (avatar.IsChildAgent) { - m_innerScene.removeUserCount(false); + m_sceneGraph.removeUserCount(false); } else { - m_innerScene.removeUserCount(true); + m_sceneGraph.removeUserCount(true); m_sceneGridService.LogOffUser(agentID, RegionInfo.RegionID, RegionInfo.RegionHandle, avatar.AbsolutePosition, avatar.Lookat); List childknownRegions = new List(); List ckn = avatar.GetKnownRegionList(); @@ -2587,7 +2593,7 @@ namespace OpenSim.Region.Environment.Scenes agentTransactions.RemoveAgentAssetTransactions(agentID); } - m_innerScene.RemoveScenePresence(agentID); + m_sceneGraph.RemoveScenePresence(agentID); try { @@ -2807,7 +2813,7 @@ namespace OpenSim.Region.Environment.Scenes cap.ItemUpdatedCall = CapsUpdateInventoryItemAsset; cap.TaskScriptUpdatedCall = CapsUpdateTaskInventoryScriptAsset; cap.CAPSFetchInventoryDescendents = CommsManager.UserProfileCacheService.HandleFetchInventoryDescendentsCAPS; - cap.GetClient = m_innerScene.GetControllingClient; + cap.GetClient = m_sceneGraph.GetControllingClient; m_capsHandlers[agentId] = cap; } @@ -2917,18 +2923,18 @@ namespace OpenSim.Region.Environment.Scenes /// public bool CloseConnection(UUID agentID) { - ScenePresence presence = m_innerScene.GetScenePresence(agentID); + ScenePresence presence = m_sceneGraph.GetScenePresence(agentID); if (presence != null) { // Nothing is removed here, so down count it as such // if (presence.IsChildAgent) // { - // m_innerScene.removeUserCount(false); + // m_sceneGraph.removeUserCount(false); // } // else // { - // m_innerScene.removeUserCount(true); + // m_sceneGraph.removeUserCount(true); // } // Tell a single agent to disconnect from the region. @@ -3501,7 +3507,7 @@ namespace OpenSim.Region.Environment.Scenes } else { - m_innerScene.removeUserCount(!m_scenePresences[agentID].IsChildAgent); + m_sceneGraph.removeUserCount(!m_scenePresences[agentID].IsChildAgent); m_scenePresences[agentID].ControllingClient.Kick(Utils.BytesToString(reason)); m_scenePresences[agentID].ControllingClient.Close(true); @@ -3872,7 +3878,7 @@ namespace OpenSim.Region.Environment.Scenes #endregion - #region InnerScene wrapper methods + #region SceneGraph wrapper methods /// /// @@ -3881,22 +3887,22 @@ namespace OpenSim.Region.Environment.Scenes /// public UUID ConvertLocalIDToFullID(uint localID) { - return m_innerScene.ConvertLocalIDToFullID(localID); + return m_sceneGraph.ConvertLocalIDToFullID(localID); } public void SwapRootAgentCount(bool rootChildChildRootTF) { - m_innerScene.SwapRootChildAgent(rootChildChildRootTF); + m_sceneGraph.SwapRootChildAgent(rootChildChildRootTF); } public void AddPhysicalPrim(int num) { - m_innerScene.AddPhysicalPrim(num); + m_sceneGraph.AddPhysicalPrim(num); } public void RemovePhysicalPrim(int num) { - m_innerScene.RemovePhysicalPrim(num); + m_sceneGraph.RemovePhysicalPrim(num); } //The idea is to have a group of method that return a list of avatars meeting some requirement @@ -3909,7 +3915,7 @@ namespace OpenSim.Region.Environment.Scenes /// public List GetAvatars() { - return m_innerScene.GetAvatars(); + return m_sceneGraph.GetAvatars(); } /// @@ -3919,7 +3925,7 @@ namespace OpenSim.Region.Environment.Scenes /// public List GetScenePresences() { - return m_innerScene.GetScenePresences(); + return m_sceneGraph.GetScenePresences(); } /// @@ -3930,7 +3936,7 @@ namespace OpenSim.Region.Environment.Scenes /// public List GetScenePresences(FilterAvatarList filter) { - return m_innerScene.GetScenePresences(filter); + return m_sceneGraph.GetScenePresences(filter); } /// @@ -3940,7 +3946,7 @@ namespace OpenSim.Region.Environment.Scenes /// public ScenePresence GetScenePresence(UUID avatarID) { - return m_innerScene.GetScenePresence(avatarID); + return m_sceneGraph.GetScenePresence(avatarID); } /// @@ -4005,7 +4011,7 @@ namespace OpenSim.Region.Environment.Scenes /// public SceneObjectPart GetSceneObjectPart(string name) { - return m_innerScene.GetSceneObjectPart(name); + return m_sceneGraph.GetSceneObjectPart(name); } /// @@ -4015,7 +4021,7 @@ namespace OpenSim.Region.Environment.Scenes /// public SceneObjectPart GetSceneObjectPart(uint localID) { - return m_innerScene.GetSceneObjectPart(localID); + return m_sceneGraph.GetSceneObjectPart(localID); } /// @@ -4025,22 +4031,22 @@ namespace OpenSim.Region.Environment.Scenes /// public SceneObjectPart GetSceneObjectPart(UUID fullID) { - return m_innerScene.GetSceneObjectPart(fullID); + return m_sceneGraph.GetSceneObjectPart(fullID); } internal bool TryGetAvatar(UUID avatarId, out ScenePresence avatar) { - return m_innerScene.TryGetAvatar(avatarId, out avatar); + return m_sceneGraph.TryGetAvatar(avatarId, out avatar); } internal bool TryGetAvatarByName(string avatarName, out ScenePresence avatar) { - return m_innerScene.TryGetAvatarByName(avatarName, out avatar); + return m_sceneGraph.TryGetAvatarByName(avatarName, out avatar); } internal void ForEachClient(Action action) { - m_innerScene.ForEachClient(action); + m_sceneGraph.ForEachClient(action); } /// @@ -4050,7 +4056,7 @@ namespace OpenSim.Region.Environment.Scenes /// public List GetEntities() { - return m_innerScene.GetEntities(); + return m_sceneGraph.GetEntities(); } #endregion 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 + } +} diff --git a/OpenSim/Region/Environment/Scenes/SceneObjectGroup.cs b/OpenSim/Region/Environment/Scenes/SceneObjectGroup.cs index f5c5838..4f00843 100644 --- a/OpenSim/Region/Environment/Scenes/SceneObjectGroup.cs +++ b/OpenSim/Region/Environment/Scenes/SceneObjectGroup.cs @@ -1051,13 +1051,13 @@ namespace OpenSim.Region.Environment.Scenes scriptScore = 0; scriptScore += (float)count; - InnerScene d = m_scene.m_innerScene; + SceneGraph d = m_scene.m_sceneGraph; d.AddToScriptLPS(count); } public void AddActiveScriptCount(int count) { - InnerScene d = m_scene.m_innerScene; + SceneGraph d = m_scene.m_sceneGraph; d.AddActiveScripts(count); } @@ -1672,7 +1672,7 @@ namespace OpenSim.Region.Environment.Scenes public void QueueForUpdateCheck() { - m_scene.m_innerScene.AddToUpdateList(this); + m_scene.m_sceneGraph.AddToUpdateList(this); } /// diff --git a/OpenSim/Region/Environment/Scenes/ScenePresence.cs b/OpenSim/Region/Environment/Scenes/ScenePresence.cs index bfd210e..af17fac 100644 --- a/OpenSim/Region/Environment/Scenes/ScenePresence.cs +++ b/OpenSim/Region/Environment/Scenes/ScenePresence.cs @@ -2483,7 +2483,7 @@ namespace OpenSim.Region.Environment.Scenes foreach (SceneObjectGroup grp in m_attachments) { // ControllingClient may be null at this point! - m_scene.m_innerScene.DetachSingleAttachmentToInv(grp.GetFromAssetID(), ControllingClient); + m_scene.m_sceneGraph.DetachSingleAttachmentToInv(grp.GetFromAssetID(), ControllingClient); } } catch (InvalidOperationException) diff --git a/OpenSim/Region/Physics/Manager/PhysicsScene.cs b/OpenSim/Region/Physics/Manager/PhysicsScene.cs index 3575a51..f104632 100644 --- a/OpenSim/Region/Physics/Manager/PhysicsScene.cs +++ b/OpenSim/Region/Physics/Manager/PhysicsScene.cs @@ -40,7 +40,7 @@ namespace OpenSim.Region.Physics.Manager { private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); - // The only thing that should register for this event is the InnerScene + // The only thing that should register for this event is the SceneGraph // Anything else could cause problems. public event physicsCrash OnPhysicsCrash; -- cgit v1.1