AttachmentsModule.cs 39 KB

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