diff --git a/OpenSim/Region/CoreModules/Avatar/Attachments/AttachmentsModule.cs b/OpenSim/Region/CoreModules/Avatar/Attachments/AttachmentsModule.cs index f073c4abce..31e8a2e4cd 100644 --- a/OpenSim/Region/CoreModules/Avatar/Attachments/AttachmentsModule.cs +++ b/OpenSim/Region/CoreModules/Avatar/Attachments/AttachmentsModule.cs @@ -28,6 +28,7 @@ using System; using System.Collections.Generic; using System.Reflection; +using System.IO; using System.Xml; using log4net; using Mono.Addins; @@ -248,7 +249,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments } } - public void DeRezAttachments(IScenePresence sp, bool saveChanged, bool saveAllScripted) + public void DeRezAttachments(IScenePresence sp) { if (!Enabled) return; @@ -259,18 +260,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments { foreach (SceneObjectGroup so in sp.GetAttachments()) { - // We can only remove the script instances from the script engine after we've retrieved their xml state - // when we update the attachment item. - m_scene.DeleteSceneObject(so, false, false); - - if (saveChanged || saveAllScripted) - { - so.IsAttachment = false; - so.AbsolutePosition = so.RootPart.AttachedPos; - UpdateKnownItem(sp, so, saveAllScripted); - } - - so.RemoveScriptInstances(true); + UpdateDetachedObject(sp, so); } sp.ClearAttachments(); @@ -597,7 +587,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments /// /// /// - private void UpdateKnownItem(IScenePresence sp, SceneObjectGroup grp, bool saveAllScripted) + private void UpdateKnownItem(IScenePresence sp, SceneObjectGroup grp, string scriptedState) { // Saving attachments for NPCs messes them up for the real owner! INPCModule module = m_scene.RequestModuleInterface(); @@ -607,13 +597,13 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments return; } - if (grp.HasGroupChanged || (saveAllScripted && grp.ContainsScripts())) + if (grp.HasGroupChanged) { // m_log.DebugFormat( // "[ATTACHMENTS MODULE]: Updating asset for attachment {0}, attachpoint {1}", // grp.UUID, grp.AttachmentPoint); - string sceneObjectXml = SceneObjectSerializer.ToOriginalXmlFormat(grp); + string sceneObjectXml = SceneObjectSerializer.ToOriginalXmlFormat(grp, scriptedState); InventoryItemBase item = new InventoryItemBase(grp.FromItemID, sp.UUID); item = m_scene.InventoryService.GetItem(item); @@ -750,6 +740,60 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments return newItem; } + private string GetObjectScriptStates(SceneObjectGroup grp) + { + using (StringWriter sw = new StringWriter()) + { + using (XmlTextWriter writer = new XmlTextWriter(sw)) + { + grp.SaveScriptedState(writer); + } + + return sw.ToString(); + } + } + + private void UpdateDetachedObject(IScenePresence sp, SceneObjectGroup so) + { + // Don't save attachments for HG visitors, it + // messes up their inventory. When a HG visitor logs + // out on a foreign grid, their attachments will be + // reloaded in the state they were in when they left + // the home grid. This is best anyway as the visited + // grid may use an incompatible script engine. + bool saveChanged + = sp.PresenceType != PresenceType.Npc + && (m_scene.UserManagementModule == null + || m_scene.UserManagementModule.IsLocalGridUser(sp.UUID)); + + // Scripts MUST be snapshotted before the object is + // removed from the scene because doing otherwise will + // clobber the run flag + string scriptedState = GetObjectScriptStates(so); + + // Remove the object from the scene so no more updates + // are sent. Doing this before the below changes will ensure + // updates can't cause "HUD artefacts" + m_scene.DeleteSceneObject(so, false, false); + + // Prepare sog for storage + so.AttachedAvatar = UUID.Zero; + so.RootPart.SetParentLocalId(0); + so.IsAttachment = false; + + if (saveChanged) + { + // We cannot use AbsolutePosition here because that would + // attempt to cross the prim as it is detached + so.ForEachPart(x => { x.GroupPosition = so.RootPart.AttachedPos; }); + + UpdateKnownItem(sp, so, scriptedState); + } + + // Now, remove the scripts + so.RemoveScriptInstances(true); + } + private void DetachSingleAttachmentToInvInternal(IScenePresence sp, SceneObjectGroup so) { // m_log.DebugFormat("[ATTACHMENTS MODULE]: Detaching item {0} to inventory for {1}", itemID, sp.Name); @@ -757,22 +801,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments m_scene.EventManager.TriggerOnAttach(so.LocalId, so.FromItemID, UUID.Zero); sp.RemoveAttachment(so); - // Prepare sog for storage - so.AttachedAvatar = UUID.Zero; - so.RootPart.SetParentLocalId(0); - so.IsAttachment = false; - - // We cannot use AbsolutePosition here because that would - // attempt to cross the prim as it is detached - so.ForEachPart(x => { x.GroupPosition = so.RootPart.AttachedPos; }); - - UpdateKnownItem(sp, so, true); - - // This MUST happen AFTER serialization because it will - // either stop or remove the scripts. Both will cause scripts - // to be serialized in a stopped state with the true run - // state already lost. - m_scene.DeleteSceneObject(so, false, true); + UpdateDetachedObject(sp, so); } protected SceneObjectGroup RezSingleAttachmentFromInventoryInternal( diff --git a/OpenSim/Region/Framework/Interfaces/IAttachmentsModule.cs b/OpenSim/Region/Framework/Interfaces/IAttachmentsModule.cs index 410eda05fa..11a13e1097 100644 --- a/OpenSim/Region/Framework/Interfaces/IAttachmentsModule.cs +++ b/OpenSim/Region/Framework/Interfaces/IAttachmentsModule.cs @@ -66,7 +66,7 @@ namespace OpenSim.Region.Framework.Interfaces /// The presence closing /// Save changed attachments. /// Save attachments with scripts even if they haven't changed. - void DeRezAttachments(IScenePresence sp, bool saveChanged, bool saveAllScripted); + void DeRezAttachments(IScenePresence sp); /// /// Delete all the presence's attachments from the scene diff --git a/OpenSim/Region/Framework/Interfaces/IScenePresence.cs b/OpenSim/Region/Framework/Interfaces/IScenePresence.cs index e6b926ce8c..3f68ee02ec 100644 --- a/OpenSim/Region/Framework/Interfaces/IScenePresence.cs +++ b/OpenSim/Region/Framework/Interfaces/IScenePresence.cs @@ -40,6 +40,8 @@ namespace OpenSim.Region.Framework.Interfaces /// public interface IScenePresence : ISceneAgent { + PresenceType PresenceType { get; } + /// /// Copy of the script states while the agent is in transit. This state may /// need to be placed back in case of transfer fail. @@ -83,4 +85,4 @@ namespace OpenSim.Region.Framework.Interfaces void RemoveAttachment(SceneObjectGroup gobj); void ClearAttachments(); } -} \ No newline at end of file +} diff --git a/OpenSim/Region/Framework/Scenes/Scene.cs b/OpenSim/Region/Framework/Scenes/Scene.cs index f5018283c7..ad9e91d7b9 100644 --- a/OpenSim/Region/Framework/Scenes/Scene.cs +++ b/OpenSim/Region/Framework/Scenes/Scene.cs @@ -3461,17 +3461,7 @@ namespace OpenSim.Region.Framework.Scenes { if (AttachmentsModule != null) { - // Don't save attachments for HG visitors, it - // messes up their inventory. When a HG visitor logs - // out on a foreign grid, their attachments will be - // reloaded in the state they were in when they left - // the home grid. This is best anyway as the visited - // grid may use an incompatible script engine. - bool saveChanged - = avatar.PresenceType != PresenceType.Npc - && (UserManagementModule == null || UserManagementModule.IsLocalGridUser(avatar.UUID)); - - AttachmentsModule.DeRezAttachments(avatar, saveChanged, false); + AttachmentsModule.DeRezAttachments(avatar); } ForEachClient( diff --git a/OpenSim/Region/Framework/Scenes/Serialization/SceneObjectSerializer.cs b/OpenSim/Region/Framework/Scenes/Serialization/SceneObjectSerializer.cs index 2372d6ba93..0d292e7d95 100644 --- a/OpenSim/Region/Framework/Scenes/Serialization/SceneObjectSerializer.cs +++ b/OpenSim/Region/Framework/Scenes/Serialization/SceneObjectSerializer.cs @@ -151,6 +151,24 @@ namespace OpenSim.Region.Framework.Scenes.Serialization ToOriginalXmlFormat(sceneObject, writer, doScriptStates, false); } + public static string ToOriginalXmlFormat(SceneObjectGroup sceneObject, string scriptedState) + { + using (StringWriter sw = new StringWriter()) + { + using (XmlTextWriter writer = new XmlTextWriter(sw)) + { + writer.WriteStartElement(String.Empty, "SceneObjectGroup", String.Empty); + + ToOriginalXmlFormat(sceneObject, writer, false, true); + + writer.WriteRaw(scriptedState); + + writer.WriteEndElement(); + } + return sw.ToString(); + } + } + /// /// Serialize a scene object to the original xml format ///