AttachmentsModule.cs 60 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501
  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 System.IO;
  31. using System.Text;
  32. using System.Threading;
  33. using System.Xml;
  34. using log4net;
  35. using Mono.Addins;
  36. using Nini.Config;
  37. using OpenMetaverse;
  38. using OpenMetaverse.Packets;
  39. using OpenSim.Framework;
  40. using OpenSim.Framework.Console;
  41. using OpenSim.Region.Framework;
  42. using OpenSim.Region.Framework.Interfaces;
  43. using OpenSim.Region.Framework.Scenes;
  44. using OpenSim.Region.Framework.Scenes.Serialization;
  45. using OpenSim.Services.Interfaces;
  46. using PermissionMask = OpenSim.Framework.PermissionMask;
  47. namespace OpenSim.Region.CoreModules.Avatar.Attachments
  48. {
  49. [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "AttachmentsModule")]
  50. public class AttachmentsModule : IAttachmentsModule, INonSharedRegionModule
  51. {
  52. #region INonSharedRegionModule
  53. private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
  54. public int DebugLevel { get; set; }
  55. private Scene m_scene;
  56. private IRegionConsole m_regionConsole;
  57. private IInventoryAccessModule m_invAccessModule;
  58. private bool m_wearReplacesAllOption = true;
  59. /// <summary>
  60. /// Are attachments enabled?
  61. /// </summary>
  62. public bool Enabled { get; private set; }
  63. public string Name { get { return "Attachments Module"; } }
  64. public Type ReplaceableInterface { get { return null; } }
  65. public void Initialise(IConfigSource source)
  66. {
  67. IConfig config = source.Configs["Attachments"];
  68. if (config != null)
  69. {
  70. Enabled = config.GetBoolean("Enabled", true);
  71. m_wearReplacesAllOption = config.GetBoolean("WearReplacesAll", true);
  72. }
  73. else
  74. {
  75. Enabled = true;
  76. }
  77. }
  78. public void AddRegion(Scene scene)
  79. {
  80. m_scene = scene;
  81. if (Enabled)
  82. {
  83. // Only register module with scene if it is enabled. All callers check for a null attachments module.
  84. // Ideally, there should be a null attachments module for when this core attachments module has been
  85. // disabled. Registering only when enabled allows for other attachments module implementations.
  86. m_scene.RegisterModuleInterface<IAttachmentsModule>(this);
  87. m_scene.EventManager.OnNewClient += SubscribeToClientEvents;
  88. m_scene.EventManager.OnStartScript += (localID, itemID) => OnScriptStateChange(localID, true);
  89. m_scene.EventManager.OnStopScript += (localID, itemID) => OnScriptStateChange(localID, false);
  90. }
  91. // TODO: Should probably be subscribing to CloseClient too, but this doesn't yet give us IClientAPI
  92. }
  93. public void RemoveRegion(Scene scene)
  94. {
  95. if (!Enabled)
  96. return;
  97. m_scene.UnregisterModuleInterface<IAttachmentsModule>(this);
  98. m_scene.EventManager.OnNewClient -= SubscribeToClientEvents;
  99. }
  100. public void RegionLoaded(Scene scene)
  101. {
  102. if (!Enabled)
  103. return;
  104. m_invAccessModule = m_scene.RequestModuleInterface<IInventoryAccessModule>();
  105. m_regionConsole = scene.RequestModuleInterface<IRegionConsole>();
  106. if (m_regionConsole != null)
  107. {
  108. m_regionConsole.AddCommand("AttachModule", false, "set auto_grant_attach_perms", "set auto_grant_attach_perms true|false", "Allow objects owned by the region owner or estate managers to obtain attach permissions without asking the user", HandleSetAutoGrantAttachPerms);
  109. }
  110. scene.AddCommand(
  111. "Debug",
  112. this,
  113. "debug attachments log",
  114. "debug attachments log [0|1]",
  115. "Turn on attachments debug logging",
  116. " <= 0 - turns off debug logging\n"
  117. + " >= 1 - turns on attachment message debug logging",
  118. HandleDebugAttachmentsLog);
  119. scene.AddCommand(
  120. "Debug",
  121. this,
  122. "debug attachments status",
  123. "debug attachments status",
  124. "Show current attachments debug status",
  125. HandleDebugAttachmentsStatus);
  126. // next should work on console root also
  127. MainConsole.Instance.Commands.AddCommand(
  128. "Users", true, "attachments show",
  129. "attachments show [<first-name> <last-name>]",
  130. "Show attachment information for avatars in this simulator.",
  131. "If no name is supplied then information for all avatars is shown.",
  132. HandleShowAttachmentsCommand);
  133. }
  134. public void Close()
  135. {
  136. if (!Enabled)
  137. return;
  138. RemoveRegion(m_scene);
  139. }
  140. private void HandleDebugAttachmentsLog(string module, string[] args)
  141. {
  142. int debugLevel;
  143. if (!(args.Length == 4 && int.TryParse(args[3], out debugLevel)))
  144. {
  145. MainConsole.Instance.Output("Usage: debug attachments log [0|1]");
  146. }
  147. else
  148. {
  149. DebugLevel = debugLevel;
  150. MainConsole.Instance.Output(
  151. "Set attachments debug level to {0} in {1}", DebugLevel, m_scene.Name);
  152. }
  153. }
  154. private void HandleDebugAttachmentsStatus(string module, string[] args)
  155. {
  156. MainConsole.Instance.Output("Settings for {0}", m_scene.Name);
  157. MainConsole.Instance.Output("Debug logging level: {0}", DebugLevel);
  158. }
  159. protected void HandleShowAttachmentsCommand(string module, string[] cmd)
  160. {
  161. if (cmd.Length != 2 && cmd.Length < 4)
  162. {
  163. MainConsole.Instance.Output("Usage: attachments show [<first-name> <last-name>]");
  164. return;
  165. }
  166. SceneManager sm = SceneManager.Instance;
  167. if(sm == null || sm.Scenes.Count == 0)
  168. return;
  169. bool targetNameSupplied = false;
  170. string optionalTargetFirstName = null;
  171. string optionalTargetLastName = null;
  172. if (cmd.Length >= 4)
  173. {
  174. targetNameSupplied = true;
  175. optionalTargetFirstName = cmd[2];
  176. optionalTargetLastName = cmd[3];
  177. }
  178. StringBuilder sb = new StringBuilder();
  179. sm.ForEachSelectedScene(
  180. scene =>
  181. {
  182. if (targetNameSupplied)
  183. {
  184. ScenePresence sp = scene.GetScenePresence(optionalTargetFirstName, optionalTargetLastName);
  185. if (sp != null && !sp.IsChildAgent)
  186. GetAttachmentsReport(sp, sb);
  187. }
  188. else
  189. {
  190. sb.AppendFormat("--- All attachments for region {0}:\n", scene.Name);
  191. scene.ForEachRootScenePresence(sp => GetAttachmentsReport(sp, sb));
  192. }
  193. });
  194. MainConsole.Instance.Output(sb.ToString());
  195. }
  196. private void GetAttachmentsReport(ScenePresence sp, StringBuilder sb)
  197. {
  198. sb.AppendFormat("Attachments for {0}\n\n", sp.Name);
  199. ConsoleDisplayList ct = new ConsoleDisplayList();
  200. List<SceneObjectGroup> attachmentObjects = sp.GetAttachments();
  201. for (int i = 0; i < attachmentObjects.Count; ++i)
  202. {
  203. SceneObjectGroup attachmentObject = attachmentObjects[i];
  204. ct.Indent = 2;
  205. ct.AddRow("Attachment Name", attachmentObject.Name);
  206. ct.AddRow("Local ID", attachmentObject.LocalId);
  207. ct.AddRow("Item ID", attachmentObject.UUID);
  208. ct.AddRow("From Item ID", attachmentObject.FromItemID);
  209. ct.AddRow("Attach Point", ((AttachmentPoint)attachmentObject.AttachmentPoint));
  210. ct.AddRow("Position", attachmentObject.RootPart.AttachedPos + "\n\n");
  211. }
  212. ct.AddToStringBuilder(sb);
  213. }
  214. private void SendConsoleOutput(UUID agentID, string text)
  215. {
  216. if (m_regionConsole == null)
  217. return;
  218. m_regionConsole.SendConsoleOutput(agentID, text);
  219. }
  220. private void HandleSetAutoGrantAttachPerms(string module, string[] parms)
  221. {
  222. UUID agentID = new UUID(parms[parms.Length - 1]);
  223. Array.Resize(ref parms, parms.Length - 1);
  224. if (parms.Length != 3)
  225. {
  226. SendConsoleOutput(agentID, "Command parameter error");
  227. return;
  228. }
  229. string val = parms[2];
  230. if (val != "true" && val != "false")
  231. {
  232. SendConsoleOutput(agentID, "Command parameter error");
  233. return;
  234. }
  235. m_scene.StoreExtraSetting("auto_grant_attach_perms", val);
  236. SendConsoleOutput(agentID, String.Format("auto_grant_attach_perms set to {0}", val));
  237. }
  238. /// <summary>
  239. /// Listen for client triggered running state changes so that we can persist the script's object if necessary.
  240. /// </summary>
  241. /// <param name='localID'></param>
  242. /// <param name='itemID'></param>
  243. private void OnScriptStateChange(uint localID, bool started)
  244. {
  245. SceneObjectGroup sog = m_scene.GetGroupByPrim(localID);
  246. if (sog != null && sog.IsAttachment)
  247. {
  248. if (!started)
  249. {
  250. // FIXME: This is a convoluted way for working out whether the script state has changed to stop
  251. // because it has been manually stopped or because the stop was called in UpdateDetachedObject() below
  252. // This needs to be handled in a less tangled way.
  253. ScenePresence sp = m_scene.GetScenePresence(sog.AttachedAvatar);
  254. if (sp.ControllingClient.IsActive)
  255. sog.HasGroupChanged = true;
  256. }
  257. else
  258. {
  259. sog.HasGroupChanged = true;
  260. }
  261. }
  262. }
  263. #endregion
  264. #region IAttachmentsModule
  265. public void CopyAttachments(IScenePresence sp, AgentData ad)
  266. {
  267. lock (sp.AttachmentsSyncLock)
  268. {
  269. // Attachment objects
  270. List<SceneObjectGroup> attachments = sp.GetAttachments();
  271. if (attachments.Count > 0)
  272. {
  273. ad.AttachmentObjects = new List<ISceneObject>(attachments.Count);
  274. ad.AttachmentObjectStates = new List<string>(attachments.Count);
  275. sp.InTransitScriptStates.Clear();
  276. for (int indx = 0; indx < attachments.Count; ++indx)
  277. {
  278. SceneObjectGroup sog = attachments[indx];
  279. // We need to make a copy and pass that copy
  280. // because of transfers withn the same sim
  281. ISceneObject clone = sog.CloneForNewScene();
  282. // Attachment module assumes that GroupPosition holds the offsets...!
  283. ((SceneObjectGroup)clone).RootPart.GroupPosition = sog.RootPart.AttachedPos;
  284. ((SceneObjectGroup)clone).IsAttachment = false;
  285. ad.AttachmentObjects.Add(clone);
  286. string state = sog.GetStateSnapshot();
  287. ad.AttachmentObjectStates.Add(state);
  288. sp.InTransitScriptStates.Add(state);
  289. // Scripts of the originals will be removed when the Agent is successfully removed.
  290. // sog.RemoveScriptInstances(true);
  291. }
  292. }
  293. }
  294. }
  295. public void CopyAttachments(AgentData ad, IScenePresence sp)
  296. {
  297. // m_log.DebugFormat("[ATTACHMENTS MODULE]: Copying attachment data into {0} in {1}", sp.Name, m_scene.Name);
  298. if (ad.AttachmentObjects != null && ad.AttachmentObjects.Count > 0)
  299. {
  300. lock (sp.AttachmentsSyncLock)
  301. sp.ClearAttachments();
  302. int i = 0;
  303. for (int indx = 0; indx < ad.AttachmentObjects.Count; ++indx)
  304. {
  305. SceneObjectGroup sog = (SceneObjectGroup)ad.AttachmentObjects[indx];
  306. sog.LocalId = 0;
  307. sog.RootPart.ClearUpdateSchedule();
  308. // m_log.DebugFormat(
  309. // "[ATTACHMENTS MODULE]: Copying script state with {0} bytes for object {1} for {2} in {3}",
  310. // ad.AttachmentObjectStates[i].Length, sog.Name, sp.Name, m_scene.Name);
  311. sog.SetState(ad.AttachmentObjectStates[i++], m_scene);
  312. m_scene.IncomingCreateObject(Vector3.Zero, sog);
  313. }
  314. }
  315. }
  316. public void RezAttachments(IScenePresence sp)
  317. {
  318. if (!Enabled)
  319. return;
  320. if (sp.Appearance == null)
  321. {
  322. m_log.WarnFormat("[ATTACHMENTS MODULE]: Appearance has not been initialized for agent {0}", sp.UUID);
  323. return;
  324. }
  325. if (sp.GetAttachmentsCount() > 0)
  326. {
  327. if (DebugLevel > 0)
  328. m_log.DebugFormat(
  329. "[ATTACHMENTS MODULE]: Not doing simulator-side attachment rez for {0} in {1} as their viewer has already rezzed attachments",
  330. m_scene.Name, sp.Name);
  331. return;
  332. }
  333. if (DebugLevel > 0)
  334. m_log.DebugFormat("[ATTACHMENTS MODULE]: Rezzing any attachments for {0} from simulator-side", sp.Name);
  335. XmlDocument doc = new XmlDocument();
  336. string stateData = String.Empty;
  337. IAttachmentsService attServ = m_scene.RequestModuleInterface<IAttachmentsService>();
  338. if (attServ != null)
  339. {
  340. m_log.DebugFormat("[ATTACHMENT]: Loading attachment data from attachment service");
  341. stateData = attServ.Get(sp.UUID.ToString());
  342. if (stateData != String.Empty)
  343. {
  344. try
  345. {
  346. doc.LoadXml(stateData);
  347. }
  348. catch { }
  349. }
  350. }
  351. Dictionary<UUID, string> itemData = new Dictionary<UUID, string>();
  352. XmlNodeList nodes = doc.GetElementsByTagName("Attachment");
  353. if (nodes.Count > 0)
  354. {
  355. foreach (XmlNode n in nodes)
  356. {
  357. XmlElement elem = (XmlElement)n;
  358. string itemID = elem.GetAttribute("ItemID");
  359. string xml = elem.InnerXml;
  360. itemData[new UUID(itemID)] = xml;
  361. }
  362. }
  363. List<AvatarAttachment> attachments = sp.Appearance.GetAttachments();
  364. // Let's get all items at once, so they get cached
  365. UUID[] items = new UUID[attachments.Count];
  366. for (int i = 0; i < attachments.Count; ++i)
  367. items[i] = attachments[i].ItemID;
  368. m_scene.InventoryService.GetMultipleItems(sp.UUID, items);
  369. for (int indx = 0; indx < attachments.Count; ++indx)
  370. {
  371. AvatarAttachment attach = attachments[indx];
  372. uint attachmentPt = (uint)attach.AttachPoint;
  373. // m_log.DebugFormat(
  374. // "[ATTACHMENTS MODULE]: Doing initial rez of attachment with itemID {0}, assetID {1}, point {2} for {3} in {4}",
  375. // attach.ItemID, attach.AssetID, p, sp.Name, m_scene.RegionInfo.RegionName);
  376. try
  377. {
  378. string xmlData;
  379. XmlDocument d = null;
  380. if (itemData.TryGetValue(attach.ItemID, out xmlData))
  381. {
  382. d = new XmlDocument();
  383. d.LoadXml(xmlData);
  384. m_log.InfoFormat("[ATTACHMENT]: Found saved state for item {0}, loading it", attach.ItemID);
  385. }
  386. // If we're an NPC then skip all the item checks and manipulations since we don't have an
  387. // inventory right now.
  388. RezSingleAttachmentFromInventoryInternal(
  389. sp, sp.PresenceType == PresenceType.Npc ? UUID.Zero : attach.ItemID, attach.AssetID, attachmentPt, true, d);
  390. }
  391. catch (Exception e)
  392. {
  393. UUID agentId = (sp.ControllingClient == null) ? default(UUID) : sp.ControllingClient.AgentId;
  394. m_log.ErrorFormat("[ATTACHMENTS MODULE]: Unable to rez attachment with itemID {0}, assetID {1}, point {2} for {3}: {4}\n{5}",
  395. attach.ItemID, attach.AssetID, attachmentPt, agentId, e.Message, e.StackTrace);
  396. }
  397. }
  398. }
  399. public void DeRezAttachments(IScenePresence sp)
  400. {
  401. if (!Enabled)
  402. return;
  403. List<SceneObjectGroup> attachments = sp.GetAttachments();
  404. if (DebugLevel > 0)
  405. m_log.DebugFormat(
  406. "[ATTACHMENTS MODULE]: Saving for {0} attachments for {1} in {2}",
  407. attachments.Count, sp.Name, m_scene.Name);
  408. if (attachments.Count <= 0)
  409. return;
  410. if (sp.PresenceType != PresenceType.Npc)
  411. {
  412. lock (sp.AttachmentsSyncLock)
  413. {
  414. for (int i = 0; i < attachments.Count; ++i)
  415. {
  416. SceneObjectGroup sog = attachments[i];
  417. UpdateDetachedObject(sp, sog, PrepareScriptInstanceForSave(sog, false));
  418. }
  419. sp.ClearAttachments();
  420. }
  421. }
  422. else
  423. {
  424. lock (sp.AttachmentsSyncLock)
  425. {
  426. for (int i = 0; i < attachments.Count; ++i)
  427. UpdateDetachedObject(sp, attachments[i], String.Empty);
  428. sp.ClearAttachments();
  429. }
  430. }
  431. }
  432. public void DeleteAttachmentsFromScene(IScenePresence sp, bool silent)
  433. {
  434. if (!Enabled)
  435. return;
  436. if (DebugLevel > 0)
  437. m_log.DebugFormat(
  438. "[ATTACHMENTS MODULE]: Deleting attachments from scene {0} for {1}, silent = {2}",
  439. m_scene.RegionInfo.RegionName, sp.Name, silent);
  440. List<SceneObjectGroup> attachments = sp.GetAttachments();
  441. for(int i = 0; i < attachments.Count; ++i)
  442. {
  443. SceneObjectGroup sog = attachments[i];
  444. sog.Scene.DeleteSceneObject(sog, silent);
  445. }
  446. sp.ClearAttachments();
  447. }
  448. public bool AttachObject(IScenePresence sp, SceneObjectGroup group, uint attachmentPt, bool silent,
  449. bool addToInventory, bool append)
  450. {
  451. if (!Enabled)
  452. return false;
  453. return AttachObjectInternal(sp, group, attachmentPt, silent, addToInventory, false, append);
  454. }
  455. /// <summary>
  456. /// Internal method which actually does all the work for attaching an object.
  457. /// </summary>
  458. /// <returns>The object attached.</returns>
  459. /// <param name='sp'></param>
  460. /// <param name='group'>The object to attach.</param>
  461. /// <param name='attachmentPt'></param>
  462. /// <param name='silent'></param>
  463. /// <param name='addToInventory'>If true then add object to user inventory.</param>
  464. /// <param name='resumeScripts'>If true then scripts are resumed on the attached object.</param>
  465. private bool AttachObjectInternal(IScenePresence sp, SceneObjectGroup group, uint attachmentPt,
  466. bool silent, bool addToInventory, bool resumeScripts, bool append)
  467. {
  468. // m_log.DebugFormat(
  469. // "[ATTACHMENTS MODULE]: Attaching object {0} {1} to {2} point {3} from ground (silent = {4})",
  470. // group.Name, group.LocalId, sp.Name, attachmentPt, silent);
  471. if (group.GetSittingAvatarsCount() != 0)
  472. {
  473. if (DebugLevel > 0)
  474. m_log.WarnFormat(
  475. "[ATTACHMENTS MODULE]: Ignoring request to attach {0} {1} to {2} on {3} since {4} avatars are still sitting on it",
  476. group.Name, group.LocalId, sp.Name, attachmentPt, group.GetSittingAvatarsCount());
  477. return false;
  478. }
  479. Vector3 attachPos = group.AbsolutePosition;
  480. // TODO: this short circuits multiple attachments functionality in LL viewer 2.1+ and should
  481. // be removed when that functionality is implemented in opensim
  482. attachmentPt &= 0x7f;
  483. // If the attachment point isn't the same as the one previously used
  484. // set it's offset position = 0 so that it appears on the attachment point
  485. // and not in a weird location somewhere unknown.
  486. if (attachmentPt != (uint)AttachmentPoint.Default && attachmentPt != group.AttachmentPoint)
  487. {
  488. attachPos = Vector3.Zero;
  489. }
  490. // if the attachment point is the same as previous, make sure we get the saved
  491. // position info.
  492. if (attachmentPt != 0 && attachmentPt == group.RootPart.Shape.LastAttachPoint)
  493. {
  494. attachPos = group.RootPart.AttachedPos;
  495. }
  496. // AttachmentPt 0 means the client chose to 'wear' the attachment.
  497. if (attachmentPt == (uint)AttachmentPoint.Default)
  498. {
  499. // Check object for stored attachment point
  500. attachmentPt = group.AttachmentPoint;
  501. }
  502. // if we didn't find an attach point, look for where it was last attached
  503. if (attachmentPt == 0)
  504. {
  505. attachmentPt = (uint)group.RootPart.Shape.LastAttachPoint;
  506. attachPos = group.RootPart.AttachedPos;
  507. }
  508. // if we still didn't find a suitable attachment point.......
  509. if (attachmentPt == 0)
  510. {
  511. // Stick it on left hand with Zero Offset from the attachment point.
  512. attachmentPt = (uint)AttachmentPoint.LeftHand;
  513. attachPos = Vector3.Zero;
  514. }
  515. if(attachmentPt > (uint)AttachmentPoint.LastValid)
  516. {
  517. m_log.WarnFormat("[ATTACHMENTS MODULE]: Invalid attachment point {0} SP {1}", attachmentPt, sp.Name);
  518. return false;
  519. }
  520. List<SceneObjectGroup> attachments = sp.GetAttachments();
  521. List<SceneObjectGroup> toRemove = new List<SceneObjectGroup>(attachments.Count);
  522. bool doRemCheck = !append;
  523. for(int i = 0; i < attachments.Count; ++i)
  524. {
  525. SceneObjectGroup sog = attachments[i];
  526. // duplications ?
  527. if (group.UUID == sog.UUID)
  528. {
  529. // if (DebugLevel > 0)
  530. // m_log.WarnFormat(
  531. // "[ATTACHMENTS MODULE]: Ignoring request to attach {0} {1} to {2} on {3} since it's already attached",
  532. // group.Name, group.LocalId, sp.Name, attachmentPt);
  533. return false;
  534. }
  535. if(doRemCheck)
  536. {
  537. if(sog.AttachmentPoint == attachmentPt)
  538. {
  539. toRemove.Add(sog);
  540. if(!m_wearReplacesAllOption)
  541. doRemCheck = false;
  542. }
  543. }
  544. }
  545. if(attachments.Count - toRemove.Count >= Constants.MaxAgentAttachments)
  546. {
  547. m_log.WarnFormat("[ATTACHMENTS MODULE]: Max attachments exceded {0}",sp.Name);
  548. return false;
  549. }
  550. group.DetachFromBackup();
  551. group.AttachmentPoint = attachmentPt;
  552. group.RootPart.AttachedPos = attachPos;
  553. // If we're not appending, remove the rest as well
  554. lock (sp.AttachmentsSyncLock)
  555. {
  556. if (toRemove.Count > 0)
  557. {
  558. for (int i = 0; i< toRemove.Count; ++i)
  559. {
  560. SceneObjectGroup g = toRemove[i];
  561. if (g.FromItemID != UUID.Zero)
  562. DetachSingleAttachmentToInv(sp, g);
  563. }
  564. }
  565. if (addToInventory && sp.PresenceType != PresenceType.Npc)
  566. UpdateUserInventoryWithAttachment(sp, group, attachmentPt, append);
  567. AttachToAgent(sp, group, attachmentPt, attachPos, silent);
  568. if (resumeScripts)
  569. {
  570. // Fire after attach, so we don't get messy perms dialogs
  571. // 4 == AttachedRez
  572. group.CreateScriptInstances(0, true, m_scene.DefaultScriptEngine, 4);
  573. group.ResumeScripts();
  574. }
  575. else
  576. // Do this last so that event listeners have access to all the effects of the attachment
  577. // this can't be done when creating scripts:
  578. // scripts do internal enqueue of attach event
  579. // and not all scripts are loaded at this point
  580. m_scene.EventManager.TriggerOnAttach(group.LocalId, group.FromItemID, sp.UUID);
  581. }
  582. return true;
  583. }
  584. private void UpdateUserInventoryWithAttachment(IScenePresence sp, SceneObjectGroup group, uint attachmentPt, bool append)
  585. {
  586. // Add the new attachment to inventory if we don't already have it.
  587. UUID newAttachmentItemID = group.FromItemID;
  588. if (newAttachmentItemID == UUID.Zero)
  589. newAttachmentItemID = AddSceneObjectAsNewAttachmentInInv(sp, group).ID;
  590. ShowAttachInUserInventory(sp, attachmentPt, newAttachmentItemID, group, append);
  591. }
  592. public ISceneEntity RezSingleAttachmentFromInventory(IScenePresence sp, UUID itemID, uint AttachmentPt)
  593. {
  594. return RezSingleAttachmentFromInventory(sp, itemID, AttachmentPt, null);
  595. }
  596. public ISceneEntity RezSingleAttachmentFromInventory(IScenePresence sp, UUID itemID, uint AttachmentPt, XmlDocument doc)
  597. {
  598. if (!Enabled)
  599. return null;
  600. if (DebugLevel > 0)
  601. m_log.DebugFormat(
  602. "[ATTACHMENTS MODULE]: RezSingleAttachmentFromInventory to point {0} from item {1} for {2} in {3}",
  603. (AttachmentPoint)AttachmentPt, itemID, sp.Name, m_scene.Name);
  604. // We check the attachments in the avatar appearance here rather than the objects attached to the
  605. // ScenePresence itself so that we can ignore calls by viewer 2/3 to attach objects on startup. We are
  606. // already doing this in ScenePresence.MakeRootAgent(). Simulator-side attaching needs to be done
  607. // because pre-outfit folder viewers (most version 1 viewers) require it.
  608. bool alreadyOn = false;
  609. List<AvatarAttachment> existingAttachments = sp.Appearance.GetAttachments();
  610. foreach (AvatarAttachment existingAttachment in existingAttachments)
  611. {
  612. if (existingAttachment.ItemID == itemID)
  613. {
  614. alreadyOn = true;
  615. break;
  616. }
  617. }
  618. if (alreadyOn)
  619. {
  620. if (DebugLevel > 0)
  621. m_log.DebugFormat(
  622. "[ATTACHMENTS MODULE]: Ignoring request by {0} to wear item {1} at {2} since it is already worn",
  623. sp.Name, itemID, AttachmentPt);
  624. return null;
  625. }
  626. bool append = (AttachmentPt & 0x80) != 0;
  627. AttachmentPt &= 0x7f;
  628. return RezSingleAttachmentFromInventoryInternal(sp, itemID, UUID.Zero, AttachmentPt, append, doc);
  629. }
  630. public void RezMultipleAttachmentsFromInventory(IScenePresence sp, List<KeyValuePair<UUID, uint>> rezlist)
  631. {
  632. if (!Enabled)
  633. return;
  634. if (DebugLevel > 0)
  635. m_log.DebugFormat(
  636. "[ATTACHMENTS MODULE]: Rezzing {0} attachments from inventory for {1} in {2}",
  637. rezlist.Count, sp.Name, m_scene.Name);
  638. foreach (KeyValuePair<UUID, uint> rez in rezlist)
  639. {
  640. RezSingleAttachmentFromInventory(sp, rez.Key, rez.Value);
  641. }
  642. }
  643. public void DetachSingleAttachmentToGround(IScenePresence sp, uint soLocalId)
  644. {
  645. Vector3 pos = new Vector3(2.5f, 0f, 0f);
  646. pos *= ((ScenePresence)sp).Rotation;
  647. pos += sp.AbsolutePosition;
  648. DetachSingleAttachmentToGround(sp, soLocalId, pos, Quaternion.Identity);
  649. }
  650. public void DetachSingleAttachmentToGround(IScenePresence sp, uint soLocalId, Vector3 absolutePos, Quaternion absoluteRot)
  651. {
  652. if (!Enabled)
  653. return;
  654. if (DebugLevel > 0)
  655. m_log.DebugFormat(
  656. "[ATTACHMENTS MODULE]: DetachSingleAttachmentToGround() for {0}, object {1}",
  657. sp.UUID, soLocalId);
  658. SceneObjectGroup so = m_scene.GetGroupByPrim(soLocalId);
  659. if (so == null)
  660. return;
  661. if (so.AttachedAvatar != sp.UUID)
  662. return;
  663. UUID inventoryID = so.FromItemID;
  664. // As per Linden spec, drop is disabled for temp attachs
  665. if (inventoryID == UUID.Zero)
  666. return;
  667. if (DebugLevel > 0)
  668. m_log.DebugFormat(
  669. "[ATTACHMENTS MODULE]: In DetachSingleAttachmentToGround(), object is {0} {1}, associated item is {2}",
  670. so.Name, so.LocalId, inventoryID);
  671. lock (sp.AttachmentsSyncLock)
  672. {
  673. if (!m_scene.Permissions.CanRezObject(
  674. so.PrimCount, sp.UUID, sp.AbsolutePosition))
  675. return;
  676. bool changed = false;
  677. if (inventoryID != UUID.Zero)
  678. changed = sp.Appearance.DetachAttachment(inventoryID);
  679. if (changed && m_scene.AvatarFactory != null)
  680. m_scene.AvatarFactory.QueueAppearanceSave(sp.UUID);
  681. so.RootPart.Shape.LastAttachPoint = (byte)so.AttachmentPoint;
  682. sp.RemoveAttachment(so);
  683. so.FromItemID = UUID.Zero;
  684. so.AttachedAvatar = UUID.Zero;
  685. so.ClearPartAttachmentData();
  686. SceneObjectPart rootPart = so.RootPart;
  687. rootPart.SetParentLocalId(0);
  688. so.AbsolutePosition = absolutePos;
  689. if (absoluteRot != Quaternion.Identity)
  690. {
  691. so.UpdateGroupRotationR(absoluteRot);
  692. }
  693. rootPart.RemFlag(PrimFlags.TemporaryOnRez);
  694. so.ApplyPhysics();
  695. rootPart.Rezzed = DateTime.Now;
  696. so.AttachToBackup();
  697. m_scene.EventManager.TriggerParcelPrimCountTainted();
  698. rootPart.ClearUndoState();
  699. List<UUID> uuids = new List<UUID>();
  700. uuids.Add(inventoryID);
  701. m_scene.InventoryService.DeleteItems(sp.UUID, uuids);
  702. sp.ControllingClient.SendRemoveInventoryItem(inventoryID);
  703. }
  704. m_scene.EventManager.TriggerOnAttach(so.LocalId, so.UUID, UUID.Zero);
  705. // Attach (NULL) stops scripts. We don't want that. Resume them.
  706. so.RemoveScriptsPermissions(4 | 2048); // take controls and camera control
  707. so.ResumeScripts();
  708. so.HasGroupChanged = true;
  709. so.RootPart.ScheduleFullUpdate();
  710. so.ScheduleGroupForTerseUpdate();
  711. }
  712. public void DetachSingleAttachmentToInv(IScenePresence sp, SceneObjectGroup so)
  713. {
  714. if (so.AttachedAvatar != sp.UUID)
  715. {
  716. m_log.WarnFormat(
  717. "[ATTACHMENTS MODULE]: Tried to detach object {0} from {1} {2} but attached avatar id was {3} in {4}",
  718. so.Name, sp.Name, sp.UUID, so.AttachedAvatar, m_scene.RegionInfo.RegionName);
  719. return;
  720. }
  721. so.RemoveScriptsPermissions(4 | 2048); // take controls and camera control
  722. // If this didn't come from inventory, it also shouldn't go there
  723. // on detach. It's likely a temp attachment.
  724. if (so.FromItemID == UUID.Zero)
  725. {
  726. PrepareScriptInstanceForSave(so, true);
  727. lock (sp.AttachmentsSyncLock)
  728. {
  729. bool changed = sp.Appearance.DetachAttachment(so.FromItemID);
  730. if (changed && m_scene.AvatarFactory != null)
  731. m_scene.AvatarFactory.QueueAppearanceSave(sp.UUID);
  732. sp.RemoveAttachment(so);
  733. }
  734. m_scene.DeleteSceneObject(so, false, false);
  735. so.RemoveScriptInstances(true);
  736. so.Clear();
  737. return;
  738. }
  739. if (DebugLevel > 0)
  740. m_log.DebugFormat(
  741. "[ATTACHMENTS MODULE]: Detaching object {0} {1} (FromItemID {2}) for {3} in {4}",
  742. so.Name, so.LocalId, so.FromItemID, sp.Name, m_scene.Name);
  743. // Scripts MUST be snapshotted before the object is
  744. // removed from the scene because doing otherwise will
  745. // clobber the run flag
  746. // This must be done outside the sp.AttachmentSyncLock so that there is no risk of a deadlock from
  747. // scripts performing attachment operations at the same time. Getting object states stops the scripts.
  748. string scriptedState = PrepareScriptInstanceForSave(so, true);
  749. lock (sp.AttachmentsSyncLock)
  750. {
  751. // Save avatar attachment information
  752. // m_log.Debug("[ATTACHMENTS MODULE]: Detaching from UserID: " + sp.UUID + ", ItemID: " + itemID);
  753. bool changed = sp.Appearance.DetachAttachment(so.FromItemID);
  754. if (changed && m_scene.AvatarFactory != null)
  755. m_scene.AvatarFactory.QueueAppearanceSave(sp.UUID);
  756. sp.RemoveAttachment(so);
  757. UpdateDetachedObject(sp, so, scriptedState);
  758. }
  759. }
  760. public void UpdateAttachmentPosition(SceneObjectGroup sog, Vector3 pos)
  761. {
  762. if (!Enabled)
  763. return;
  764. sog.UpdateGroupPosition(pos);
  765. sog.HasGroupChanged = true;
  766. }
  767. #endregion
  768. #region AttachmentModule private methods
  769. // This is public but is not part of the IAttachmentsModule interface.
  770. // RegionCombiner module needs to poke at it to deliver client events.
  771. // This breaks the encapsulation of the module and should get fixed somehow.
  772. public void SubscribeToClientEvents(IClientAPI client)
  773. {
  774. client.OnRezSingleAttachmentFromInv += Client_OnRezSingleAttachmentFromInv;
  775. client.OnRezMultipleAttachmentsFromInv += Client_OnRezMultipleAttachmentsFromInv;
  776. client.OnObjectAttach += Client_OnObjectAttach;
  777. client.OnObjectDetach += Client_OnObjectDetach;
  778. client.OnDetachAttachmentIntoInv += Client_OnDetachAttachmentIntoInv;
  779. client.OnObjectDrop += Client_OnObjectDrop;
  780. }
  781. // This is public but is not part of the IAttachmentsModule interface.
  782. // RegionCombiner module needs to poke at it to deliver client events.
  783. // This breaks the encapsulation of the module and should get fixed somehow.
  784. public void UnsubscribeFromClientEvents(IClientAPI client)
  785. {
  786. client.OnRezSingleAttachmentFromInv -= Client_OnRezSingleAttachmentFromInv;
  787. client.OnRezMultipleAttachmentsFromInv -= Client_OnRezMultipleAttachmentsFromInv;
  788. client.OnObjectAttach -= Client_OnObjectAttach;
  789. client.OnObjectDetach -= Client_OnObjectDetach;
  790. client.OnDetachAttachmentIntoInv -= Client_OnDetachAttachmentIntoInv;
  791. client.OnObjectDrop -= Client_OnObjectDrop;
  792. }
  793. /// <summary>
  794. /// Update the attachment asset for the new sog details if they have changed.
  795. /// </summary>
  796. /// <remarks>
  797. /// This is essential for preserving attachment attributes such as permission. Unlike normal scene objects,
  798. /// these details are not stored on the region.
  799. /// </remarks>
  800. /// <param name="sp"></param>
  801. /// <param name="grp"></param>
  802. /// <param name="saveAllScripted"></param>
  803. private void UpdateKnownItem(IScenePresence sp, SceneObjectGroup grp, string scriptedState)
  804. {
  805. if (grp.FromItemID == UUID.Zero)
  806. {
  807. // We can't save temp attachments
  808. grp.HasGroupChanged = false;
  809. return;
  810. }
  811. if(sp.IsNPC)
  812. return;
  813. if (grp.HasGroupChanged)
  814. {
  815. m_log.DebugFormat(
  816. "[ATTACHMENTS MODULE]: Updating asset for attachment {0}, attachpoint {1}",
  817. grp.UUID, grp.AttachmentPoint);
  818. string sceneObjectXml = SceneObjectSerializer.ToOriginalXmlFormat(grp, scriptedState);
  819. InventoryItemBase item = m_scene.InventoryService.GetItem(sp.UUID, grp.FromItemID);
  820. if (item != null)
  821. {
  822. // attach is rez, need to update permissions
  823. item.Flags &= ~(uint)(InventoryItemFlags.ObjectSlamPerm | InventoryItemFlags.ObjectOverwriteBase |
  824. InventoryItemFlags.ObjectOverwriteOwner | InventoryItemFlags.ObjectOverwriteGroup |
  825. InventoryItemFlags.ObjectOverwriteEveryone | InventoryItemFlags.ObjectOverwriteNextOwner);
  826. uint permsBase = (uint)(PermissionMask.Copy | PermissionMask.Transfer |
  827. PermissionMask.Modify | PermissionMask.Move |
  828. PermissionMask.Export | PermissionMask.FoldedMask);
  829. permsBase &= grp.CurrentAndFoldedNextPermissions();
  830. permsBase |= (uint)PermissionMask.Move;
  831. item.BasePermissions = permsBase;
  832. item.CurrentPermissions = permsBase;
  833. item.NextPermissions = permsBase & grp.RootPart.NextOwnerMask | (uint)PermissionMask.Move;
  834. item.EveryOnePermissions = permsBase & grp.RootPart.EveryoneMask;
  835. item.GroupPermissions = permsBase & grp.RootPart.GroupMask;
  836. item.CurrentPermissions &=
  837. ((uint)PermissionMask.Copy |
  838. (uint)PermissionMask.Transfer |
  839. (uint)PermissionMask.Modify |
  840. (uint)PermissionMask.Move |
  841. (uint)PermissionMask.Export |
  842. (uint)PermissionMask.FoldedMask); // Preserve folded permissions ??
  843. string name = grp.RootPart.Name;
  844. string desc = grp.RootPart.Description;
  845. AssetBase asset = m_scene.CreateAsset(
  846. name, desc,
  847. (sbyte)AssetType.Object,
  848. Utils.StringToBytes(sceneObjectXml),
  849. sp.UUID);
  850. item.Name = name;
  851. item.Description = desc;
  852. item.AssetID = asset.FullID;
  853. item.AssetType = (int)AssetType.Object;
  854. item.InvType = (int)InventoryType.Object;
  855. if (m_invAccessModule != null)
  856. m_invAccessModule.UpdateInventoryItemAsset(sp.UUID, item, asset);
  857. if (sp.ControllingClient != null)
  858. sp.ControllingClient.SendInventoryItemCreateUpdate(item, 0);
  859. }
  860. grp.HasGroupChanged = false; // Prevent it being saved over and over
  861. }
  862. else if (DebugLevel > 0)
  863. {
  864. m_log.DebugFormat(
  865. "[ATTACHMENTS MODULE]: Don't need to update asset for unchanged attachment {0}, attachpoint {1}",
  866. grp.UUID, grp.AttachmentPoint);
  867. }
  868. }
  869. /// <summary>
  870. /// Attach this scene object to the given avatar.
  871. /// </summary>
  872. /// <remarks>
  873. /// This isn't publicly available since attachments should always perform the corresponding inventory
  874. /// operation (to show the attach in user inventory and update the asset with positional information).
  875. /// </remarks>
  876. /// <param name="sp"></param>
  877. /// <param name="so"></param>
  878. /// <param name="attachmentpoint"></param>
  879. /// <param name="attachOffset"></param>
  880. /// <param name="silent"></param>
  881. private void AttachToAgent(
  882. IScenePresence sp, SceneObjectGroup so, uint attachmentpoint, Vector3 attachOffset, bool silent)
  883. {
  884. if (DebugLevel > 0)
  885. m_log.DebugFormat(
  886. "[ATTACHMENTS MODULE]: Adding attachment {0} to avatar {1} at pt {2} pos {3} {4} in {5}",
  887. so.Name, sp.Name, attachmentpoint, attachOffset, so.RootPart.AttachedPos, m_scene.Name);
  888. // Remove from database and parcel prim count
  889. m_scene.DeleteFromStorage(so.UUID);
  890. m_scene.EventManager.TriggerParcelPrimCountTainted();
  891. foreach (SceneObjectPart part in so.Parts)
  892. {
  893. // if (part.KeyframeMotion != null)
  894. // part.KeyframeMotion.Suspend();
  895. if (part.PhysActor != null)
  896. {
  897. part.RemoveFromPhysics();
  898. }
  899. }
  900. so.RootPart.SetParentLocalId(sp.LocalId);
  901. so.AttachedAvatar = sp.UUID;
  902. so.AttachmentPoint = attachmentpoint;
  903. so.RootPart.AttachedPos = attachOffset;
  904. so.AbsolutePosition = attachOffset;
  905. so.IsAttachment = true;
  906. sp.AddAttachment(so);
  907. if (!silent)
  908. {
  909. if (so.HasPrivateAttachmentPoint)
  910. {
  911. if (DebugLevel > 0)
  912. m_log.DebugFormat(
  913. "[ATTACHMENTS MODULE]: Killing private HUD {0} for avatars other than {1} at attachment point {2}",
  914. so.Name, sp.Name, so.AttachmentPoint);
  915. // As this scene object can now only be seen by the attaching avatar, tell everybody else in the
  916. // scene that it's no longer in their awareness.
  917. m_scene.ForEachClient(
  918. client =>
  919. { if (client.AgentId != so.AttachedAvatar)
  920. client.SendKillObject(new List<uint>() { so.LocalId });
  921. });
  922. }
  923. // Fudge below is an extremely unhelpful comment. It's probably here so that the scheduled full update
  924. // will succeed, as that will not update if an attachment is selected.
  925. so.IsSelected = false; // fudge....
  926. so.ScheduleGroupForFullAnimUpdate();
  927. }
  928. // In case it is later dropped again, don't let
  929. // it get cleaned up
  930. so.RootPart.RemFlag(PrimFlags.TemporaryOnRez);
  931. }
  932. /// <summary>
  933. /// Add a scene object as a new attachment in the user inventory.
  934. /// </summary>
  935. /// <param name="remoteClient"></param>
  936. /// <param name="grp"></param>
  937. /// <returns>The user inventory item created that holds the attachment.</returns>
  938. private InventoryItemBase AddSceneObjectAsNewAttachmentInInv(IScenePresence sp, SceneObjectGroup grp)
  939. {
  940. if (m_invAccessModule == null)
  941. return null;
  942. if (DebugLevel > 0)
  943. m_log.DebugFormat(
  944. "[ATTACHMENTS MODULE]: Called AddSceneObjectAsAttachment for object {0} {1} for {2}",
  945. grp.Name, grp.LocalId, sp.Name);
  946. InventoryItemBase newItem
  947. = m_invAccessModule.CopyToInventory(
  948. DeRezAction.TakeCopy,
  949. m_scene.InventoryService.GetFolderForType(sp.UUID, FolderType.Object).ID,
  950. new List<SceneObjectGroup> { grp },
  951. sp.ControllingClient, true)[0];
  952. // sets itemID so client can show item as 'attached' in inventory
  953. grp.FromItemID = newItem.ID;
  954. return newItem;
  955. }
  956. /// <summary>
  957. /// Prepares the script instance for save.
  958. /// </summary>
  959. /// <remarks>
  960. /// This involves triggering the detach event and getting the script state (which also stops the script)
  961. /// This MUST be done outside sp.AttachmentsSyncLock, since otherwise there is a chance of deadlock if a
  962. /// running script is performing attachment operations.
  963. /// </remarks>
  964. /// <returns>
  965. /// The script state ready for persistence.
  966. /// </returns>
  967. /// <param name='grp'>
  968. /// </param>
  969. /// <param name='fireDetachEvent'>
  970. /// If true, then fire the script event before we save its state.
  971. /// </param>
  972. private string PrepareScriptInstanceForSave(SceneObjectGroup grp, bool fireDetachEvent)
  973. {
  974. if (fireDetachEvent)
  975. {
  976. m_scene.EventManager.TriggerOnAttach(grp.LocalId, grp.FromItemID, UUID.Zero);
  977. // Allow detach event time to do some work before stopping the script
  978. Thread.Sleep(30);
  979. }
  980. using (StringWriter sw = new StringWriter())
  981. {
  982. using (XmlTextWriter writer = new XmlTextWriter(sw))
  983. {
  984. grp.SaveScriptedState(writer);
  985. }
  986. return sw.ToString();
  987. }
  988. }
  989. private void UpdateDetachedObject(IScenePresence sp, SceneObjectGroup so, string scriptedState)
  990. {
  991. // Don't save attachments for HG visitors, it
  992. // messes up their inventory. When a HG visitor logs
  993. // out on a foreign grid, their attachments will be
  994. // reloaded in the state they were in when they left
  995. // the home grid. This is best anyway as the visited
  996. // grid may use an incompatible script engine.
  997. bool saveChanged
  998. = sp.PresenceType != PresenceType.Npc
  999. && (m_scene.UserManagementModule == null
  1000. || m_scene.UserManagementModule.IsLocalGridUser(sp.UUID));
  1001. // Remove the object from the scene so no more updates
  1002. // are sent. Doing this before the below changes will ensure
  1003. // updates can't cause "HUD artefacts"
  1004. m_scene.DeleteSceneObject(so, false, false);
  1005. // Prepare sog for storage
  1006. so.AttachedAvatar = UUID.Zero;
  1007. so.RootPart.SetParentLocalId(0);
  1008. so.IsAttachment = false;
  1009. if (saveChanged)
  1010. {
  1011. // We cannot use AbsolutePosition here because that would
  1012. // attempt to cross the prim as it is detached
  1013. so.ForEachPart(x => { x.GroupPosition = so.RootPart.AttachedPos; });
  1014. UpdateKnownItem(sp, so, scriptedState);
  1015. }
  1016. // Now, remove the scripts
  1017. so.RemoveScriptInstances(true);
  1018. so.Clear();
  1019. }
  1020. protected SceneObjectGroup RezSingleAttachmentFromInventoryInternal(
  1021. IScenePresence sp, UUID itemID, UUID assetID, uint attachmentPt, bool append, XmlDocument doc)
  1022. {
  1023. if (m_invAccessModule == null)
  1024. return null;
  1025. SceneObjectGroup objatt;
  1026. UUID rezGroupID;
  1027. // This will fail if the user aborts login. sp will exist
  1028. // but ControllintClient will be null.
  1029. try
  1030. {
  1031. rezGroupID = sp.ControllingClient.ActiveGroupId;
  1032. }
  1033. catch
  1034. {
  1035. return null;
  1036. }
  1037. if (itemID != UUID.Zero)
  1038. objatt = m_invAccessModule.RezObject(sp.ControllingClient,
  1039. itemID, rezGroupID, Vector3.Zero, Vector3.Zero, UUID.Zero, (byte)1, true,
  1040. false, false, sp.UUID, true);
  1041. else
  1042. objatt = m_invAccessModule.RezObject(sp.ControllingClient,
  1043. null, rezGroupID, assetID, Vector3.Zero, Vector3.Zero, UUID.Zero, (byte)1, true,
  1044. false, false, sp.UUID, true);
  1045. if (objatt == null)
  1046. {
  1047. m_log.WarnFormat(
  1048. "[ATTACHMENTS MODULE]: did not attached item {0} to avatar {1} at point {2}",
  1049. itemID, sp.Name, attachmentPt);
  1050. return null;
  1051. }
  1052. else if (itemID == UUID.Zero)
  1053. {
  1054. // We need to have a FromItemID for multiple attachments on a single attach point to appear. This is
  1055. // true on Singularity 1.8.5 and quite possibly other viewers as well. As NPCs don't have an inventory
  1056. // we will satisfy this requirement by inserting a random UUID.
  1057. objatt.FromItemID = UUID.Random();
  1058. }
  1059. if (DebugLevel > 0)
  1060. m_log.DebugFormat(
  1061. "[ATTACHMENTS MODULE]: Rezzed single object {0} with {1} prims for attachment to {2} on point {3} in {4}",
  1062. objatt.Name, objatt.PrimCount, sp.Name, attachmentPt, m_scene.Name);
  1063. // HasGroupChanged is being set from within RezObject. Ideally it would be set by the caller.
  1064. objatt.HasGroupChanged = false;
  1065. Vector3 lastPos = objatt.RootPart.OffsetPosition;
  1066. Vector3 lastAttPos = objatt.RootPart.AttachedPos;
  1067. uint lastattPoint = objatt.AttachmentPoint;
  1068. bool doneAttach = false;
  1069. // FIXME: Detect whether it's really likely for AttachObject to throw an exception in the normal
  1070. // course of events. If not, then it's probably not worth trying to recover the situation
  1071. // since this is more likely to trigger further exceptions and confuse later debugging. If
  1072. // exceptions can be thrown in expected error conditions (not NREs) then make this consistent
  1073. // since other normal error conditions will simply return false instead.
  1074. // This will throw if the attachment fails
  1075. try
  1076. {
  1077. if (doc != null)
  1078. {
  1079. objatt.LoadScriptState(doc);
  1080. objatt.ResetOwnerChangeFlag();
  1081. }
  1082. doneAttach = AttachObjectInternal(sp, objatt, attachmentPt, false, true, true, append);
  1083. }
  1084. catch (Exception e)
  1085. {
  1086. m_log.ErrorFormat(
  1087. "[ATTACHMENTS MODULE]: Failed to attach {0} {1} for {2}, exception {3}{4}",
  1088. objatt.Name, objatt.UUID, sp.Name, e.Message, e.StackTrace);
  1089. doneAttach = false;
  1090. }
  1091. if(!doneAttach)
  1092. {
  1093. // Make sure the object doesn't stick around and bail
  1094. sp.RemoveAttachment(objatt);
  1095. m_scene.DeleteSceneObject(objatt, false);
  1096. return null;
  1097. }
  1098. if (lastattPoint != objatt.AttachmentPoint ||
  1099. lastPos != objatt.RootPart.OffsetPosition ||
  1100. lastAttPos != objatt.RootPart.AttachedPos)
  1101. objatt.HasGroupChanged = true;
  1102. return objatt;
  1103. }
  1104. /// <summary>
  1105. /// Update the user inventory to reflect an attachment
  1106. /// </summary>
  1107. /// <param name="sp"></param>
  1108. /// <param name="AttachmentPt"></param>
  1109. /// <param name="itemID"></param>
  1110. /// <param name="att"></param>
  1111. private void ShowAttachInUserInventory(IScenePresence sp, uint AttachmentPt, UUID itemID, SceneObjectGroup att, bool append)
  1112. {
  1113. // m_log.DebugFormat(
  1114. // "[USER INVENTORY]: Updating attachment {0} for {1} at {2} using item ID {3}",
  1115. // att.Name, sp.Name, AttachmentPt, itemID);
  1116. if (UUID.Zero == itemID)
  1117. {
  1118. m_log.Error("[ATTACHMENTS MODULE]: Unable to save attachment. Error inventory item ID.");
  1119. return;
  1120. }
  1121. if (0 == AttachmentPt)
  1122. {
  1123. m_log.Error("[ATTACHMENTS MODULE]: Unable to save attachment. Error attachment point.");
  1124. return;
  1125. }
  1126. InventoryItemBase item = m_scene.InventoryService.GetItem(sp.UUID, itemID);
  1127. if (item == null)
  1128. return;
  1129. int attFlag = append ? 0x80 : 0;
  1130. bool changed = sp.Appearance.SetAttachment((int)AttachmentPt | attFlag, itemID, item.AssetID);
  1131. if (changed && m_scene.AvatarFactory != null)
  1132. {
  1133. if (DebugLevel > 0)
  1134. m_log.DebugFormat(
  1135. "[ATTACHMENTS MODULE]: Queueing appearance save for {0}, attachment {1} point {2} in ShowAttachInUserInventory()",
  1136. sp.Name, att.Name, AttachmentPt);
  1137. m_scene.AvatarFactory.QueueAppearanceSave(sp.UUID);
  1138. }
  1139. }
  1140. #endregion
  1141. #region Client Event Handlers
  1142. private ISceneEntity Client_OnRezSingleAttachmentFromInv(IClientAPI remoteClient, UUID itemID, uint AttachmentPt)
  1143. {
  1144. if (!Enabled)
  1145. return null;
  1146. if (DebugLevel > 0)
  1147. m_log.DebugFormat(
  1148. "[ATTACHMENTS MODULE]: Rezzing attachment to point {0} from item {1} for {2}",
  1149. (AttachmentPoint)AttachmentPt, itemID, remoteClient.Name);
  1150. ScenePresence sp = m_scene.GetScenePresence(remoteClient.AgentId);
  1151. if (sp == null)
  1152. {
  1153. m_log.ErrorFormat(
  1154. "[ATTACHMENTS MODULE]: Could not find presence for client {0} {1} in RezSingleAttachmentFromInventory()",
  1155. remoteClient.Name, remoteClient.AgentId);
  1156. return null;
  1157. }
  1158. return RezSingleAttachmentFromInventory(sp, itemID, AttachmentPt);
  1159. }
  1160. private void Client_OnRezMultipleAttachmentsFromInv(IClientAPI remoteClient, List<KeyValuePair<UUID, uint>> rezlist)
  1161. {
  1162. if (!Enabled)
  1163. return;
  1164. ScenePresence sp = m_scene.GetScenePresence(remoteClient.AgentId);
  1165. if (sp != null)
  1166. RezMultipleAttachmentsFromInventory(sp, rezlist);
  1167. else
  1168. m_log.ErrorFormat(
  1169. "[ATTACHMENTS MODULE]: Could not find presence for client {0} {1} in RezMultipleAttachmentsFromInventory()",
  1170. remoteClient.Name, remoteClient.AgentId);
  1171. }
  1172. private void Client_OnObjectAttach(IClientAPI remoteClient, uint objectLocalID, uint AttachmentPt, bool silent)
  1173. {
  1174. if (DebugLevel > 0)
  1175. m_log.DebugFormat(
  1176. "[ATTACHMENTS MODULE]: Attaching object local id {0} to {1} point {2} from ground (silent = {3})",
  1177. objectLocalID, remoteClient.Name, AttachmentPt, silent);
  1178. if (!Enabled)
  1179. return;
  1180. try
  1181. {
  1182. ScenePresence sp = m_scene.GetScenePresence(remoteClient.AgentId);
  1183. if (sp == null)
  1184. {
  1185. m_log.ErrorFormat(
  1186. "[ATTACHMENTS MODULE]: Could not find presence for client {0} {1}", remoteClient.Name, remoteClient.AgentId);
  1187. return;
  1188. }
  1189. // If we can't take it, we can't attach it!
  1190. SceneObjectPart part = m_scene.GetSceneObjectPart(objectLocalID);
  1191. if (part == null)
  1192. return;
  1193. SceneObjectGroup group = part.ParentGroup;
  1194. if (!m_scene.Permissions.CanTakeObject(group, sp))
  1195. {
  1196. remoteClient.SendAgentAlertMessage(
  1197. "You don't have sufficient permissions to attach this object", false);
  1198. return;
  1199. }
  1200. bool append = (AttachmentPt & 0x80) != 0;
  1201. AttachmentPt &= 0x7f;
  1202. // Calls attach with a Zero position
  1203. if (AttachObject(sp, group , AttachmentPt, false, true, append))
  1204. {
  1205. if (DebugLevel > 0)
  1206. m_log.Debug(
  1207. "[ATTACHMENTS MODULE]: Saving avatar attachment. AgentID: " + remoteClient.AgentId
  1208. + ", AttachmentPoint: " + AttachmentPt);
  1209. // Save avatar attachment information
  1210. m_scene.AvatarFactory.QueueAppearanceSave(sp.UUID);
  1211. }
  1212. }
  1213. catch (Exception e)
  1214. {
  1215. m_log.ErrorFormat("[ATTACHMENTS MODULE]: exception upon Attach Object {0}{1}", e.Message, e.StackTrace);
  1216. }
  1217. }
  1218. private void Client_OnObjectDetach(uint objectLocalID, IClientAPI remoteClient)
  1219. {
  1220. if (!Enabled)
  1221. return;
  1222. ScenePresence sp = m_scene.GetScenePresence(remoteClient.AgentId);
  1223. SceneObjectGroup group = m_scene.GetGroupByPrim(objectLocalID);
  1224. if (sp != null && group != null)
  1225. DetachSingleAttachmentToInv(sp, group);
  1226. }
  1227. private void Client_OnDetachAttachmentIntoInv(UUID itemID, IClientAPI remoteClient)
  1228. {
  1229. if (!Enabled)
  1230. return;
  1231. ScenePresence sp = m_scene.GetScenePresence(remoteClient.AgentId);
  1232. if (sp != null)
  1233. {
  1234. List<SceneObjectGroup> attachments = sp.GetAttachments();
  1235. foreach (SceneObjectGroup group in attachments)
  1236. {
  1237. if (group.FromItemID == itemID && group.FromItemID != UUID.Zero)
  1238. {
  1239. DetachSingleAttachmentToInv(sp, group);
  1240. return;
  1241. }
  1242. }
  1243. }
  1244. }
  1245. private void Client_OnObjectDrop(uint soLocalId, IClientAPI remoteClient)
  1246. {
  1247. if (!Enabled)
  1248. return;
  1249. ScenePresence sp = m_scene.GetScenePresence(remoteClient.AgentId);
  1250. if (sp != null)
  1251. DetachSingleAttachmentToGround(sp, soLocalId);
  1252. }
  1253. #endregion
  1254. }
  1255. }