AttachmentsModule.cs 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787
  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. private Scene m_scene = null;
  47. private IDialogModule m_dialogModule;
  48. public string Name { get { return "Attachments Module"; } }
  49. public Type ReplaceableInterface { get { return null; } }
  50. public void Initialise(IConfigSource source) {}
  51. public void AddRegion(Scene scene)
  52. {
  53. m_scene = scene;
  54. m_dialogModule = m_scene.RequestModuleInterface<IDialogModule>();
  55. m_scene.RegisterModuleInterface<IAttachmentsModule>(this);
  56. m_scene.EventManager.OnNewClient += SubscribeToClientEvents;
  57. // TODO: Should probably be subscribing to CloseClient too, but this doesn't yet give us IClientAPI
  58. }
  59. public void RemoveRegion(Scene scene)
  60. {
  61. m_scene.UnregisterModuleInterface<IAttachmentsModule>(this);
  62. m_scene.EventManager.OnNewClient -= SubscribeToClientEvents;
  63. }
  64. public void RegionLoaded(Scene scene) {}
  65. public void Close()
  66. {
  67. RemoveRegion(m_scene);
  68. }
  69. public void SubscribeToClientEvents(IClientAPI client)
  70. {
  71. client.OnRezSingleAttachmentFromInv += RezSingleAttachmentFromInventory;
  72. client.OnRezMultipleAttachmentsFromInv += RezMultipleAttachmentsFromInventory;
  73. client.OnObjectAttach += AttachObject;
  74. client.OnObjectDetach += DetachObject;
  75. client.OnDetachAttachmentIntoInv += DetachSingleAttachmentToInv;
  76. }
  77. public void UnsubscribeFromClientEvents(IClientAPI client)
  78. {
  79. client.OnRezSingleAttachmentFromInv -= RezSingleAttachmentFromInventory;
  80. client.OnRezMultipleAttachmentsFromInv -= RezMultipleAttachmentsFromInventory;
  81. client.OnObjectAttach -= AttachObject;
  82. client.OnObjectDetach -= DetachObject;
  83. client.OnDetachAttachmentIntoInv -= DetachSingleAttachmentToInv;
  84. }
  85. /// <summary>
  86. /// Called by client
  87. /// </summary>
  88. /// <param name="remoteClient"></param>
  89. /// <param name="objectLocalID"></param>
  90. /// <param name="AttachmentPt"></param>
  91. /// <param name="silent"></param>
  92. public void AttachObject(IClientAPI remoteClient, uint objectLocalID, uint AttachmentPt, bool silent)
  93. {
  94. // m_log.DebugFormat(
  95. // "[ATTACHMENTS MODULE]: Attaching object local id {0} to {1} point {2} from ground (silent = {3})",
  96. // objectLocalID, remoteClient.Name, AttachmentPt, silent);
  97. try
  98. {
  99. ScenePresence sp = m_scene.GetScenePresence(remoteClient.AgentId);
  100. if (sp == null)
  101. {
  102. m_log.ErrorFormat(
  103. "[ATTACHMENTS MODULE]: Could not find presence for client {0} {1}", remoteClient.Name, remoteClient.AgentId);
  104. return;
  105. }
  106. // If we can't take it, we can't attach it!
  107. SceneObjectPart part = m_scene.GetSceneObjectPart(objectLocalID);
  108. if (part == null)
  109. return;
  110. if (!m_scene.Permissions.CanTakeObject(part.UUID, remoteClient.AgentId))
  111. {
  112. remoteClient.SendAgentAlertMessage(
  113. "You don't have sufficient permissions to attach this object", false);
  114. return;
  115. }
  116. // TODO: this short circuits multiple attachments functionality in LL viewer 2.1+ and should
  117. // be removed when that functionality is implemented in opensim
  118. AttachmentPt &= 0x7f;
  119. // Calls attach with a Zero position
  120. if (AttachObject(sp, part.ParentGroup, AttachmentPt, false))
  121. {
  122. m_scene.EventManager.TriggerOnAttach(objectLocalID, part.ParentGroup.GetFromItemID(), remoteClient.AgentId);
  123. // Save avatar attachment information
  124. m_log.Info(
  125. "[ATTACHMENTS MODULE]: Saving avatar attachment. AgentID: " + remoteClient.AgentId
  126. + ", AttachmentPoint: " + AttachmentPt);
  127. }
  128. }
  129. catch (Exception e)
  130. {
  131. m_log.ErrorFormat("[ATTACHMENTS MODULE]: exception upon Attach Object {0}{1}", e.Message, e.StackTrace);
  132. }
  133. }
  134. public bool AttachObject(IClientAPI remoteClient, SceneObjectGroup group, uint AttachmentPt, bool silent)
  135. {
  136. ScenePresence sp = m_scene.GetScenePresence(remoteClient.AgentId);
  137. if (sp == null)
  138. {
  139. m_log.ErrorFormat(
  140. "[ATTACHMENTS MODULE]: Could not find presence for client {0} {1}", remoteClient.Name, remoteClient.AgentId);
  141. return false;
  142. }
  143. return AttachObject(sp, group, AttachmentPt, silent);
  144. }
  145. private bool AttachObject(ScenePresence sp, SceneObjectGroup group, uint attachmentPt, bool silent)
  146. {
  147. // m_log.DebugFormat(
  148. // "[ATTACHMENTS MODULE]: Attaching object {0} {1} to {2} point {3} from ground (silent = {4})",
  149. // group.Name, group.LocalId, sp.Name, AttachmentPt, silent);
  150. if (sp.GetAttachments(attachmentPt).Contains(group))
  151. {
  152. // m_log.WarnFormat(
  153. // "[ATTACHMENTS MODULE]: Ignoring request to attach {0} {1} to {2} on {3} since it's already attached",
  154. // group.Name, group.LocalId, sp.Name, AttachmentPt);
  155. return false;
  156. }
  157. Vector3 attachPos = group.AbsolutePosition;
  158. // TODO: this short circuits multiple attachments functionality in LL viewer 2.1+ and should
  159. // be removed when that functionality is implemented in opensim
  160. attachmentPt &= 0x7f;
  161. // If the attachment point isn't the same as the one previously used
  162. // set it's offset position = 0 so that it appears on the attachment point
  163. // and not in a weird location somewhere unknown.
  164. if (attachmentPt != 0 && attachmentPt != group.AttachmentPoint)
  165. {
  166. attachPos = Vector3.Zero;
  167. }
  168. // AttachmentPt 0 means the client chose to 'wear' the attachment.
  169. if (attachmentPt == 0)
  170. {
  171. // Check object for stored attachment point
  172. attachmentPt = group.AttachmentPoint;
  173. }
  174. // if we still didn't find a suitable attachment point.......
  175. if (attachmentPt == 0)
  176. {
  177. // Stick it on left hand with Zero Offset from the attachment point.
  178. attachmentPt = (uint)AttachmentPoint.LeftHand;
  179. attachPos = Vector3.Zero;
  180. }
  181. group.AttachmentPoint = attachmentPt;
  182. group.AbsolutePosition = attachPos;
  183. // Remove any previous attachments
  184. UUID itemID = UUID.Zero;
  185. foreach (SceneObjectGroup grp in sp.Attachments)
  186. {
  187. if (grp.AttachmentPoint == attachmentPt)
  188. {
  189. itemID = grp.GetFromItemID();
  190. break;
  191. }
  192. }
  193. if (itemID != UUID.Zero)
  194. DetachSingleAttachmentToInv(itemID, sp);
  195. itemID = group.GetFromItemID();
  196. if (itemID == UUID.Zero)
  197. itemID = AddSceneObjectAsAttachment(sp.ControllingClient, group).ID;
  198. ShowAttachInUserInventory(sp, attachmentPt, itemID, group);
  199. AttachToAgent(sp, group, attachmentPt, attachPos, silent);
  200. return true;
  201. }
  202. public void RezMultipleAttachmentsFromInventory(
  203. IClientAPI remoteClient,
  204. RezMultipleAttachmentsFromInvPacket.HeaderDataBlock header,
  205. RezMultipleAttachmentsFromInvPacket.ObjectDataBlock[] objects)
  206. {
  207. foreach (RezMultipleAttachmentsFromInvPacket.ObjectDataBlock obj in objects)
  208. {
  209. RezSingleAttachmentFromInventory(remoteClient, obj.ItemID, obj.AttachmentPt);
  210. }
  211. }
  212. public UUID RezSingleAttachmentFromInventory(IClientAPI remoteClient, UUID itemID, uint AttachmentPt)
  213. {
  214. return RezSingleAttachmentFromInventory(remoteClient, itemID, AttachmentPt, true);
  215. }
  216. public UUID RezSingleAttachmentFromInventory(
  217. IClientAPI remoteClient, UUID itemID, uint AttachmentPt, bool updateInventoryStatus)
  218. {
  219. // m_log.DebugFormat(
  220. // "[ATTACHMENTS MODULE]: Rezzing attachment to point {0} from item {1} for {2}",
  221. // (AttachmentPoint)AttachmentPt, itemID, remoteClient.Name);
  222. ScenePresence sp = m_scene.GetScenePresence(remoteClient.AgentId);
  223. if (sp == null)
  224. {
  225. m_log.ErrorFormat(
  226. "[ATTACHMENTS MODULE]: Could not find presence for client {0} {1} in RezSingleAttachmentFromInventory()",
  227. remoteClient.Name, remoteClient.AgentId);
  228. return UUID.Zero;
  229. }
  230. // TODO: this short circuits multiple attachments functionality in LL viewer 2.1+ and should
  231. // be removed when that functionality is implemented in opensim
  232. AttachmentPt &= 0x7f;
  233. SceneObjectGroup att = RezSingleAttachmentFromInventoryInternal(sp, itemID, AttachmentPt);
  234. if (updateInventoryStatus)
  235. {
  236. if (att == null)
  237. DetachSingleAttachmentToInv(itemID, sp.ControllingClient);
  238. else
  239. ShowAttachInUserInventory(att, sp, itemID, AttachmentPt);
  240. }
  241. if (null == att)
  242. return UUID.Zero;
  243. else
  244. return att.UUID;
  245. }
  246. private SceneObjectGroup RezSingleAttachmentFromInventoryInternal(
  247. ScenePresence sp, UUID itemID, uint attachmentPt)
  248. {
  249. IInventoryAccessModule invAccess = m_scene.RequestModuleInterface<IInventoryAccessModule>();
  250. if (invAccess != null)
  251. {
  252. SceneObjectGroup objatt = invAccess.RezObject(sp.ControllingClient,
  253. itemID, Vector3.Zero, Vector3.Zero, UUID.Zero, (byte)1, true,
  254. false, false, sp.UUID, true);
  255. // m_log.DebugFormat(
  256. // "[ATTACHMENTS MODULE]: Retrieved single object {0} for attachment to {1} on point {2}",
  257. // objatt.Name, remoteClient.Name, AttachmentPt);
  258. if (objatt != null)
  259. {
  260. // Loading the inventory from XML will have set this, but
  261. // there is no way the object could have changed yet,
  262. // since scripts aren't running yet. So, clear it here.
  263. objatt.HasGroupChanged = false;
  264. bool tainted = false;
  265. if (attachmentPt != 0 && attachmentPt != objatt.AttachmentPoint)
  266. tainted = true;
  267. // This will throw if the attachment fails
  268. try
  269. {
  270. AttachObject(sp, objatt, attachmentPt, false);
  271. }
  272. catch (Exception e)
  273. {
  274. m_log.ErrorFormat(
  275. "[ATTACHMENTS MODULE]: Failed to attach {0} {1} for {2}, exception {3}{4}",
  276. objatt.Name, objatt.UUID, sp.Name, e.Message, e.StackTrace);
  277. // Make sure the object doesn't stick around and bail
  278. sp.RemoveAttachment(objatt);
  279. m_scene.DeleteSceneObject(objatt, false);
  280. return null;
  281. }
  282. if (tainted)
  283. objatt.HasGroupChanged = true;
  284. // Fire after attach, so we don't get messy perms dialogs
  285. // 4 == AttachedRez
  286. objatt.CreateScriptInstances(0, true, m_scene.DefaultScriptEngine, 4);
  287. objatt.ResumeScripts();
  288. // Do this last so that event listeners have access to all the effects of the attachment
  289. m_scene.EventManager.TriggerOnAttach(objatt.LocalId, itemID, sp.UUID);
  290. }
  291. else
  292. {
  293. m_log.WarnFormat(
  294. "[ATTACHMENTS MODULE]: Could not retrieve item {0} for attaching to avatar {1} at point {2}",
  295. itemID, sp.Name, attachmentPt);
  296. }
  297. return objatt;
  298. }
  299. return null;
  300. }
  301. /// <summary>
  302. /// Update the user inventory to the attachment of an item
  303. /// </summary>
  304. /// <param name="att"></param>
  305. /// <param name="sp"></param>
  306. /// <param name="itemID"></param>
  307. /// <param name="attachmentPoint"></param>
  308. /// <returns></returns>
  309. private UUID ShowAttachInUserInventory(
  310. SceneObjectGroup att, ScenePresence sp, UUID itemID, uint attachmentPoint)
  311. {
  312. // m_log.DebugFormat(
  313. // "[ATTACHMENTS MODULE]: Updating inventory of {0} to show attachment of {1} {2} (item ID {3}) at {4}",
  314. // sp.Name, att.Name, att.LocalId, itemID, AttachmentPt);
  315. if (!att.IsDeleted)
  316. attachmentPoint = att.AttachmentPoint;
  317. InventoryItemBase item = new InventoryItemBase(itemID, sp.UUID);
  318. item = m_scene.InventoryService.GetItem(item);
  319. bool changed = sp.Appearance.SetAttachment((int)attachmentPoint, itemID, item.AssetID);
  320. if (changed && m_scene.AvatarFactory != null)
  321. m_scene.AvatarFactory.QueueAppearanceSave(sp.UUID);
  322. return att.UUID;
  323. }
  324. /// <summary>
  325. /// Update the user inventory to reflect an attachment
  326. /// </summary>
  327. /// <param name="sp"></param>
  328. /// <param name="AttachmentPt"></param>
  329. /// <param name="itemID"></param>
  330. /// <param name="att"></param>
  331. private void ShowAttachInUserInventory(
  332. ScenePresence sp, uint AttachmentPt, UUID itemID, SceneObjectGroup att)
  333. {
  334. // m_log.DebugFormat(
  335. // "[USER INVENTORY]: Updating attachment {0} for {1} at {2} using item ID {3}",
  336. // att.Name, remoteClient.Name, AttachmentPt, itemID);
  337. if (UUID.Zero == itemID)
  338. {
  339. m_log.Error("[ATTACHMENTS MODULE]: Unable to save attachment. Error inventory item ID.");
  340. return;
  341. }
  342. if (0 == AttachmentPt)
  343. {
  344. m_log.Error("[ATTACHMENTS MODULE]: Unable to save attachment. Error attachment point.");
  345. return;
  346. }
  347. if (null == att.RootPart)
  348. {
  349. m_log.Error("[ATTACHMENTS MODULE]: Unable to save attachment for a prim without the rootpart!");
  350. return;
  351. }
  352. InventoryItemBase item = new InventoryItemBase(itemID, sp.UUID);
  353. item = m_scene.InventoryService.GetItem(item);
  354. bool changed = sp.Appearance.SetAttachment((int)AttachmentPt, itemID, item.AssetID);
  355. if (changed && m_scene.AvatarFactory != null)
  356. m_scene.AvatarFactory.QueueAppearanceSave(sp.UUID);
  357. }
  358. public void DetachObject(uint objectLocalID, IClientAPI remoteClient)
  359. {
  360. SceneObjectGroup group = m_scene.GetGroupByPrim(objectLocalID);
  361. if (group != null)
  362. {
  363. DetachSingleAttachmentToInv(group.GetFromItemID(), remoteClient);
  364. }
  365. }
  366. public void DetachSingleAttachmentToInv(UUID itemID, IClientAPI remoteClient)
  367. {
  368. ScenePresence presence;
  369. if (m_scene.TryGetScenePresence(remoteClient.AgentId, out presence))
  370. {
  371. // Save avatar attachment information
  372. m_log.Debug("[ATTACHMENTS MODULE]: Detaching from UserID: " + remoteClient.AgentId + ", ItemID: " + itemID);
  373. bool changed = presence.Appearance.DetachAttachment(itemID);
  374. if (changed && m_scene.AvatarFactory != null)
  375. m_scene.AvatarFactory.QueueAppearanceSave(remoteClient.AgentId);
  376. DetachSingleAttachmentToInv(itemID, presence);
  377. }
  378. }
  379. public void DetachSingleAttachmentToGround(UUID sceneObjectID, IClientAPI remoteClient)
  380. {
  381. SceneObjectGroup so = m_scene.GetSceneObjectGroup(sceneObjectID);
  382. if (so == null)
  383. return;
  384. if (so.AttachedAvatar != remoteClient.AgentId)
  385. return;
  386. UUID inventoryID = so.GetFromItemID();
  387. ScenePresence presence;
  388. if (m_scene.TryGetScenePresence(remoteClient.AgentId, out presence))
  389. {
  390. if (!m_scene.Permissions.CanRezObject(
  391. so.PrimCount, remoteClient.AgentId, presence.AbsolutePosition))
  392. return;
  393. bool changed = presence.Appearance.DetachAttachment(sceneObjectID);
  394. if (changed && m_scene.AvatarFactory != null)
  395. m_scene.AvatarFactory.QueueAppearanceSave(remoteClient.AgentId);
  396. presence.RemoveAttachment(so);
  397. DetachSceneObjectToGround(so, presence);
  398. List<UUID> uuids = new List<UUID>();
  399. uuids.Add(inventoryID);
  400. m_scene.InventoryService.DeleteItems(remoteClient.AgentId, uuids);
  401. remoteClient.SendRemoveInventoryItem(inventoryID);
  402. }
  403. m_scene.EventManager.TriggerOnAttach(so.LocalId, sceneObjectID, UUID.Zero);
  404. }
  405. /// <summary>
  406. /// Detach the given scene objet to the ground.
  407. /// </summary>
  408. /// <remarks>
  409. /// The caller has to take care of all the other work in updating avatar appearance, inventory, etc.
  410. /// </remarks>
  411. /// <param name="so">The scene object to detach.</param>
  412. /// <param name="sp">The scene presence from which the scene object is being detached.</param>
  413. private void DetachSceneObjectToGround(SceneObjectGroup so, ScenePresence sp)
  414. {
  415. SceneObjectPart rootPart = so.RootPart;
  416. rootPart.FromItemID = UUID.Zero;
  417. so.AbsolutePosition = sp.AbsolutePosition;
  418. so.AttachedAvatar = UUID.Zero;
  419. rootPart.SetParentLocalId(0);
  420. so.ClearPartAttachmentData();
  421. rootPart.ApplyPhysics(rootPart.GetEffectiveObjectFlags(), rootPart.VolumeDetectActive, m_scene.m_physicalPrim);
  422. so.HasGroupChanged = true;
  423. rootPart.Rezzed = DateTime.Now;
  424. rootPart.RemFlag(PrimFlags.TemporaryOnRez);
  425. so.AttachToBackup();
  426. m_scene.EventManager.TriggerParcelPrimCountTainted();
  427. rootPart.ScheduleFullUpdate();
  428. rootPart.ClearUndoState();
  429. }
  430. // What makes this method odd and unique is it tries to detach using an UUID.... Yay for standards.
  431. // To LocalId or UUID, *THAT* is the question. How now Brown UUID??
  432. private void DetachSingleAttachmentToInv(UUID itemID, ScenePresence sp)
  433. {
  434. if (itemID == UUID.Zero) // If this happened, someone made a mistake....
  435. return;
  436. // We can NOT use the dictionries here, as we are looking
  437. // for an entity by the fromAssetID, which is NOT the prim UUID
  438. EntityBase[] detachEntities = m_scene.GetEntities();
  439. SceneObjectGroup group;
  440. foreach (EntityBase entity in detachEntities)
  441. {
  442. if (entity is SceneObjectGroup)
  443. {
  444. group = (SceneObjectGroup)entity;
  445. if (group.GetFromItemID() == itemID)
  446. {
  447. m_scene.EventManager.TriggerOnAttach(group.LocalId, itemID, UUID.Zero);
  448. sp.RemoveAttachment(group);
  449. // Prepare sog for storage
  450. group.AttachedAvatar = UUID.Zero;
  451. group.ForEachPart(
  452. delegate(SceneObjectPart part)
  453. {
  454. // If there are any scripts,
  455. // then always trigger a new object and state persistence in UpdateKnownItem()
  456. if (part.Inventory.ContainsScripts())
  457. group.HasGroupChanged = true;
  458. }
  459. );
  460. group.RootPart.SetParentLocalId(0);
  461. group.IsAttachment = false;
  462. group.AbsolutePosition = group.RootPart.AttachedPos;
  463. UpdateKnownItem(sp.ControllingClient, group, group.GetFromItemID(), group.OwnerID);
  464. m_scene.DeleteSceneObject(group, false);
  465. return;
  466. }
  467. }
  468. }
  469. }
  470. public void UpdateAttachmentPosition(SceneObjectGroup sog, Vector3 pos)
  471. {
  472. // First we save the
  473. // attachment point information, then we update the relative
  474. // positioning. Then we have to mark the object as NOT an
  475. // attachment. This is necessary in order to correctly save
  476. // and retrieve GroupPosition information for the attachment.
  477. // Finally, we restore the object's attachment status.
  478. uint attachmentPoint = sog.AttachmentPoint;
  479. sog.UpdateGroupPosition(pos);
  480. sog.IsAttachment = false;
  481. sog.AbsolutePosition = sog.RootPart.AttachedPos;
  482. sog.AttachmentPoint = attachmentPoint;
  483. sog.HasGroupChanged = true;
  484. }
  485. /// <summary>
  486. /// Update the attachment asset for the new sog details if they have changed.
  487. /// </summary>
  488. /// <remarks>
  489. /// This is essential for preserving attachment attributes such as permission. Unlike normal scene objects,
  490. /// these details are not stored on the region.
  491. /// </remarks>
  492. /// <param name="remoteClient"></param>
  493. /// <param name="grp"></param>
  494. /// <param name="itemID"></param>
  495. /// <param name="agentID"></param>
  496. public void UpdateKnownItem(IClientAPI remoteClient, SceneObjectGroup grp, UUID itemID, UUID agentID)
  497. {
  498. if (grp != null)
  499. {
  500. if (!grp.HasGroupChanged)
  501. {
  502. m_log.DebugFormat(
  503. "[ATTACHMENTS MODULE]: Don't need to update asset for unchanged attachment {0}, attachpoint {1}",
  504. grp.UUID, grp.AttachmentPoint);
  505. return;
  506. }
  507. m_log.DebugFormat(
  508. "[ATTACHMENTS MODULE]: Updating asset for attachment {0}, attachpoint {1}",
  509. grp.UUID, grp.AttachmentPoint);
  510. string sceneObjectXml = SceneObjectSerializer.ToOriginalXmlFormat(grp);
  511. InventoryItemBase item = new InventoryItemBase(itemID, remoteClient.AgentId);
  512. item = m_scene.InventoryService.GetItem(item);
  513. if (item != null)
  514. {
  515. AssetBase asset = m_scene.CreateAsset(
  516. grp.GetPartName(grp.LocalId),
  517. grp.GetPartDescription(grp.LocalId),
  518. (sbyte)AssetType.Object,
  519. Utils.StringToBytes(sceneObjectXml),
  520. remoteClient.AgentId);
  521. m_scene.AssetService.Store(asset);
  522. item.AssetID = asset.FullID;
  523. item.Description = asset.Description;
  524. item.Name = asset.Name;
  525. item.AssetType = asset.Type;
  526. item.InvType = (int)InventoryType.Object;
  527. m_scene.InventoryService.UpdateItem(item);
  528. // this gets called when the agent logs off!
  529. if (remoteClient != null)
  530. remoteClient.SendInventoryItemCreateUpdate(item, 0);
  531. }
  532. }
  533. }
  534. /// <summary>
  535. /// Attach this scene object to the given avatar.
  536. /// </summary>
  537. ///
  538. /// This isn't publicly available since attachments should always perform the corresponding inventory
  539. /// operation (to show the attach in user inventory and update the asset with positional information).
  540. ///
  541. /// <param name="sp"></param>
  542. /// <param name="so"></param>
  543. /// <param name="attachmentpoint"></param>
  544. /// <param name="attachOffset"></param>
  545. /// <param name="silent"></param>
  546. protected void AttachToAgent(ScenePresence avatar, SceneObjectGroup so, uint attachmentpoint, Vector3 attachOffset, bool silent)
  547. {
  548. // m_log.DebugFormat("[ATTACHMENTS MODULE]: Adding attachment {0} to avatar {1} in pt {2} pos {3} {4}",
  549. // so.Name, avatar.Name, attachmentpoint, attachOffset, so.RootPart.AttachedPos);
  550. so.DetachFromBackup();
  551. // Remove from database and parcel prim count
  552. m_scene.DeleteFromStorage(so.UUID);
  553. m_scene.EventManager.TriggerParcelPrimCountTainted();
  554. so.AttachedAvatar = avatar.UUID;
  555. if (so.RootPart.PhysActor != null)
  556. {
  557. m_scene.PhysicsScene.RemovePrim(so.RootPart.PhysActor);
  558. so.RootPart.PhysActor = null;
  559. }
  560. so.AbsolutePosition = attachOffset;
  561. so.RootPart.AttachedPos = attachOffset;
  562. so.IsAttachment = true;
  563. so.RootPart.SetParentLocalId(avatar.LocalId);
  564. so.AttachmentPoint = attachmentpoint;
  565. avatar.AddAttachment(so);
  566. if (!silent)
  567. {
  568. // Killing it here will cause the client to deselect it
  569. // It then reappears on the avatar, deselected
  570. // through the full update below
  571. //
  572. if (so.IsSelected)
  573. {
  574. m_scene.SendKillObject(so.RootPart.LocalId);
  575. }
  576. so.IsSelected = false; // fudge....
  577. so.ScheduleGroupForFullUpdate();
  578. }
  579. // In case it is later dropped again, don't let
  580. // it get cleaned up
  581. so.RootPart.RemFlag(PrimFlags.TemporaryOnRez);
  582. }
  583. /// <summary>
  584. /// Add a scene object that was previously free in the scene as an attachment to an avatar.
  585. /// </summary>
  586. /// <param name="remoteClient"></param>
  587. /// <param name="grp"></param>
  588. /// <returns>The user inventory item created that holds the attachment.</returns>
  589. private InventoryItemBase AddSceneObjectAsAttachment(IClientAPI remoteClient, SceneObjectGroup grp)
  590. {
  591. // m_log.DebugFormat("[SCENE]: Called AddSceneObjectAsAttachment for object {0} {1} for {2} {3} {4}", grp.Name, grp.LocalId, remoteClient.Name, remoteClient.AgentId, AgentId);
  592. Vector3 inventoryStoredPosition = new Vector3
  593. (((grp.AbsolutePosition.X > (int)Constants.RegionSize)
  594. ? Constants.RegionSize - 6
  595. : grp.AbsolutePosition.X)
  596. ,
  597. (grp.AbsolutePosition.Y > (int)Constants.RegionSize)
  598. ? Constants.RegionSize - 6
  599. : grp.AbsolutePosition.Y,
  600. grp.AbsolutePosition.Z);
  601. Vector3 originalPosition = grp.AbsolutePosition;
  602. grp.AbsolutePosition = inventoryStoredPosition;
  603. // If we're being called from a script, then trying to serialize that same script's state will not complete
  604. // in any reasonable time period. Therefore, we'll avoid it. The worst that can happen is that if
  605. // the client/server crashes rather than logging out normally, the attachment's scripts will resume
  606. // without state on relog. Arguably, this is what we want anyway.
  607. string sceneObjectXml = SceneObjectSerializer.ToOriginalXmlFormat(grp, false);
  608. grp.AbsolutePosition = originalPosition;
  609. AssetBase asset = m_scene.CreateAsset(
  610. grp.GetPartName(grp.LocalId),
  611. grp.GetPartDescription(grp.LocalId),
  612. (sbyte)AssetType.Object,
  613. Utils.StringToBytes(sceneObjectXml),
  614. remoteClient.AgentId);
  615. m_scene.AssetService.Store(asset);
  616. InventoryItemBase item = new InventoryItemBase();
  617. item.CreatorId = grp.RootPart.CreatorID.ToString();
  618. item.CreatorData = grp.RootPart.CreatorData;
  619. item.Owner = remoteClient.AgentId;
  620. item.ID = UUID.Random();
  621. item.AssetID = asset.FullID;
  622. item.Description = asset.Description;
  623. item.Name = asset.Name;
  624. item.AssetType = asset.Type;
  625. item.InvType = (int)InventoryType.Object;
  626. InventoryFolderBase folder = m_scene.InventoryService.GetFolderForType(remoteClient.AgentId, AssetType.Object);
  627. if (folder != null)
  628. item.Folder = folder.ID;
  629. else // oopsies
  630. item.Folder = UUID.Zero;
  631. if ((remoteClient.AgentId != grp.RootPart.OwnerID) && m_scene.Permissions.PropagatePermissions())
  632. {
  633. item.BasePermissions = grp.RootPart.NextOwnerMask;
  634. item.CurrentPermissions = grp.RootPart.NextOwnerMask;
  635. item.NextPermissions = grp.RootPart.NextOwnerMask;
  636. item.EveryOnePermissions = grp.RootPart.EveryoneMask & grp.RootPart.NextOwnerMask;
  637. item.GroupPermissions = grp.RootPart.GroupMask & grp.RootPart.NextOwnerMask;
  638. }
  639. else
  640. {
  641. item.BasePermissions = grp.RootPart.BaseMask;
  642. item.CurrentPermissions = grp.RootPart.OwnerMask;
  643. item.NextPermissions = grp.RootPart.NextOwnerMask;
  644. item.EveryOnePermissions = grp.RootPart.EveryoneMask;
  645. item.GroupPermissions = grp.RootPart.GroupMask;
  646. }
  647. item.CreationDate = Util.UnixTimeSinceEpoch();
  648. // sets itemID so client can show item as 'attached' in inventory
  649. grp.SetFromItemID(item.ID);
  650. if (m_scene.AddInventoryItem(item))
  651. {
  652. remoteClient.SendInventoryItemCreateUpdate(item, 0);
  653. }
  654. else
  655. {
  656. if (m_dialogModule != null)
  657. m_dialogModule.SendAlertToUser(remoteClient, "Operation failed");
  658. }
  659. return item;
  660. }
  661. }
  662. }