AttachmentsModule.cs 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606
  1. /*
  2. * Copyright (c) Contributors, http://opensimulator.org/
  3. * See CONTRIBUTORS.TXT for a full list of copyright holders.
  4. *
  5. * Redistribution and use in source and binary forms, with or without
  6. * modification, are permitted provided that the following conditions are met:
  7. * * Redistributions of source code must retain the above copyright
  8. * notice, this list of conditions and the following disclaimer.
  9. * * Redistributions in binary form must reproduce the above copyright
  10. * notice, this list of conditions and the following disclaimer in the
  11. * documentation and/or other materials provided with the distribution.
  12. * * Neither the name of the OpenSimulator Project nor the
  13. * names of its contributors may be used to endorse or promote products
  14. * derived from this software without specific prior written permission.
  15. *
  16. * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
  17. * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  18. * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  19. * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
  20. * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  21. * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  22. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  23. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  24. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  25. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  26. */
  27. using System;
  28. using System.Collections.Generic;
  29. using System.Reflection;
  30. using log4net;
  31. using Mono.Addins;
  32. using Nini.Config;
  33. using OpenMetaverse;
  34. using OpenMetaverse.Packets;
  35. using OpenSim.Framework;
  36. using OpenSim.Region.Framework;
  37. using OpenSim.Region.Framework.Interfaces;
  38. using OpenSim.Region.Framework.Scenes;
  39. using OpenSim.Region.Framework.Scenes.Serialization;
  40. namespace OpenSim.Region.CoreModules.Avatar.Attachments
  41. {
  42. [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "AttachmentsModule")]
  43. public class AttachmentsModule : IAttachmentsModule, INonSharedRegionModule
  44. {
  45. private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
  46. protected Scene m_scene = null;
  47. public string Name { get { return "Attachments Module"; } }
  48. public Type ReplaceableInterface { get { return null; } }
  49. public void Initialise(IConfigSource source) {}
  50. public void AddRegion(Scene scene)
  51. {
  52. m_scene = scene;
  53. m_scene.RegisterModuleInterface<IAttachmentsModule>(this);
  54. m_scene.EventManager.OnNewClient += SubscribeToClientEvents;
  55. // TODO: Should probably be subscribing to CloseClient too, but this doesn't yet give us IClientAPI
  56. }
  57. public void RemoveRegion(Scene scene)
  58. {
  59. m_scene.UnregisterModuleInterface<IAttachmentsModule>(this);
  60. m_scene.EventManager.OnNewClient -= SubscribeToClientEvents;
  61. }
  62. public void RegionLoaded(Scene scene) {}
  63. public void Close()
  64. {
  65. RemoveRegion(m_scene);
  66. }
  67. public void SubscribeToClientEvents(IClientAPI client)
  68. {
  69. client.OnRezSingleAttachmentFromInv += RezSingleAttachmentFromInventory;
  70. client.OnRezMultipleAttachmentsFromInv += RezMultipleAttachmentsFromInventory;
  71. client.OnObjectAttach += AttachObject;
  72. client.OnObjectDetach += DetachObject;
  73. client.OnDetachAttachmentIntoInv += ShowDetachInUserInventory;
  74. }
  75. public void UnsubscribeFromClientEvents(IClientAPI client)
  76. {
  77. client.OnRezSingleAttachmentFromInv -= RezSingleAttachmentFromInventory;
  78. client.OnRezMultipleAttachmentsFromInv -= RezMultipleAttachmentsFromInventory;
  79. client.OnObjectAttach -= AttachObject;
  80. client.OnObjectDetach -= DetachObject;
  81. client.OnDetachAttachmentIntoInv -= ShowDetachInUserInventory;
  82. }
  83. /// <summary>
  84. /// Called by client
  85. /// </summary>
  86. /// <param name="remoteClient"></param>
  87. /// <param name="objectLocalID"></param>
  88. /// <param name="AttachmentPt"></param>
  89. /// <param name="silent"></param>
  90. public void AttachObject(IClientAPI remoteClient, uint objectLocalID, uint AttachmentPt, bool silent)
  91. {
  92. m_log.Debug("[ATTACHMENTS MODULE]: Invoking AttachObject");
  93. try
  94. {
  95. // If we can't take it, we can't attach it!
  96. SceneObjectPart part = m_scene.GetSceneObjectPart(objectLocalID);
  97. if (part == null)
  98. return;
  99. if (!m_scene.Permissions.CanTakeObject(part.UUID, remoteClient.AgentId))
  100. return;
  101. // Calls attach with a Zero position
  102. if (AttachObject(remoteClient, part.ParentGroup, AttachmentPt, false))
  103. {
  104. m_scene.EventManager.TriggerOnAttach(objectLocalID, part.ParentGroup.GetFromItemID(), remoteClient.AgentId);
  105. // Save avatar attachment information
  106. ScenePresence presence;
  107. if (m_scene.AvatarFactory != null && m_scene.TryGetScenePresence(remoteClient.AgentId, out presence))
  108. {
  109. m_log.Info(
  110. "[ATTACHMENTS MODULE]: Saving avatar attachment. AgentID: " + remoteClient.AgentId
  111. + ", AttachmentPoint: " + AttachmentPt);
  112. m_scene.AvatarFactory.UpdateDatabase(remoteClient.AgentId, presence.Appearance);
  113. }
  114. }
  115. }
  116. catch (Exception e)
  117. {
  118. m_log.DebugFormat("[ATTACHMENTS MODULE]: exception upon Attach Object {0}", e);
  119. }
  120. }
  121. public bool AttachObject(IClientAPI remoteClient, SceneObjectGroup group, uint AttachmentPt, bool silent)
  122. {
  123. Vector3 attachPos = group.AbsolutePosition;
  124. if (m_scene.Permissions.CanTakeObject(group.UUID, remoteClient.AgentId))
  125. {
  126. // If the attachment point isn't the same as the one previously used
  127. // set it's offset position = 0 so that it appears on the attachment point
  128. // and not in a weird location somewhere unknown.
  129. if (AttachmentPt != 0 && AttachmentPt != (uint)group.GetAttachmentPoint())
  130. {
  131. attachPos = Vector3.Zero;
  132. }
  133. // AttachmentPt 0 means the client chose to 'wear' the attachment.
  134. if (AttachmentPt == 0)
  135. {
  136. // Check object for stored attachment point
  137. AttachmentPt = (uint)group.GetAttachmentPoint();
  138. }
  139. // if we still didn't find a suitable attachment point.......
  140. if (AttachmentPt == 0)
  141. {
  142. // Stick it on left hand with Zero Offset from the attachment point.
  143. AttachmentPt = (uint)AttachmentPoint.LeftHand;
  144. attachPos = Vector3.Zero;
  145. }
  146. group.SetAttachmentPoint((byte)AttachmentPt);
  147. group.AbsolutePosition = attachPos;
  148. // Remove any previous attachments
  149. ScenePresence sp = m_scene.GetScenePresence(remoteClient.AgentId);
  150. UUID itemID = UUID.Zero;
  151. if (sp != null)
  152. {
  153. foreach(SceneObjectGroup grp in sp.GetAttachments(AttachmentPt))
  154. {
  155. itemID = grp.GetFromItemID();
  156. if (itemID != UUID.Zero)
  157. DetachSingleAttachmentToInv(itemID, remoteClient);
  158. }
  159. }
  160. if (group.GetFromItemID() == UUID.Zero)
  161. {
  162. m_scene.attachObjectAssetStore(remoteClient, group, remoteClient.AgentId, out itemID);
  163. }
  164. else
  165. {
  166. itemID = group.GetFromItemID();
  167. }
  168. SetAttachmentInventoryStatus(remoteClient, AttachmentPt, itemID, group);
  169. AttachToAgent(sp, group, AttachmentPt, attachPos, silent);
  170. }
  171. else
  172. {
  173. remoteClient.SendAgentAlertMessage(
  174. "You don't have sufficient permissions to attach this object", false);
  175. return false;
  176. }
  177. return true;
  178. }
  179. public void RezMultipleAttachmentsFromInventory(
  180. IClientAPI remoteClient,
  181. RezMultipleAttachmentsFromInvPacket.HeaderDataBlock header,
  182. RezMultipleAttachmentsFromInvPacket.ObjectDataBlock[] objects)
  183. {
  184. foreach (RezMultipleAttachmentsFromInvPacket.ObjectDataBlock obj in objects)
  185. {
  186. RezSingleAttachmentFromInventory(remoteClient, obj.ItemID, obj.AttachmentPt);
  187. }
  188. }
  189. public UUID RezSingleAttachmentFromInventory(IClientAPI remoteClient, UUID itemID, uint AttachmentPt)
  190. {
  191. m_log.DebugFormat("[ATTACHMENTS MODULE]: Rezzing single attachment from item {0} for {1}", itemID, remoteClient.Name);
  192. return RezSingleAttachmentFromInventory(remoteClient, itemID, AttachmentPt, true);
  193. }
  194. public UUID RezSingleAttachmentFromInventory(
  195. IClientAPI remoteClient, UUID itemID, uint AttachmentPt, bool updateInventoryStatus)
  196. {
  197. SceneObjectGroup att = RezSingleAttachmentFromInventoryInternal(remoteClient, itemID, AttachmentPt);
  198. if (updateInventoryStatus)
  199. {
  200. if (att == null)
  201. {
  202. ShowDetachInUserInventory(itemID, remoteClient);
  203. }
  204. SetAttachmentInventoryStatus(att, remoteClient, itemID, AttachmentPt);
  205. }
  206. if (null == att)
  207. return UUID.Zero;
  208. else
  209. return att.UUID;
  210. }
  211. protected SceneObjectGroup RezSingleAttachmentFromInventoryInternal(
  212. IClientAPI remoteClient, UUID itemID, uint AttachmentPt)
  213. {
  214. IInventoryAccessModule invAccess = m_scene.RequestModuleInterface<IInventoryAccessModule>();
  215. if (invAccess != null)
  216. {
  217. SceneObjectGroup objatt = invAccess.RezObject(remoteClient,
  218. itemID, Vector3.Zero, Vector3.Zero, UUID.Zero, (byte)1, true,
  219. false, false, remoteClient.AgentId, true);
  220. // m_log.DebugFormat(
  221. // "[ATTACHMENTS MODULE]: Retrieved single object {0} for attachment to {1} on point {2}",
  222. // objatt.Name, remoteClient.Name, AttachmentPt);
  223. if (objatt != null)
  224. {
  225. bool tainted = false;
  226. if (AttachmentPt != 0 && AttachmentPt != objatt.GetAttachmentPoint())
  227. tainted = true;
  228. // This will throw if the attachment fails
  229. try
  230. {
  231. AttachObject(remoteClient, objatt, AttachmentPt, false);
  232. }
  233. catch
  234. {
  235. // Make sure the object doesn't stick around and bail
  236. m_scene.DeleteSceneObject(objatt, false);
  237. return null;
  238. }
  239. if (tainted)
  240. objatt.HasGroupChanged = true;
  241. // Fire after attach, so we don't get messy perms dialogs
  242. // 4 == AttachedRez
  243. objatt.CreateScriptInstances(0, true, m_scene.DefaultScriptEngine, 4);
  244. objatt.ResumeScripts();
  245. // Do this last so that event listeners have access to all the effects of the attachment
  246. m_scene.EventManager.TriggerOnAttach(objatt.LocalId, itemID, remoteClient.AgentId);
  247. }
  248. else
  249. {
  250. m_log.WarnFormat(
  251. "[ATTACHMENTS MODULE]: Could not retrieve item {0} for attaching to avatar {1} at point {2}",
  252. itemID, remoteClient.Name, AttachmentPt);
  253. }
  254. return objatt;
  255. }
  256. return null;
  257. }
  258. public UUID SetAttachmentInventoryStatus(
  259. SceneObjectGroup att, IClientAPI remoteClient, UUID itemID, uint AttachmentPt)
  260. {
  261. m_log.DebugFormat(
  262. "[ATTACHMENTS MODULE]: Updating inventory of {0} to show attachment of {1} (item ID {2})",
  263. remoteClient.Name, att.Name, itemID);
  264. if (!att.IsDeleted)
  265. AttachmentPt = att.RootPart.AttachmentPoint;
  266. ScenePresence presence;
  267. if (m_scene.TryGetScenePresence(remoteClient.AgentId, out presence))
  268. {
  269. InventoryItemBase item = new InventoryItemBase(itemID, remoteClient.AgentId);
  270. item = m_scene.InventoryService.GetItem(item);
  271. presence.Appearance.SetAttachment((int)AttachmentPt, itemID, item.AssetID /*att.UUID*/);
  272. }
  273. return att.UUID;
  274. }
  275. /// <summary>
  276. /// Update the user inventory to reflect an attachment
  277. /// </summary>
  278. /// <param name="remoteClient"></param>
  279. /// <param name="AttachmentPt"></param>
  280. /// <param name="itemID"></param>
  281. /// <param name="att"></param>
  282. public void SetAttachmentInventoryStatus(
  283. IClientAPI remoteClient, uint AttachmentPt, UUID itemID, SceneObjectGroup att)
  284. {
  285. // m_log.DebugFormat(
  286. // "[USER INVENTORY]: Updating attachment {0} for {1} at {2} using item ID {3}",
  287. // att.Name, remoteClient.Name, AttachmentPt, itemID);
  288. if (UUID.Zero == itemID)
  289. {
  290. m_log.Error("[ATTACHMENTS MODULE]: Unable to save attachment. Error inventory item ID.");
  291. return;
  292. }
  293. if (0 == AttachmentPt)
  294. {
  295. m_log.Error("[ATTACHMENTS MODULE]: Unable to save attachment. Error attachment point.");
  296. return;
  297. }
  298. if (null == att.RootPart)
  299. {
  300. m_log.Error("[ATTACHMENTS MODULE]: Unable to save attachment for a prim without the rootpart!");
  301. return;
  302. }
  303. ScenePresence presence;
  304. if (m_scene.TryGetScenePresence(remoteClient.AgentId, out presence))
  305. {
  306. // XXYY!!
  307. InventoryItemBase item = new InventoryItemBase(itemID, remoteClient.AgentId);
  308. item = m_scene.InventoryService.GetItem(item);
  309. presence.Appearance.SetAttachment((int)AttachmentPt, itemID, item.AssetID /* att.UUID */);
  310. if (m_scene.AvatarFactory != null)
  311. m_scene.AvatarFactory.UpdateDatabase(remoteClient.AgentId, presence.Appearance);
  312. }
  313. }
  314. public void DetachObject(uint objectLocalID, IClientAPI remoteClient)
  315. {
  316. SceneObjectGroup group = m_scene.GetGroupByPrim(objectLocalID);
  317. if (group != null)
  318. {
  319. //group.DetachToGround();
  320. ShowDetachInUserInventory(group.GetFromItemID(), remoteClient);
  321. }
  322. }
  323. public void ShowDetachInUserInventory(UUID itemID, IClientAPI remoteClient)
  324. {
  325. ScenePresence presence;
  326. if (m_scene.TryGetScenePresence(remoteClient.AgentId, out presence))
  327. {
  328. presence.Appearance.DetachAttachment(itemID);
  329. // Save avatar attachment information
  330. if (m_scene.AvatarFactory != null)
  331. {
  332. m_log.Debug("[ATTACHMENTS MODULE]: Dettaching from UserID: " + remoteClient.AgentId + ", ItemID: " + itemID);
  333. m_scene.AvatarFactory.UpdateDatabase(remoteClient.AgentId, presence.Appearance);
  334. }
  335. }
  336. DetachSingleAttachmentToInv(itemID, remoteClient);
  337. }
  338. public void DetachSingleAttachmentToGround(UUID itemID, IClientAPI remoteClient)
  339. {
  340. SceneObjectPart part = m_scene.GetSceneObjectPart(itemID);
  341. if (part == null || part.ParentGroup == null)
  342. return;
  343. UUID inventoryID = part.ParentGroup.GetFromItemID();
  344. ScenePresence presence;
  345. if (m_scene.TryGetScenePresence(remoteClient.AgentId, out presence))
  346. {
  347. if (!m_scene.Permissions.CanRezObject(
  348. part.ParentGroup.PrimCount, remoteClient.AgentId, presence.AbsolutePosition))
  349. return;
  350. presence.Appearance.DetachAttachment(itemID);
  351. if (m_scene.AvatarFactory != null)
  352. {
  353. m_scene.AvatarFactory.UpdateDatabase(remoteClient.AgentId, presence.Appearance);
  354. }
  355. part.ParentGroup.DetachToGround();
  356. List<UUID> uuids = new List<UUID>();
  357. uuids.Add(inventoryID);
  358. m_scene.InventoryService.DeleteItems(remoteClient.AgentId, uuids);
  359. remoteClient.SendRemoveInventoryItem(inventoryID);
  360. }
  361. m_scene.EventManager.TriggerOnAttach(part.ParentGroup.LocalId, itemID, UUID.Zero);
  362. }
  363. // What makes this method odd and unique is it tries to detach using an UUID.... Yay for standards.
  364. // To LocalId or UUID, *THAT* is the question. How now Brown UUID??
  365. protected void DetachSingleAttachmentToInv(UUID itemID, IClientAPI remoteClient)
  366. {
  367. if (itemID == UUID.Zero) // If this happened, someone made a mistake....
  368. return;
  369. // We can NOT use the dictionries here, as we are looking
  370. // for an entity by the fromAssetID, which is NOT the prim UUID
  371. EntityBase[] detachEntities = m_scene.GetEntities();
  372. SceneObjectGroup group;
  373. foreach (EntityBase entity in detachEntities)
  374. {
  375. if (entity is SceneObjectGroup)
  376. {
  377. group = (SceneObjectGroup)entity;
  378. if (group.GetFromItemID() == itemID)
  379. {
  380. m_scene.EventManager.TriggerOnAttach(group.LocalId, itemID, UUID.Zero);
  381. group.DetachToInventoryPrep();
  382. m_log.Debug("[ATTACHMENTS MODULE]: Saving attachpoint: " + ((uint)group.GetAttachmentPoint()).ToString());
  383. UpdateKnownItem(remoteClient, group, group.GetFromItemID(), group.OwnerID);
  384. m_scene.DeleteSceneObject(group, false);
  385. return;
  386. }
  387. }
  388. }
  389. }
  390. public void UpdateAttachmentPosition(IClientAPI client, SceneObjectGroup sog, Vector3 pos)
  391. {
  392. // If this is an attachment, then we need to save the modified
  393. // object back into the avatar's inventory. First we save the
  394. // attachment point information, then we update the relative
  395. // positioning (which caused this method to get driven in the
  396. // first place. Then we have to mark the object as NOT an
  397. // attachment. This is necessary in order to correctly save
  398. // and retrieve GroupPosition information for the attachment.
  399. // Then we save the asset back into the appropriate inventory
  400. // entry. Finally, we restore the object's attachment status.
  401. byte attachmentPoint = sog.GetAttachmentPoint();
  402. sog.UpdateGroupPosition(pos);
  403. sog.RootPart.IsAttachment = false;
  404. sog.AbsolutePosition = sog.RootPart.AttachedPos;
  405. UpdateKnownItem(client, sog, sog.GetFromItemID(), sog.OwnerID);
  406. sog.SetAttachmentPoint(attachmentPoint);
  407. }
  408. /// <summary>
  409. /// Update the attachment asset for the new sog details if they have changed.
  410. /// </summary>
  411. ///
  412. /// This is essential for preserving attachment attributes such as permission. Unlike normal scene objects,
  413. /// these details are not stored on the region.
  414. ///
  415. /// <param name="remoteClient"></param>
  416. /// <param name="grp"></param>
  417. /// <param name="itemID"></param>
  418. /// <param name="agentID"></param>
  419. protected void UpdateKnownItem(IClientAPI remoteClient, SceneObjectGroup grp, UUID itemID, UUID agentID)
  420. {
  421. if (grp != null)
  422. {
  423. if (!grp.HasGroupChanged)
  424. {
  425. m_log.WarnFormat("[ATTACHMENTS MODULE]: Save request for {0} which is unchanged", grp.UUID);
  426. return;
  427. }
  428. m_log.DebugFormat(
  429. "[ATTACHMENTS MODULE]: Updating asset for attachment {0}, attachpoint {1}",
  430. grp.UUID, grp.GetAttachmentPoint());
  431. string sceneObjectXml = SceneObjectSerializer.ToOriginalXmlFormat(grp);
  432. InventoryItemBase item = new InventoryItemBase(itemID, remoteClient.AgentId);
  433. item = m_scene.InventoryService.GetItem(item);
  434. if (item != null)
  435. {
  436. AssetBase asset = m_scene.CreateAsset(
  437. grp.GetPartName(grp.LocalId),
  438. grp.GetPartDescription(grp.LocalId),
  439. (sbyte)AssetType.Object,
  440. Utils.StringToBytes(sceneObjectXml),
  441. remoteClient.AgentId);
  442. m_scene.AssetService.Store(asset);
  443. item.AssetID = asset.FullID;
  444. item.Description = asset.Description;
  445. item.Name = asset.Name;
  446. item.AssetType = asset.Type;
  447. item.InvType = (int)InventoryType.Object;
  448. m_scene.InventoryService.UpdateItem(item);
  449. // this gets called when the agent logs off!
  450. if (remoteClient != null)
  451. remoteClient.SendInventoryItemCreateUpdate(item, 0);
  452. }
  453. }
  454. }
  455. /// <summary>
  456. /// Attach this scene object to the given avatar.
  457. /// </summary>
  458. ///
  459. /// This isn't publicly available since attachments should always perform the corresponding inventory
  460. /// operation (to show the attach in user inventory and update the asset with positional information).
  461. ///
  462. /// <param name="sp"></param>
  463. /// <param name="so"></param>
  464. /// <param name="attachmentpoint"></param>
  465. /// <param name="AttachOffset"></param>
  466. /// <param name="silent"></param>
  467. protected void AttachToAgent(ScenePresence avatar, SceneObjectGroup so, uint attachmentpoint, Vector3 AttachOffset, bool silent)
  468. {
  469. // don't attach attachments to child agents
  470. if (avatar.IsChildAgent) return;
  471. // m_log.DebugFormat("[ATTACHMENTS MODULE]: Adding attachment {0} to avatar {1}", Name, avatar.Name);
  472. so.DetachFromBackup();
  473. // Remove from database and parcel prim count
  474. m_scene.DeleteFromStorage(so.UUID);
  475. m_scene.EventManager.TriggerParcelPrimCountTainted();
  476. so.RootPart.AttachedAvatar = avatar.UUID;
  477. //Anakin Lohner bug #3839
  478. lock (so.Children)
  479. {
  480. foreach (SceneObjectPart p in so.Children.Values)
  481. {
  482. p.AttachedAvatar = avatar.UUID;
  483. }
  484. }
  485. if (so.RootPart.PhysActor != null)
  486. {
  487. m_scene.PhysicsScene.RemovePrim(so.RootPart.PhysActor);
  488. so.RootPart.PhysActor = null;
  489. }
  490. so.AbsolutePosition = AttachOffset;
  491. so.RootPart.AttachedPos = AttachOffset;
  492. so.RootPart.IsAttachment = true;
  493. so.RootPart.SetParentLocalId(avatar.LocalId);
  494. so.SetAttachmentPoint(Convert.ToByte(attachmentpoint));
  495. avatar.AddAttachment(so);
  496. if (!silent)
  497. {
  498. // Killing it here will cause the client to deselect it
  499. // It then reappears on the avatar, deselected
  500. // through the full update below
  501. //
  502. if (so.IsSelected)
  503. {
  504. m_scene.SendKillObject(so.RootPart.LocalId);
  505. }
  506. so.IsSelected = false; // fudge....
  507. so.ScheduleGroupForFullUpdate();
  508. }
  509. // In case it is later dropped again, don't let
  510. // it get cleaned up
  511. so.RootPart.RemFlag(PrimFlags.TemporaryOnRez);
  512. so.HasGroupChanged = false;
  513. }
  514. }
  515. }