AttachmentsModule.cs 60 KB

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