AttachmentsModule.cs 60 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496
  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.ResumeScripts();
  707. so.HasGroupChanged = true;
  708. so.RootPart.ScheduleFullUpdate();
  709. so.ScheduleGroupForTerseUpdate();
  710. }
  711. public void DetachSingleAttachmentToInv(IScenePresence sp, SceneObjectGroup so)
  712. {
  713. if (so.AttachedAvatar != sp.UUID)
  714. {
  715. m_log.WarnFormat(
  716. "[ATTACHMENTS MODULE]: Tried to detach object {0} from {1} {2} but attached avatar id was {3} in {4}",
  717. so.Name, sp.Name, sp.UUID, so.AttachedAvatar, m_scene.RegionInfo.RegionName);
  718. return;
  719. }
  720. // If this didn't come from inventory, it also shouldn't go there
  721. // on detach. It's likely a temp attachment.
  722. if (so.FromItemID == UUID.Zero)
  723. {
  724. // Retirn value is ignored
  725. PrepareScriptInstanceForSave(so, true);
  726. lock (sp.AttachmentsSyncLock)
  727. {
  728. bool changed = sp.Appearance.DetachAttachment(so.FromItemID);
  729. if (changed && m_scene.AvatarFactory != null)
  730. m_scene.AvatarFactory.QueueAppearanceSave(sp.UUID);
  731. sp.RemoveAttachment(so);
  732. }
  733. m_scene.DeleteSceneObject(so, false, false);
  734. so.RemoveScriptInstances(true);
  735. so.Clear();
  736. return;
  737. }
  738. if (DebugLevel > 0)
  739. m_log.DebugFormat(
  740. "[ATTACHMENTS MODULE]: Detaching object {0} {1} (FromItemID {2}) for {3} in {4}",
  741. so.Name, so.LocalId, so.FromItemID, sp.Name, m_scene.Name);
  742. // Scripts MUST be snapshotted before the object is
  743. // removed from the scene because doing otherwise will
  744. // clobber the run flag
  745. // This must be done outside the sp.AttachmentSyncLock so that there is no risk of a deadlock from
  746. // scripts performing attachment operations at the same time. Getting object states stops the scripts.
  747. string scriptedState = PrepareScriptInstanceForSave(so, true);
  748. lock (sp.AttachmentsSyncLock)
  749. {
  750. // Save avatar attachment information
  751. // m_log.Debug("[ATTACHMENTS MODULE]: Detaching from UserID: " + sp.UUID + ", ItemID: " + itemID);
  752. bool changed = sp.Appearance.DetachAttachment(so.FromItemID);
  753. if (changed && m_scene.AvatarFactory != null)
  754. m_scene.AvatarFactory.QueueAppearanceSave(sp.UUID);
  755. sp.RemoveAttachment(so);
  756. UpdateDetachedObject(sp, so, scriptedState);
  757. }
  758. }
  759. public void UpdateAttachmentPosition(SceneObjectGroup sog, Vector3 pos)
  760. {
  761. if (!Enabled)
  762. return;
  763. sog.UpdateGroupPosition(pos);
  764. sog.HasGroupChanged = true;
  765. }
  766. #endregion
  767. #region AttachmentModule private methods
  768. // This is public but is not part of the IAttachmentsModule interface.
  769. // RegionCombiner module needs to poke at it to deliver client events.
  770. // This breaks the encapsulation of the module and should get fixed somehow.
  771. public void SubscribeToClientEvents(IClientAPI client)
  772. {
  773. client.OnRezSingleAttachmentFromInv += Client_OnRezSingleAttachmentFromInv;
  774. client.OnRezMultipleAttachmentsFromInv += Client_OnRezMultipleAttachmentsFromInv;
  775. client.OnObjectAttach += Client_OnObjectAttach;
  776. client.OnObjectDetach += Client_OnObjectDetach;
  777. client.OnDetachAttachmentIntoInv += Client_OnDetachAttachmentIntoInv;
  778. client.OnObjectDrop += Client_OnObjectDrop;
  779. }
  780. // This is public but is not part of the IAttachmentsModule interface.
  781. // RegionCombiner module needs to poke at it to deliver client events.
  782. // This breaks the encapsulation of the module and should get fixed somehow.
  783. public void UnsubscribeFromClientEvents(IClientAPI client)
  784. {
  785. client.OnRezSingleAttachmentFromInv -= Client_OnRezSingleAttachmentFromInv;
  786. client.OnRezMultipleAttachmentsFromInv -= Client_OnRezMultipleAttachmentsFromInv;
  787. client.OnObjectAttach -= Client_OnObjectAttach;
  788. client.OnObjectDetach -= Client_OnObjectDetach;
  789. client.OnDetachAttachmentIntoInv -= Client_OnDetachAttachmentIntoInv;
  790. client.OnObjectDrop -= Client_OnObjectDrop;
  791. }
  792. /// <summary>
  793. /// Update the attachment asset for the new sog details if they have changed.
  794. /// </summary>
  795. /// <remarks>
  796. /// This is essential for preserving attachment attributes such as permission. Unlike normal scene objects,
  797. /// these details are not stored on the region.
  798. /// </remarks>
  799. /// <param name="sp"></param>
  800. /// <param name="grp"></param>
  801. /// <param name="saveAllScripted"></param>
  802. private void UpdateKnownItem(IScenePresence sp, SceneObjectGroup grp, string scriptedState)
  803. {
  804. if (grp.FromItemID == UUID.Zero)
  805. {
  806. // We can't save temp attachments
  807. grp.HasGroupChanged = false;
  808. return;
  809. }
  810. if(sp.IsNPC)
  811. return;
  812. if (grp.HasGroupChanged)
  813. {
  814. m_log.DebugFormat(
  815. "[ATTACHMENTS MODULE]: Updating asset for attachment {0}, attachpoint {1}",
  816. grp.UUID, grp.AttachmentPoint);
  817. string sceneObjectXml = SceneObjectSerializer.ToOriginalXmlFormat(grp, scriptedState);
  818. InventoryItemBase item = m_scene.InventoryService.GetItem(sp.UUID, grp.FromItemID);
  819. if (item != null)
  820. {
  821. // attach is rez, need to update permissions
  822. item.Flags &= ~(uint)(InventoryItemFlags.ObjectSlamPerm | InventoryItemFlags.ObjectOverwriteBase |
  823. InventoryItemFlags.ObjectOverwriteOwner | InventoryItemFlags.ObjectOverwriteGroup |
  824. InventoryItemFlags.ObjectOverwriteEveryone | InventoryItemFlags.ObjectOverwriteNextOwner);
  825. uint permsBase = (uint)(PermissionMask.Copy | PermissionMask.Transfer |
  826. PermissionMask.Modify | PermissionMask.Move |
  827. PermissionMask.Export | PermissionMask.FoldedMask);
  828. permsBase &= grp.CurrentAndFoldedNextPermissions();
  829. permsBase |= (uint)PermissionMask.Move;
  830. item.BasePermissions = permsBase;
  831. item.CurrentPermissions = permsBase;
  832. item.NextPermissions = permsBase & grp.RootPart.NextOwnerMask | (uint)PermissionMask.Move;
  833. item.EveryOnePermissions = permsBase & grp.RootPart.EveryoneMask;
  834. item.GroupPermissions = permsBase & grp.RootPart.GroupMask;
  835. item.CurrentPermissions &=
  836. ((uint)PermissionMask.Copy |
  837. (uint)PermissionMask.Transfer |
  838. (uint)PermissionMask.Modify |
  839. (uint)PermissionMask.Move |
  840. (uint)PermissionMask.Export |
  841. (uint)PermissionMask.FoldedMask); // Preserve folded permissions ??
  842. string name = grp.RootPart.Name;
  843. string desc = grp.RootPart.Description;
  844. AssetBase asset = m_scene.CreateAsset(
  845. name, desc,
  846. (sbyte)AssetType.Object,
  847. Utils.StringToBytes(sceneObjectXml),
  848. sp.UUID);
  849. item.Name = name;
  850. item.Description = desc;
  851. item.AssetID = asset.FullID;
  852. item.AssetType = (int)AssetType.Object;
  853. item.InvType = (int)InventoryType.Object;
  854. if (m_invAccessModule != null)
  855. m_invAccessModule.UpdateInventoryItemAsset(sp.UUID, item, asset);
  856. if (sp.ControllingClient != null)
  857. sp.ControllingClient.SendInventoryItemCreateUpdate(item, 0);
  858. }
  859. grp.HasGroupChanged = false; // Prevent it being saved over and over
  860. }
  861. else if (DebugLevel > 0)
  862. {
  863. m_log.DebugFormat(
  864. "[ATTACHMENTS MODULE]: Don't need to update asset for unchanged attachment {0}, attachpoint {1}",
  865. grp.UUID, grp.AttachmentPoint);
  866. }
  867. }
  868. /// <summary>
  869. /// Attach this scene object to the given avatar.
  870. /// </summary>
  871. /// <remarks>
  872. /// This isn't publicly available since attachments should always perform the corresponding inventory
  873. /// operation (to show the attach in user inventory and update the asset with positional information).
  874. /// </remarks>
  875. /// <param name="sp"></param>
  876. /// <param name="so"></param>
  877. /// <param name="attachmentpoint"></param>
  878. /// <param name="attachOffset"></param>
  879. /// <param name="silent"></param>
  880. private void AttachToAgent(
  881. IScenePresence sp, SceneObjectGroup so, uint attachmentpoint, Vector3 attachOffset, bool silent)
  882. {
  883. if (DebugLevel > 0)
  884. m_log.DebugFormat(
  885. "[ATTACHMENTS MODULE]: Adding attachment {0} to avatar {1} at pt {2} pos {3} {4} in {5}",
  886. so.Name, sp.Name, attachmentpoint, attachOffset, so.RootPart.AttachedPos, m_scene.Name);
  887. // Remove from database and parcel prim count
  888. m_scene.DeleteFromStorage(so.UUID);
  889. m_scene.EventManager.TriggerParcelPrimCountTainted();
  890. foreach (SceneObjectPart part in so.Parts)
  891. {
  892. // if (part.KeyframeMotion != null)
  893. // part.KeyframeMotion.Suspend();
  894. if (part.PhysActor != null)
  895. {
  896. part.RemoveFromPhysics();
  897. }
  898. }
  899. so.RootPart.SetParentLocalId(sp.LocalId);
  900. so.AttachedAvatar = sp.UUID;
  901. so.AttachmentPoint = attachmentpoint;
  902. so.RootPart.AttachedPos = attachOffset;
  903. so.AbsolutePosition = attachOffset;
  904. so.IsAttachment = true;
  905. sp.AddAttachment(so);
  906. if (!silent)
  907. {
  908. if (so.HasPrivateAttachmentPoint)
  909. {
  910. if (DebugLevel > 0)
  911. m_log.DebugFormat(
  912. "[ATTACHMENTS MODULE]: Killing private HUD {0} for avatars other than {1} at attachment point {2}",
  913. so.Name, sp.Name, so.AttachmentPoint);
  914. // As this scene object can now only be seen by the attaching avatar, tell everybody else in the
  915. // scene that it's no longer in their awareness.
  916. m_scene.ForEachClient(
  917. client =>
  918. { if (client.AgentId != so.AttachedAvatar)
  919. client.SendKillObject(new List<uint>() { so.LocalId });
  920. });
  921. }
  922. // Fudge below is an extremely unhelpful comment. It's probably here so that the scheduled full update
  923. // will succeed, as that will not update if an attachment is selected.
  924. so.IsSelected = false; // fudge....
  925. so.ScheduleGroupForFullAnimUpdate();
  926. }
  927. // In case it is later dropped again, don't let
  928. // it get cleaned up
  929. so.RootPart.RemFlag(PrimFlags.TemporaryOnRez);
  930. }
  931. /// <summary>
  932. /// Add a scene object as a new attachment in the user inventory.
  933. /// </summary>
  934. /// <param name="remoteClient"></param>
  935. /// <param name="grp"></param>
  936. /// <returns>The user inventory item created that holds the attachment.</returns>
  937. private InventoryItemBase AddSceneObjectAsNewAttachmentInInv(IScenePresence sp, SceneObjectGroup grp)
  938. {
  939. if (m_invAccessModule == null)
  940. return null;
  941. if (DebugLevel > 0)
  942. m_log.DebugFormat(
  943. "[ATTACHMENTS MODULE]: Called AddSceneObjectAsAttachment for object {0} {1} for {2}",
  944. grp.Name, grp.LocalId, sp.Name);
  945. InventoryItemBase newItem
  946. = m_invAccessModule.CopyToInventory(
  947. DeRezAction.TakeCopy,
  948. m_scene.InventoryService.GetFolderForType(sp.UUID, FolderType.Object).ID,
  949. new List<SceneObjectGroup> { grp },
  950. sp.ControllingClient, true)[0];
  951. // sets itemID so client can show item as 'attached' in inventory
  952. grp.FromItemID = newItem.ID;
  953. return newItem;
  954. }
  955. /// <summary>
  956. /// Prepares the script instance for save.
  957. /// </summary>
  958. /// <remarks>
  959. /// This involves triggering the detach event and getting the script state (which also stops the script)
  960. /// This MUST be done outside sp.AttachmentsSyncLock, since otherwise there is a chance of deadlock if a
  961. /// running script is performing attachment operations.
  962. /// </remarks>
  963. /// <returns>
  964. /// The script state ready for persistence.
  965. /// </returns>
  966. /// <param name='grp'>
  967. /// </param>
  968. /// <param name='fireDetachEvent'>
  969. /// If true, then fire the script event before we save its state.
  970. /// </param>
  971. private string PrepareScriptInstanceForSave(SceneObjectGroup grp, bool fireDetachEvent)
  972. {
  973. if (fireDetachEvent)
  974. {
  975. m_scene.EventManager.TriggerOnAttach(grp.LocalId, grp.FromItemID, UUID.Zero);
  976. // Allow detach event time to do some work before stopping the script
  977. Thread.Sleep(2);
  978. }
  979. using (StringWriter sw = new StringWriter())
  980. {
  981. using (XmlTextWriter writer = new XmlTextWriter(sw))
  982. {
  983. grp.SaveScriptedState(writer);
  984. }
  985. return sw.ToString();
  986. }
  987. }
  988. private void UpdateDetachedObject(IScenePresence sp, SceneObjectGroup so, string scriptedState)
  989. {
  990. // Don't save attachments for HG visitors, it
  991. // messes up their inventory. When a HG visitor logs
  992. // out on a foreign grid, their attachments will be
  993. // reloaded in the state they were in when they left
  994. // the home grid. This is best anyway as the visited
  995. // grid may use an incompatible script engine.
  996. bool saveChanged
  997. = sp.PresenceType != PresenceType.Npc
  998. && (m_scene.UserManagementModule == null
  999. || m_scene.UserManagementModule.IsLocalGridUser(sp.UUID));
  1000. // Remove the object from the scene so no more updates
  1001. // are sent. Doing this before the below changes will ensure
  1002. // updates can't cause "HUD artefacts"
  1003. m_scene.DeleteSceneObject(so, false, false);
  1004. // Prepare sog for storage
  1005. so.AttachedAvatar = UUID.Zero;
  1006. so.RootPart.SetParentLocalId(0);
  1007. so.IsAttachment = false;
  1008. if (saveChanged)
  1009. {
  1010. // We cannot use AbsolutePosition here because that would
  1011. // attempt to cross the prim as it is detached
  1012. so.ForEachPart(x => { x.GroupPosition = so.RootPart.AttachedPos; });
  1013. UpdateKnownItem(sp, so, scriptedState);
  1014. }
  1015. // Now, remove the scripts
  1016. so.RemoveScriptInstances(true);
  1017. so.Clear();
  1018. }
  1019. protected SceneObjectGroup RezSingleAttachmentFromInventoryInternal(
  1020. IScenePresence sp, UUID itemID, UUID assetID, uint attachmentPt, bool append, XmlDocument doc)
  1021. {
  1022. if (m_invAccessModule == null)
  1023. return null;
  1024. SceneObjectGroup objatt;
  1025. UUID rezGroupID;
  1026. // This will fail if the user aborts login. sp will exist
  1027. // but ControllintClient will be null.
  1028. try
  1029. {
  1030. rezGroupID = sp.ControllingClient.ActiveGroupId;
  1031. }
  1032. catch
  1033. {
  1034. return null;
  1035. }
  1036. if (itemID != UUID.Zero)
  1037. objatt = m_invAccessModule.RezObject(sp.ControllingClient,
  1038. itemID, rezGroupID, Vector3.Zero, Vector3.Zero, UUID.Zero, (byte)1, true,
  1039. false, false, sp.UUID, true);
  1040. else
  1041. objatt = m_invAccessModule.RezObject(sp.ControllingClient,
  1042. null, rezGroupID, assetID, Vector3.Zero, Vector3.Zero, UUID.Zero, (byte)1, true,
  1043. false, false, sp.UUID, true);
  1044. if (objatt == null)
  1045. {
  1046. m_log.WarnFormat(
  1047. "[ATTACHMENTS MODULE]: did not attached item {0} to avatar {1} at point {2}",
  1048. itemID, sp.Name, attachmentPt);
  1049. return null;
  1050. }
  1051. else if (itemID == UUID.Zero)
  1052. {
  1053. // We need to have a FromItemID for multiple attachments on a single attach point to appear. This is
  1054. // true on Singularity 1.8.5 and quite possibly other viewers as well. As NPCs don't have an inventory
  1055. // we will satisfy this requirement by inserting a random UUID.
  1056. objatt.FromItemID = UUID.Random();
  1057. }
  1058. if (DebugLevel > 0)
  1059. m_log.DebugFormat(
  1060. "[ATTACHMENTS MODULE]: Rezzed single object {0} with {1} prims for attachment to {2} on point {3} in {4}",
  1061. objatt.Name, objatt.PrimCount, sp.Name, attachmentPt, m_scene.Name);
  1062. // HasGroupChanged is being set from within RezObject. Ideally it would be set by the caller.
  1063. objatt.HasGroupChanged = false;
  1064. Vector3 lastPos = objatt.RootPart.OffsetPosition;
  1065. Vector3 lastAttPos = objatt.RootPart.AttachedPos;
  1066. bool doneAttach = false;
  1067. // FIXME: Detect whether it's really likely for AttachObject to throw an exception in the normal
  1068. // course of events. If not, then it's probably not worth trying to recover the situation
  1069. // since this is more likely to trigger further exceptions and confuse later debugging. If
  1070. // exceptions can be thrown in expected error conditions (not NREs) then make this consistent
  1071. // since other normal error conditions will simply return false instead.
  1072. // This will throw if the attachment fails
  1073. try
  1074. {
  1075. if (doc != null)
  1076. {
  1077. objatt.LoadScriptState(doc);
  1078. objatt.ResetOwnerChangeFlag();
  1079. }
  1080. doneAttach = AttachObjectInternal(sp, objatt, attachmentPt, false, true, true, append);
  1081. }
  1082. catch (Exception e)
  1083. {
  1084. m_log.ErrorFormat(
  1085. "[ATTACHMENTS MODULE]: Failed to attach {0} {1} for {2}, exception {3}{4}",
  1086. objatt.Name, objatt.UUID, sp.Name, e.Message, e.StackTrace);
  1087. doneAttach = false;
  1088. }
  1089. if(!doneAttach)
  1090. {
  1091. // Make sure the object doesn't stick around and bail
  1092. sp.RemoveAttachment(objatt);
  1093. m_scene.DeleteSceneObject(objatt, false);
  1094. return null;
  1095. }
  1096. if ((attachmentPt != 0 && attachmentPt != objatt.AttachmentPoint) ||
  1097. lastPos != objatt.RootPart.OffsetPosition ||
  1098. lastAttPos != objatt.RootPart.AttachedPos)
  1099. objatt.HasGroupChanged = true;
  1100. return objatt;
  1101. }
  1102. /// <summary>
  1103. /// Update the user inventory to reflect an attachment
  1104. /// </summary>
  1105. /// <param name="sp"></param>
  1106. /// <param name="AttachmentPt"></param>
  1107. /// <param name="itemID"></param>
  1108. /// <param name="att"></param>
  1109. private void ShowAttachInUserInventory(IScenePresence sp, uint AttachmentPt, UUID itemID, SceneObjectGroup att, bool append)
  1110. {
  1111. // m_log.DebugFormat(
  1112. // "[USER INVENTORY]: Updating attachment {0} for {1} at {2} using item ID {3}",
  1113. // att.Name, sp.Name, AttachmentPt, itemID);
  1114. if (UUID.Zero == itemID)
  1115. {
  1116. m_log.Error("[ATTACHMENTS MODULE]: Unable to save attachment. Error inventory item ID.");
  1117. return;
  1118. }
  1119. if (0 == AttachmentPt)
  1120. {
  1121. m_log.Error("[ATTACHMENTS MODULE]: Unable to save attachment. Error attachment point.");
  1122. return;
  1123. }
  1124. InventoryItemBase item = m_scene.InventoryService.GetItem(sp.UUID, itemID);
  1125. if (item == null)
  1126. return;
  1127. int attFlag = append ? 0x80 : 0;
  1128. bool changed = sp.Appearance.SetAttachment((int)AttachmentPt | attFlag, itemID, item.AssetID);
  1129. if (changed && m_scene.AvatarFactory != null)
  1130. {
  1131. if (DebugLevel > 0)
  1132. m_log.DebugFormat(
  1133. "[ATTACHMENTS MODULE]: Queueing appearance save for {0}, attachment {1} point {2} in ShowAttachInUserInventory()",
  1134. sp.Name, att.Name, AttachmentPt);
  1135. m_scene.AvatarFactory.QueueAppearanceSave(sp.UUID);
  1136. }
  1137. }
  1138. #endregion
  1139. #region Client Event Handlers
  1140. private ISceneEntity Client_OnRezSingleAttachmentFromInv(IClientAPI remoteClient, UUID itemID, uint AttachmentPt)
  1141. {
  1142. if (!Enabled)
  1143. return null;
  1144. if (DebugLevel > 0)
  1145. m_log.DebugFormat(
  1146. "[ATTACHMENTS MODULE]: Rezzing attachment to point {0} from item {1} for {2}",
  1147. (AttachmentPoint)AttachmentPt, itemID, remoteClient.Name);
  1148. ScenePresence sp = m_scene.GetScenePresence(remoteClient.AgentId);
  1149. if (sp == null)
  1150. {
  1151. m_log.ErrorFormat(
  1152. "[ATTACHMENTS MODULE]: Could not find presence for client {0} {1} in RezSingleAttachmentFromInventory()",
  1153. remoteClient.Name, remoteClient.AgentId);
  1154. return null;
  1155. }
  1156. return RezSingleAttachmentFromInventory(sp, itemID, AttachmentPt);
  1157. }
  1158. private void Client_OnRezMultipleAttachmentsFromInv(IClientAPI remoteClient, List<KeyValuePair<UUID, uint>> rezlist)
  1159. {
  1160. if (!Enabled)
  1161. return;
  1162. ScenePresence sp = m_scene.GetScenePresence(remoteClient.AgentId);
  1163. if (sp != null)
  1164. RezMultipleAttachmentsFromInventory(sp, rezlist);
  1165. else
  1166. m_log.ErrorFormat(
  1167. "[ATTACHMENTS MODULE]: Could not find presence for client {0} {1} in RezMultipleAttachmentsFromInventory()",
  1168. remoteClient.Name, remoteClient.AgentId);
  1169. }
  1170. private void Client_OnObjectAttach(IClientAPI remoteClient, uint objectLocalID, uint AttachmentPt, bool silent)
  1171. {
  1172. if (DebugLevel > 0)
  1173. m_log.DebugFormat(
  1174. "[ATTACHMENTS MODULE]: Attaching object local id {0} to {1} point {2} from ground (silent = {3})",
  1175. objectLocalID, remoteClient.Name, AttachmentPt, silent);
  1176. if (!Enabled)
  1177. return;
  1178. try
  1179. {
  1180. ScenePresence sp = m_scene.GetScenePresence(remoteClient.AgentId);
  1181. if (sp == null)
  1182. {
  1183. m_log.ErrorFormat(
  1184. "[ATTACHMENTS MODULE]: Could not find presence for client {0} {1}", remoteClient.Name, remoteClient.AgentId);
  1185. return;
  1186. }
  1187. // If we can't take it, we can't attach it!
  1188. SceneObjectPart part = m_scene.GetSceneObjectPart(objectLocalID);
  1189. if (part == null)
  1190. return;
  1191. SceneObjectGroup group = part.ParentGroup;
  1192. if (!m_scene.Permissions.CanTakeObject(group, sp))
  1193. {
  1194. remoteClient.SendAgentAlertMessage(
  1195. "You don't have sufficient permissions to attach this object", false);
  1196. return;
  1197. }
  1198. bool append = (AttachmentPt & 0x80) != 0;
  1199. AttachmentPt &= 0x7f;
  1200. // Calls attach with a Zero position
  1201. if (AttachObject(sp, group , AttachmentPt, false, true, append))
  1202. {
  1203. if (DebugLevel > 0)
  1204. m_log.Debug(
  1205. "[ATTACHMENTS MODULE]: Saving avatar attachment. AgentID: " + remoteClient.AgentId
  1206. + ", AttachmentPoint: " + AttachmentPt);
  1207. // Save avatar attachment information
  1208. m_scene.AvatarFactory.QueueAppearanceSave(sp.UUID);
  1209. }
  1210. }
  1211. catch (Exception e)
  1212. {
  1213. m_log.ErrorFormat("[ATTACHMENTS MODULE]: exception upon Attach Object {0}{1}", e.Message, e.StackTrace);
  1214. }
  1215. }
  1216. private void Client_OnObjectDetach(uint objectLocalID, IClientAPI remoteClient)
  1217. {
  1218. if (!Enabled)
  1219. return;
  1220. ScenePresence sp = m_scene.GetScenePresence(remoteClient.AgentId);
  1221. SceneObjectGroup group = m_scene.GetGroupByPrim(objectLocalID);
  1222. if (sp != null && group != null)
  1223. DetachSingleAttachmentToInv(sp, group);
  1224. }
  1225. private void Client_OnDetachAttachmentIntoInv(UUID itemID, IClientAPI remoteClient)
  1226. {
  1227. if (!Enabled)
  1228. return;
  1229. ScenePresence sp = m_scene.GetScenePresence(remoteClient.AgentId);
  1230. if (sp != null)
  1231. {
  1232. List<SceneObjectGroup> attachments = sp.GetAttachments();
  1233. foreach (SceneObjectGroup group in attachments)
  1234. {
  1235. if (group.FromItemID == itemID && group.FromItemID != UUID.Zero)
  1236. {
  1237. DetachSingleAttachmentToInv(sp, group);
  1238. return;
  1239. }
  1240. }
  1241. }
  1242. }
  1243. private void Client_OnObjectDrop(uint soLocalId, IClientAPI remoteClient)
  1244. {
  1245. if (!Enabled)
  1246. return;
  1247. ScenePresence sp = m_scene.GetScenePresence(remoteClient.AgentId);
  1248. if (sp != null)
  1249. DetachSingleAttachmentToGround(sp, soLocalId);
  1250. }
  1251. #endregion
  1252. }
  1253. }