diff --git a/OpenSim/Region/Environment/Scenes/AsyncSceneObjectGroupDeleter.cs b/OpenSim/Region/Environment/Scenes/AsyncSceneObjectGroupDeleter.cs new file mode 100644 index 0000000000..8c43bcb9b4 --- /dev/null +++ b/OpenSim/Region/Environment/Scenes/AsyncSceneObjectGroupDeleter.cs @@ -0,0 +1,146 @@ +/* + * 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 System.Timers; +using log4net; +using OpenMetaverse; +using OpenMetaverse.Packets; +using OpenSim.Framework; + +namespace OpenSim.Region.Environment.Scenes +{ + class DeleteToInventoryHolder + { + public DeRezObjectPacket DeRezPacket; + public EntityBase selectedEnt; + public IClientAPI remoteClient; + public SceneObjectGroup objectGroup; + public UUID folderID; + public bool permissionToDelete; + } + + /// + /// Asynchronously derez objects. This is used to derez large number of objects to inventory without holding + /// up the main client thread. + /// + public class AsyncSceneObjectGroupDeleter + { + private static readonly ILog m_log + = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private Timer m_inventoryTicker; + private readonly Queue m_inventoryDeletes = new Queue(); + private Scene m_scene; + + public AsyncSceneObjectGroupDeleter(Scene scene) + { + m_scene = scene; + } + + /// + /// Delete the given object from the scene + /// + public void DeleteToInventory( + DeRezObjectPacket DeRezPacket, UUID folderID, SceneObjectGroup objectGroup, IClientAPI remoteClient, + EntityBase selectedEnt, bool permissionToDelete) + { + if (m_inventoryTicker != null) + { + m_inventoryTicker.Stop(); + } + else + { + m_inventoryTicker = new Timer(2000); + m_inventoryTicker.AutoReset = false; + m_inventoryTicker.Elapsed += InventoryRunDeleteTimer; + } + + lock (m_inventoryDeletes) + { + DeleteToInventoryHolder dtis = new DeleteToInventoryHolder(); + dtis.DeRezPacket = DeRezPacket; + dtis.folderID = folderID; + dtis.objectGroup = objectGroup; + dtis.remoteClient = remoteClient; + dtis.selectedEnt = selectedEnt; + dtis.permissionToDelete = permissionToDelete; + + m_inventoryDeletes.Enqueue(dtis); + } + + m_inventoryTicker.Start(); + + // Visually remove it, even if it isnt really gone yet. + if (permissionToDelete) + objectGroup.FakeDeleteGroup(); + } + + private void InventoryRunDeleteTimer(object sender, ElapsedEventArgs e) + { + m_log.Info("Starting inventory send loop"); + while (InventoryDeQueueAndDelete() == true) + { + m_log.Info("Returned item successfully, continuing..."); + } + } + + private bool InventoryDeQueueAndDelete() + { + DeleteToInventoryHolder x = null; + + try + { + lock (m_inventoryDeletes) + { + int left = m_inventoryDeletes.Count; + if (left > 0) + { + m_log.InfoFormat("Sending deleted object to user's inventory, {0} item(s) remaining.", left); + x = m_inventoryDeletes.Dequeue(); + m_scene.DeleteToInventory( + x.DeRezPacket, x.selectedEnt, x.remoteClient, x.objectGroup, x.folderID, x.permissionToDelete); + return true; + } + } + } + catch(Exception e) + { + // We can't put the object group details in here since the root part may have disappeared (which is where these sit). + // FIXME: This needs to be fixed. + m_log.ErrorFormat( + "[AGENT INVENTORY]: Queued deletion of scene object to agent {0} {1} failed: {2}", + (x != null ? x.remoteClient.Name : "unavailable"), (x != null ? x.remoteClient.AgentId.ToString() : "unavailable"), e.ToString()); + } + + m_log.Info("No objects left in inventory delete queue."); + return false; + } + } +} diff --git a/OpenSim/Region/Environment/Scenes/Scene.Inventory.cs b/OpenSim/Region/Environment/Scenes/Scene.Inventory.cs index 5ff6dec038..8557b1c5b2 100644 --- a/OpenSim/Region/Environment/Scenes/Scene.Inventory.cs +++ b/OpenSim/Region/Environment/Scenes/Scene.Inventory.cs @@ -40,23 +40,15 @@ using OpenSim.Region.Environment.Interfaces; namespace OpenSim.Region.Environment.Scenes { - class DeleteToInventoryHolder - { - public DeRezObjectPacket DeRezPacket; - public EntityBase selectedEnt; - public IClientAPI remoteClient; - public SceneObjectGroup objectGroup; - public UUID folderID; - public bool permissionToDelete; - } - public partial class Scene { - private Timer m_inventoryTicker; - private readonly Queue m_inventoryDeletes = new Queue(); - private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + /// + /// Allows asynchronous derezzing of objects from the scene into a client's inventory. + /// + private AsyncSceneObjectGroupDeleter m_asyncSceneObjectDeleter; /// /// Start all the scripts in the scene which should be started. @@ -1546,35 +1538,8 @@ namespace OpenSim.Region.Environment.Scenes if (permissionToTake) { - if (m_inventoryTicker != null) - { - m_inventoryTicker.Stop(); - } - else - { - m_inventoryTicker = new Timer(2000); - m_inventoryTicker.AutoReset = false; - m_inventoryTicker.Elapsed += InventoryRunDeleteTimer; - } - - lock (m_inventoryDeletes) - { - DeleteToInventoryHolder dtis = new DeleteToInventoryHolder(); - dtis.DeRezPacket = DeRezPacket; - dtis.folderID = folderID; - dtis.objectGroup = objectGroup; - dtis.remoteClient = remoteClient; - dtis.selectedEnt = selectedEnt; - dtis.permissionToDelete = permissionToDelete; - - m_inventoryDeletes.Enqueue(dtis); - } - - m_inventoryTicker.Start(); - - // Visually remove it, even if it isnt really gone yet. - if (permissionToDelete) - objectGroup.FakeDeleteGroup(); + m_asyncSceneObjectDeleter.DeleteToInventory( + DeRezPacket, folderID, objectGroup, remoteClient, selectedEnt, permissionToDelete); } else if (permissionToDelete) { @@ -1584,47 +1549,17 @@ namespace OpenSim.Region.Environment.Scenes } } - void InventoryRunDeleteTimer(object sender, ElapsedEventArgs e) - { - m_log.Info("Starting inventory send loop"); - while (InventoryDeQueueAndDelete() == true) - { - m_log.Info("Returned item successfully, continuing..."); - } - } - - private bool InventoryDeQueueAndDelete() - { - DeleteToInventoryHolder x = null; - - try - { - lock (m_inventoryDeletes) - { - int left = m_inventoryDeletes.Count; - if (left > 0) - { - m_log.InfoFormat("Sending deleted object to user's inventory, {0} item(s) remaining.", left); - x = m_inventoryDeletes.Dequeue(); - DeleteToInventory(x.DeRezPacket, x.selectedEnt, x.remoteClient, x.objectGroup, x.folderID, x.permissionToDelete); - return true; - } - } - } - catch(Exception e) - { - // We can't put the object group details in here since the root part may have disappeared (which is where these sit). - // FIXME: This needs to be fixed. - m_log.ErrorFormat( - "[AGENT INVENTORY]: Queued deletion of scene object to agent {0} {1} failed: {2}", - (x != null ? x.remoteClient.Name : "unavailable"), (x != null ? x.remoteClient.AgentId.ToString() : "unavailable"), e.ToString()); - } - - m_log.Info("No objects left in inventory delete queue."); - return false; - } - - private void DeleteToInventory(DeRezObjectPacket DeRezPacket, EntityBase selectedEnt, IClientAPI remoteClient, SceneObjectGroup objectGroup, UUID folderID, bool permissionToDelete) + /// + /// Delete a scene object from a scene and place in the given avatar's inventory. + /// + /// + /// + /// + /// + /// + /// + public void DeleteToInventory(DeRezObjectPacket DeRezPacket, EntityBase selectedEnt, IClientAPI remoteClient, + SceneObjectGroup objectGroup, UUID folderID, bool permissionToDelete) { string sceneObjectXml = objectGroup.ToXmlString(); diff --git a/OpenSim/Region/Environment/Scenes/Scene.cs b/OpenSim/Region/Environment/Scenes/Scene.cs index 42e9c02a2b..88da9be279 100644 --- a/OpenSim/Region/Environment/Scenes/Scene.cs +++ b/OpenSim/Region/Environment/Scenes/Scene.cs @@ -275,6 +275,7 @@ namespace OpenSim.Region.Environment.Scenes m_eventManager = new EventManager(); m_externalChecks = new SceneExternalChecks(this); + m_asyncSceneObjectDeleter = new AsyncSceneObjectGroupDeleter(this); // Load region settings m_regInfo.RegionSettings = m_storageManager.DataStore.LoadRegionSettings(m_regInfo.RegionID);