OpenSim
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros
AttachmentsModule.cs
Go to the documentation of this file.
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 
28 using System;
29 using System.Collections.Generic;
30 using System.Reflection;
31 using System.IO;
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.Region.Framework;
41 using OpenSim.Region.Framework.Interfaces;
42 using OpenSim.Region.Framework.Scenes;
43 using OpenSim.Region.Framework.Scenes.Serialization;
44 using OpenSim.Services.Interfaces;
45 
46 namespace OpenSim.Region.CoreModules.Avatar.Attachments
47 {
48  [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "AttachmentsModule")]
50  {
51  #region INonSharedRegionModule
52  private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
53 
54  public int DebugLevel { get; set; }
55 
63  public int ThrottlePer100PrimsRezzed { get; set; }
64 
65  private Scene m_scene;
66  private IInventoryAccessModule m_invAccessModule;
67 
71  public bool Enabled { get; private set; }
72 
73  public string Name { get { return "Attachments Module"; } }
74  public Type ReplaceableInterface { get { return null; } }
75 
76  public void Initialise(IConfigSource source)
77  {
78  IConfig config = source.Configs["Attachments"];
79  if (config != null)
80  {
81  Enabled = config.GetBoolean("Enabled", true);
82 
83  ThrottlePer100PrimsRezzed = config.GetInt("ThrottlePer100PrimsRezzed", 0);
84  }
85  else
86  {
87  Enabled = true;
88  }
89  }
90 
91  public void AddRegion(Scene scene)
92  {
93  m_scene = scene;
94  if (Enabled)
95  {
96  // Only register module with scene if it is enabled. All callers check for a null attachments module.
97  // Ideally, there should be a null attachments module for when this core attachments module has been
98  // disabled. Registering only when enabled allows for other attachments module implementations.
99  m_scene.RegisterModuleInterface<IAttachmentsModule>(this);
100  m_scene.EventManager.OnNewClient += SubscribeToClientEvents;
101  m_scene.EventManager.OnStartScript += (localID, itemID) => HandleScriptStateChange(localID, true);
102  m_scene.EventManager.OnStopScript += (localID, itemID) => HandleScriptStateChange(localID, false);
103 
104  MainConsole.Instance.Commands.AddCommand(
105  "Debug",
106  false,
107  "debug attachments log",
108  "debug attachments log [0|1]",
109  "Turn on attachments debug logging",
110  " <= 0 - turns off debug logging\n"
111  + " >= 1 - turns on attachment message debug logging",
112  HandleDebugAttachmentsLog);
113 
114  MainConsole.Instance.Commands.AddCommand(
115  "Debug",
116  false,
117  "debug attachments throttle",
118  "debug attachments throttle <ms>",
119  "Turn on attachments throttling.",
120  "This requires a millisecond value. " +
121  " == 0 - disable throttling.\n"
122  + " > 0 - sleeps for this number of milliseconds per 100 prims rezzed.",
123  HandleDebugAttachmentsThrottle);
124 
125  MainConsole.Instance.Commands.AddCommand(
126  "Debug",
127  false,
128  "debug attachments status",
129  "debug attachments status",
130  "Show current attachments debug status",
131  HandleDebugAttachmentsStatus);
132  }
133 
134  // TODO: Should probably be subscribing to CloseClient too, but this doesn't yet give us IClientAPI
135  }
136 
137  private void HandleDebugAttachmentsLog(string module, string[] args)
138  {
139  int debugLevel;
140 
141  if (!(args.Length == 4 && int.TryParse(args[3], out debugLevel)))
142  {
143  MainConsole.Instance.OutputFormat("Usage: debug attachments log [0|1]");
144  }
145  else
146  {
147  DebugLevel = debugLevel;
148  MainConsole.Instance.OutputFormat(
149  "Set attachments debug level to {0} in {1}", DebugLevel, m_scene.Name);
150  }
151  }
152 
153  private void HandleDebugAttachmentsThrottle(string module, string[] args)
154  {
155  int ms;
156 
157  if (args.Length == 4 && int.TryParse(args[3], out ms))
158  {
159  ThrottlePer100PrimsRezzed = ms;
160  MainConsole.Instance.OutputFormat(
161  "Attachments rez throttle per 100 prims is now {0} in {1}", ThrottlePer100PrimsRezzed, m_scene.Name);
162 
163  return;
164  }
165 
166  MainConsole.Instance.OutputFormat("Usage: debug attachments throttle <ms>");
167  }
168 
169  private void HandleDebugAttachmentsStatus(string module, string[] args)
170  {
171  MainConsole.Instance.OutputFormat("Settings for {0}", m_scene.Name);
172  MainConsole.Instance.OutputFormat("Debug logging level: {0}", DebugLevel);
173  MainConsole.Instance.OutputFormat("Throttle per 100 prims: {0}ms", ThrottlePer100PrimsRezzed);
174  }
175 
181  private void HandleScriptStateChange(uint localID, bool started)
182  {
183  SceneObjectGroup sog = m_scene.GetGroupByPrim(localID);
184  if (sog != null && sog.IsAttachment)
185  {
186  if (!started)
187  {
188  // FIXME: This is a convoluted way for working out whether the script state has changed to stop
189  // because it has been manually stopped or because the stop was called in UpdateDetachedObject() below
190  // This needs to be handled in a less tangled way.
191  ScenePresence sp = m_scene.GetScenePresence(sog.AttachedAvatar);
193  sog.HasGroupChanged = true;
194  }
195  else
196  {
197  sog.HasGroupChanged = true;
198  }
199  }
200  }
201 
202  public void RemoveRegion(Scene scene)
203  {
204  m_scene.UnregisterModuleInterface<IAttachmentsModule>(this);
205 
206  if (Enabled)
207  m_scene.EventManager.OnNewClient -= SubscribeToClientEvents;
208  }
209 
210  public void RegionLoaded(Scene scene)
211  {
212  m_invAccessModule = m_scene.RequestModuleInterface<IInventoryAccessModule>();
213  }
214 
215  public void Close()
216  {
217  RemoveRegion(m_scene);
218  }
219 
220  #endregion
221 
222  #region IAttachmentsModule
223 
225  {
226  lock (sp.AttachmentsSyncLock)
227  {
228  // Attachment objects
229  List<SceneObjectGroup> attachments = sp.GetAttachments();
230  if (attachments.Count > 0)
231  {
232  ad.AttachmentObjects = new List<ISceneObject>();
233  ad.AttachmentObjectStates = new List<string>();
234  // IScriptModule se = m_scene.RequestModuleInterface<IScriptModule>();
235  sp.InTransitScriptStates.Clear();
236 
237  foreach (SceneObjectGroup sog in attachments)
238  {
239  // We need to make a copy and pass that copy
240  // because of transfers withn the same sim
241  ISceneObject clone = sog.CloneForNewScene();
242  // Attachment module assumes that GroupPosition holds the offsets...!
243  ((SceneObjectGroup)clone).RootPart.GroupPosition = sog.RootPart.AttachedPos;
244  ((SceneObjectGroup)clone).IsAttachment = false;
245  ad.AttachmentObjects.Add(clone);
246  string state = sog.GetStateSnapshot();
247  ad.AttachmentObjectStates.Add(state);
248  sp.InTransitScriptStates.Add(state);
249 
250  // Scripts of the originals will be removed when the Agent is successfully removed.
251  // sog.RemoveScriptInstances(true);
252  }
253  }
254  }
255  }
256 
258  {
259 // m_log.DebugFormat("[ATTACHMENTS MODULE]: Copying attachment data into {0} in {1}", sp.Name, m_scene.Name);
260 
261  if (ad.AttachmentObjects != null && ad.AttachmentObjects.Count > 0)
262  {
263  lock (sp.AttachmentsSyncLock)
264  sp.ClearAttachments();
265 
266  int i = 0;
267  foreach (ISceneObject so in ad.AttachmentObjects)
268  {
269  ((SceneObjectGroup)so).LocalId = 0;
270  ((SceneObjectGroup)so).RootPart.ClearUpdateSchedule();
271 
272 // m_log.DebugFormat(
273 // "[ATTACHMENTS MODULE]: Copying script state with {0} bytes for object {1} for {2} in {3}",
274 // ad.AttachmentObjectStates[i].Length, so.Name, sp.Name, m_scene.Name);
275 
276  so.SetState(ad.AttachmentObjectStates[i++], m_scene);
277  m_scene.IncomingCreateObject(Vector3.Zero, so);
278  }
279  }
280  }
281 
283  {
284  if (!Enabled)
285  return;
286 
287  if (null == sp.Appearance)
288  {
289  m_log.WarnFormat("[ATTACHMENTS MODULE]: Appearance has not been initialized for agent {0}", sp.UUID);
290 
291  return;
292  }
293 
294  if (sp.GetAttachments().Count > 0)
295  {
296  if (DebugLevel > 0)
297  m_log.DebugFormat(
298  "[ATTACHMENTS MODULE]: Not doing simulator-side attachment rez for {0} in {1} as their viewer has already rezzed attachments",
299  m_scene.Name, sp.Name);
300 
301  return;
302  }
303 
304  if (DebugLevel > 0)
305  m_log.DebugFormat("[ATTACHMENTS MODULE]: Rezzing any attachments for {0} from simulator-side", sp.Name);
306 
307  XmlDocument doc = new XmlDocument();
308  string stateData = String.Empty;
309 
310  IAttachmentsService attServ = m_scene.RequestModuleInterface<IAttachmentsService>();
311  if (attServ != null)
312  {
313  m_log.DebugFormat("[ATTACHMENT]: Loading attachment data from attachment service");
314  stateData = attServ.Get(sp.UUID.ToString());
315  if (stateData != String.Empty)
316  {
317  try
318  {
319  doc.LoadXml(stateData);
320  }
321  catch { }
322  }
323  }
324 
325  Dictionary<UUID, string> itemData = new Dictionary<UUID, string>();
326 
327  XmlNodeList nodes = doc.GetElementsByTagName("Attachment");
328  if (nodes.Count > 0)
329  {
330  foreach (XmlNode n in nodes)
331  {
332  XmlElement elem = (XmlElement)n;
333  string itemID = elem.GetAttribute("ItemID");
334  string xml = elem.InnerXml;
335 
336  itemData[new UUID(itemID)] = xml;
337  }
338  }
339 
340 
341  List<AvatarAttachment> attachments = sp.Appearance.GetAttachments();
342 
343  // Let's get all items at once, so they get cached
344  UUID[] items = new UUID[attachments.Count];
345  int i = 0;
346  foreach (AvatarAttachment attach in attachments)
347  items[i++] = attach.ItemID;
348  m_scene.InventoryService.GetMultipleItems(sp.UUID, items);
349 
350  foreach (AvatarAttachment attach in attachments)
351  {
352  uint attachmentPt = (uint)attach.AttachPoint;
353 
354 // m_log.DebugFormat(
355 // "[ATTACHMENTS MODULE]: Doing initial rez of attachment with itemID {0}, assetID {1}, point {2} for {3} in {4}",
356 // attach.ItemID, attach.AssetID, p, sp.Name, m_scene.RegionInfo.RegionName);
357 
358  // For some reason assetIDs are being written as Zero's in the DB -- need to track tat down
359  // But they're not used anyway, the item is being looked up for now, so let's proceed.
360  //if (UUID.Zero == assetID)
361  //{
362  // m_log.DebugFormat("[ATTACHMENT]: Cannot rez attachment in point {0} with itemID {1}", p, itemID);
363  // continue;
364  //}
365 
366  try
367  {
368  string xmlData;
369  XmlDocument d = null;
370  UUID asset;
371  if (itemData.TryGetValue(attach.ItemID, out xmlData))
372  {
373  d = new XmlDocument();
374  d.LoadXml(xmlData);
375  m_log.InfoFormat("[ATTACHMENT]: Found saved state for item {0}, loading it", attach.ItemID);
376  }
377 
378  // If we're an NPC then skip all the item checks and manipulations since we don't have an
379  // inventory right now.
380  RezSingleAttachmentFromInventoryInternal(
381  sp, sp.PresenceType == PresenceType.Npc ? UUID.Zero : attach.ItemID, attach.AssetID, attachmentPt, true, d);
382  }
383  catch (Exception e)
384  {
385  UUID agentId = (sp.ControllingClient == null) ? default(UUID) : sp.ControllingClient.AgentId;
386  m_log.ErrorFormat("[ATTACHMENTS MODULE]: Unable to rez attachment with itemID {0}, assetID {1}, point {2} for {3}: {4}\n{5}",
387  attach.ItemID, attach.AssetID, attachmentPt, agentId, e.Message, e.StackTrace);
388  }
389  }
390  }
391 
393  {
394  if (!Enabled)
395  return;
396 
397  List<SceneObjectGroup> attachments = sp.GetAttachments();
398 
399  if (DebugLevel > 0)
400  m_log.DebugFormat(
401  "[ATTACHMENTS MODULE]: Saving for {0} attachments for {1} in {2}",
402  attachments.Count, sp.Name, m_scene.Name);
403 
404  if (attachments.Count <= 0)
405  return;
406 
407  Dictionary<SceneObjectGroup, string> scriptStates = new Dictionary<SceneObjectGroup, string>();
408 
409 
410  if (sp.PresenceType != PresenceType.Npc)
411  {
412  foreach (SceneObjectGroup so in attachments)
413  {
414  // Scripts MUST be snapshotted before the object is
415  // removed from the scene because doing otherwise will
416  // clobber the run flag
417  // This must be done outside the sp.AttachmentSyncLock so that there is no risk of a deadlock from
418  // scripts performing attachment operations at the same time. Getting object states stops the scripts.
419  scriptStates[so] = PrepareScriptInstanceForSave(so, false);
420  }
421 
422  lock (sp.AttachmentsSyncLock)
423  {
424  foreach (SceneObjectGroup so in attachments)
425  UpdateDetachedObject(sp, so, scriptStates[so]);
426  sp.ClearAttachments();
427  }
428  }
429  else
430  {
431  lock (sp.AttachmentsSyncLock)
432  {
433  foreach (SceneObjectGroup so in attachments)
434  UpdateDetachedObject(sp, so, String.Empty);
435  sp.ClearAttachments();
436  }
437  }
438  }
439 
440  public void DeleteAttachmentsFromScene(IScenePresence sp, bool silent)
441  {
442  if (!Enabled)
443  return;
444 
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 
450  foreach (SceneObjectGroup sop in sp.GetAttachments())
451  {
452  sop.Scene.DeleteSceneObject(sop, silent);
453  }
454 
455  sp.ClearAttachments();
456  }
457 
458  public bool AttachObject(IScenePresence sp, SceneObjectGroup group, uint attachmentPt, bool silent,
459  bool addToInventory, bool append)
460  {
461  if (!Enabled)
462  return false;
463 
464  return AttachObjectInternal(sp, group, attachmentPt, silent, addToInventory, false, append);
465  }
466 
477  private bool AttachObjectInternal(IScenePresence sp, SceneObjectGroup group, uint attachmentPt,
478  bool silent, bool addToInventory, bool resumeScripts, bool append)
479  {
480 // m_log.DebugFormat(
481 // "[ATTACHMENTS MODULE]: Attaching object {0} {1} to {2} point {3} from ground (silent = {4})",
482 // group.Name, group.LocalId, sp.Name, attachmentPt, silent);
483 
484  if (sp.GetAttachments().Contains(group))
485  {
486 // m_log.WarnFormat(
487 // "[ATTACHMENTS MODULE]: Ignoring request to attach {0} {1} to {2} on {3} since it's already attached",
488 // group.Name, group.LocalId, sp.Name, AttachmentPt);
489 
490  return false;
491  }
492 
493  if (group.GetSittingAvatarsCount() != 0)
494  {
495  if (DebugLevel > 0)
496  m_log.WarnFormat(
497  "[ATTACHMENTS MODULE]: Ignoring request to attach {0} {1} to {2} on {3} since {4} avatars are still sitting on it",
498  group.Name, group.LocalId, sp.Name, attachmentPt, group.GetSittingAvatarsCount());
499 
500  return false;
501  }
502 
503  Vector3 attachPos = group.AbsolutePosition;
504 
505  // TODO: this short circuits multiple attachments functionality in LL viewer 2.1+ and should
506  // be removed when that functionality is implemented in opensim
507  attachmentPt &= 0x7f;
508 
509  // If the attachment point isn't the same as the one previously used
510  // set it's offset position = 0 so that it appears on the attachment point
511  // and not in a weird location somewhere unknown.
512  if (attachmentPt != (uint)AttachmentPoint.Default && attachmentPt != group.AttachmentPoint)
513  {
514  attachPos = Vector3.Zero;
515  }
516 
517  // if the attachment point is the same as previous, make sure we get the saved
518  // position info.
519  if (attachmentPt != 0 && attachmentPt == group.RootPart.Shape.LastAttachPoint)
520  {
521  attachPos = group.RootPart.AttachedPos;
522  }
523 
524  // AttachmentPt 0 means the client chose to 'wear' the attachment.
525  if (attachmentPt == (uint)AttachmentPoint.Default)
526  {
527  // Check object for stored attachment point
528  attachmentPt = group.AttachmentPoint;
529  }
530 
531  // if we didn't find an attach point, look for where it was last attached
532  if (attachmentPt == 0)
533  {
534  attachmentPt = (uint)group.RootPart.Shape.LastAttachPoint;
535  attachPos = group.RootPart.AttachedPos;
536  group.HasGroupChanged = true;
537  }
538 
539  // if we still didn't find a suitable attachment point.......
540  if (attachmentPt == 0)
541  {
542  // Stick it on left hand with Zero Offset from the attachment point.
543  attachmentPt = (uint)AttachmentPoint.LeftHand;
544  attachPos = Vector3.Zero;
545  }
546 
547  List<SceneObjectGroup> attachments = sp.GetAttachments(attachmentPt);
548 
549  if (attachments.Contains(group))
550  {
551  if (DebugLevel > 0)
552  m_log.WarnFormat(
553  "[ATTACHMENTS MODULE]: Ignoring request to attach {0} {1} to {2} on {3} since it's already attached",
554  group.Name, group.LocalId, sp.Name, attachmentPt);
555 
556  return false;
557  }
558 
559  // If we already have 5, remove the oldest until only 4 are left. Skip over temp ones
560  while (attachments.Count >= 5)
561  {
562  if (attachments[0].FromItemID != UUID.Zero)
563  DetachSingleAttachmentToInv(sp, attachments[0]);
564  attachments.RemoveAt(0);
565  }
566 
567  // If we're not appending, remove the rest as well
568  if (attachments.Count != 0 && !append)
569  {
570  foreach (SceneObjectGroup g in attachments)
571  {
572  if (g.FromItemID != UUID.Zero)
573  DetachSingleAttachmentToInv(sp, g);
574  }
575  }
576 
577  group.DetachFromBackup();
578 
579  lock (sp.AttachmentsSyncLock)
580  {
581  group.AttachmentPoint = attachmentPt;
582  group.AbsolutePosition = attachPos;
583 
584  if (addToInventory && sp.PresenceType != PresenceType.Npc)
585  UpdateUserInventoryWithAttachment(sp, group, attachmentPt, append);
586 
587  AttachToAgent(sp, group, attachmentPt, attachPos, silent);
588 
589  if (resumeScripts)
590  {
591  // Fire after attach, so we don't get messy perms dialogs
592  // 4 == AttachedRez
593  group.CreateScriptInstances(0, true, m_scene.DefaultScriptEngine, 4);
594  group.ResumeScripts();
595  }
596 
597  // Do this last so that event listeners have access to all the effects of the attachment
598  m_scene.EventManager.TriggerOnAttach(group.LocalId, group.FromItemID, sp.UUID);
599  }
600 
601  return true;
602  }
603 
604  private void UpdateUserInventoryWithAttachment(IScenePresence sp, SceneObjectGroup group, uint attachmentPt, bool append)
605  {
606  // Add the new attachment to inventory if we don't already have it.
607  UUID newAttachmentItemID = group.FromItemID;
608  if (newAttachmentItemID == UUID.Zero)
609  newAttachmentItemID = AddSceneObjectAsNewAttachmentInInv(sp, group).ID;
610 
611  ShowAttachInUserInventory(sp, attachmentPt, newAttachmentItemID, group, append);
612  }
613 
614  public ISceneEntity RezSingleAttachmentFromInventory(IScenePresence sp, UUID itemID, uint AttachmentPt)
615  {
616  return RezSingleAttachmentFromInventory(sp, itemID, AttachmentPt, null);
617  }
618 
619  public ISceneEntity RezSingleAttachmentFromInventory(IScenePresence sp, UUID itemID, uint AttachmentPt, XmlDocument doc)
620  {
621  if (!Enabled)
622  return null;
623 
624  if (DebugLevel > 0)
625  m_log.DebugFormat(
626  "[ATTACHMENTS MODULE]: RezSingleAttachmentFromInventory to point {0} from item {1} for {2} in {3}",
627  (AttachmentPoint)AttachmentPt, itemID, sp.Name, m_scene.Name);
628 
629  // We check the attachments in the avatar appearance here rather than the objects attached to the
630  // ScenePresence itself so that we can ignore calls by viewer 2/3 to attach objects on startup. We are
631  // already doing this in ScenePresence.MakeRootAgent(). Simulator-side attaching needs to be done
632  // because pre-outfit folder viewers (most version 1 viewers) require it.
633  bool alreadyOn = false;
634  List<AvatarAttachment> existingAttachments = sp.Appearance.GetAttachments();
635  foreach (AvatarAttachment existingAttachment in existingAttachments)
636  {
637  if (existingAttachment.ItemID == itemID)
638  {
639  alreadyOn = true;
640  break;
641  }
642  }
643 
644  if (alreadyOn)
645  {
646  if (DebugLevel > 0)
647  m_log.DebugFormat(
648  "[ATTACHMENTS MODULE]: Ignoring request by {0} to wear item {1} at {2} since it is already worn",
649  sp.Name, itemID, AttachmentPt);
650 
651  return null;
652  }
653 
654  bool append = (AttachmentPt & 0x80) != 0;
655  AttachmentPt &= 0x7f;
656 
657  return RezSingleAttachmentFromInventoryInternal(sp, itemID, UUID.Zero, AttachmentPt, append, doc);
658  }
659 
660  public void RezMultipleAttachmentsFromInventory(IScenePresence sp, List<KeyValuePair<UUID, uint>> rezlist)
661  {
662  if (!Enabled)
663  return;
664 
665  if (DebugLevel > 0)
666  m_log.DebugFormat(
667  "[ATTACHMENTS MODULE]: Rezzing {0} attachments from inventory for {1} in {2}",
668  rezlist.Count, sp.Name, m_scene.Name);
669 
670  foreach (KeyValuePair<UUID, uint> rez in rezlist)
671  {
672  RezSingleAttachmentFromInventory(sp, rez.Key, rez.Value);
673  }
674  }
675 
676  public void DetachSingleAttachmentToGround(IScenePresence sp, uint soLocalId)
677  {
678  Vector3 pos = new Vector3(2.5f, 0f, 0f);
679  pos *= ((ScenePresence)sp).Rotation;
680  pos += sp.AbsolutePosition;
681  DetachSingleAttachmentToGround(sp, soLocalId, pos, Quaternion.Identity);
682  }
683 
684  public void DetachSingleAttachmentToGround(IScenePresence sp, uint soLocalId, Vector3 absolutePos, Quaternion absoluteRot)
685  {
686  if (!Enabled)
687  return;
688 
689  if (DebugLevel > 0)
690  m_log.DebugFormat(
691  "[ATTACHMENTS MODULE]: DetachSingleAttachmentToGround() for {0}, object {1}",
692  sp.UUID, soLocalId);
693 
694  SceneObjectGroup so = m_scene.GetGroupByPrim(soLocalId);
695 
696  if (so == null)
697  return;
698 
699  if (so.AttachedAvatar != sp.UUID)
700  return;
701 
702  UUID inventoryID = so.FromItemID;
703 
704  // As per Linden spec, drop is disabled for temp attachs
705  if (inventoryID == UUID.Zero)
706  return;
707 
708  if (DebugLevel > 0)
709  m_log.DebugFormat(
710  "[ATTACHMENTS MODULE]: In DetachSingleAttachmentToGround(), object is {0} {1}, associated item is {2}",
711  so.Name, so.LocalId, inventoryID);
712 
713  lock (sp.AttachmentsSyncLock)
714  {
715  if (!m_scene.Permissions.CanRezObject(
716  so.PrimCount, sp.UUID, sp.AbsolutePosition))
717  return;
718 
719  bool changed = false;
720  if (inventoryID != UUID.Zero)
721  changed = sp.Appearance.DetachAttachment(inventoryID);
722  if (changed && m_scene.AvatarFactory != null)
723  m_scene.AvatarFactory.QueueAppearanceSave(sp.UUID);
724 
725  so.RootPart.Shape.LastAttachPoint = (byte)so.AttachmentPoint;
726 
727  sp.RemoveAttachment(so);
728  so.FromItemID = UUID.Zero;
729 
730  so.AttachedAvatar = UUID.Zero;
731  so.ClearPartAttachmentData();
732 
733  SceneObjectPart rootPart = so.RootPart;
734 
735  rootPart.SetParentLocalId(0);
736  so.AbsolutePosition = absolutePos;
737  if (absoluteRot != Quaternion.Identity)
738  {
739  so.UpdateGroupRotationR(absoluteRot);
740  }
741 
742  rootPart.RemFlag(PrimFlags.TemporaryOnRez);
743 
744  so.ApplyPhysics();
745 
746  rootPart.Rezzed = DateTime.Now;
747  so.AttachToBackup();
748  m_scene.EventManager.TriggerParcelPrimCountTainted();
749 
750  rootPart.ClearUndoState();
751 
752  List<UUID> uuids = new List<UUID>();
753  uuids.Add(inventoryID);
754  m_scene.InventoryService.DeleteItems(sp.UUID, uuids);
755  sp.ControllingClient.SendRemoveInventoryItem(inventoryID);
756  }
757 
758  m_scene.EventManager.TriggerOnAttach(so.LocalId, so.UUID, UUID.Zero);
759 
760  // Attach (NULL) stops scripts. We don't want that. Resume them.
761  so.ResumeScripts();
762  so.HasGroupChanged = true;
763  so.RootPart.ScheduleFullUpdate();
764  so.ScheduleGroupForTerseUpdate();
765  }
766 
768  {
769  if (so.AttachedAvatar != sp.UUID)
770  {
771  m_log.WarnFormat(
772  "[ATTACHMENTS MODULE]: Tried to detach object {0} from {1} {2} but attached avatar id was {3} in {4}",
773  so.Name, sp.Name, sp.UUID, so.AttachedAvatar, m_scene.RegionInfo.RegionName);
774 
775  return;
776  }
777 
778  if (DebugLevel > 0)
779  m_log.DebugFormat(
780  "[ATTACHMENTS MODULE]: Detaching object {0} {1} (FromItemID {2}) for {3} in {4}",
781  so.Name, so.LocalId, so.FromItemID, sp.Name, m_scene.Name);
782 
783  // Scripts MUST be snapshotted before the object is
784  // removed from the scene because doing otherwise will
785  // clobber the run flag
786  // This must be done outside the sp.AttachmentSyncLock so that there is no risk of a deadlock from
787  // scripts performing attachment operations at the same time. Getting object states stops the scripts.
788  string scriptedState = PrepareScriptInstanceForSave(so, true);
789 
790  lock (sp.AttachmentsSyncLock)
791  {
792  // Save avatar attachment information
793 // m_log.Debug("[ATTACHMENTS MODULE]: Detaching from UserID: " + sp.UUID + ", ItemID: " + itemID);
794 
795  bool changed = sp.Appearance.DetachAttachment(so.FromItemID);
796  if (changed && m_scene.AvatarFactory != null)
797  m_scene.AvatarFactory.QueueAppearanceSave(sp.UUID);
798 
799  sp.RemoveAttachment(so);
800  UpdateDetachedObject(sp, so, scriptedState);
801  }
802  }
803 
804  public void UpdateAttachmentPosition(SceneObjectGroup sog, Vector3 pos)
805  {
806  if (!Enabled)
807  return;
808 
809  sog.UpdateGroupPosition(pos);
810  sog.HasGroupChanged = true;
811  }
812 
813  #endregion
814 
815  #region AttachmentModule private methods
816 
817  // This is public but is not part of the IAttachmentsModule interface.
818  // RegionCombiner module needs to poke at it to deliver client events.
819  // This breaks the encapsulation of the module and should get fixed somehow.
821  {
822  client.OnRezSingleAttachmentFromInv += Client_OnRezSingleAttachmentFromInv;
823  client.OnRezMultipleAttachmentsFromInv += Client_OnRezMultipleAttachmentsFromInv;
824  client.OnObjectAttach += Client_OnObjectAttach;
825  client.OnObjectDetach += Client_OnObjectDetach;
826  client.OnDetachAttachmentIntoInv += Client_OnDetachAttachmentIntoInv;
827  client.OnObjectDrop += Client_OnObjectDrop;
828  }
829 
830  // This is public but is not part of the IAttachmentsModule interface.
831  // RegionCombiner module needs to poke at it to deliver client events.
832  // This breaks the encapsulation of the module and should get fixed somehow.
834  {
835  client.OnRezSingleAttachmentFromInv -= Client_OnRezSingleAttachmentFromInv;
836  client.OnRezMultipleAttachmentsFromInv -= Client_OnRezMultipleAttachmentsFromInv;
837  client.OnObjectAttach -= Client_OnObjectAttach;
838  client.OnObjectDetach -= Client_OnObjectDetach;
839  client.OnDetachAttachmentIntoInv -= Client_OnDetachAttachmentIntoInv;
840  client.OnObjectDrop -= Client_OnObjectDrop;
841  }
842 
853  private void UpdateKnownItem(IScenePresence sp, SceneObjectGroup grp, string scriptedState)
854  {
855  if (grp.FromItemID == UUID.Zero)
856  {
857  // We can't save temp attachments
858  grp.HasGroupChanged = false;
859  return;
860  }
861 
862  // Saving attachments for NPCs messes them up for the real owner!
863  INPCModule module = m_scene.RequestModuleInterface<INPCModule>();
864  if (module != null)
865  {
866  if (module.IsNPC(sp.UUID, m_scene))
867  return;
868  }
869 
870  if (grp.HasGroupChanged)
871  {
872  m_log.DebugFormat(
873  "[ATTACHMENTS MODULE]: Updating asset for attachment {0}, attachpoint {1}",
874  grp.UUID, grp.AttachmentPoint);
875 
876  string sceneObjectXml = SceneObjectSerializer.ToOriginalXmlFormat(grp, scriptedState);
877 
878  InventoryItemBase item = new InventoryItemBase(grp.FromItemID, sp.UUID);
879  item = m_scene.InventoryService.GetItem(item);
880 
881  if (item != null)
882  {
883  AssetBase asset = m_scene.CreateAsset(
884  grp.GetPartName(grp.LocalId),
885  grp.GetPartDescription(grp.LocalId),
886  (sbyte)AssetType.Object,
887  Utils.StringToBytes(sceneObjectXml),
888  sp.UUID);
889 
890  if (m_invAccessModule != null)
891  m_invAccessModule.UpdateInventoryItemAsset(sp.UUID, item, asset);
892 
893  // If the name of the object has been changed whilst attached then we want to update the inventory
894  // item in the viewer.
895  if (sp.ControllingClient != null)
896  sp.ControllingClient.SendInventoryItemCreateUpdate(item, 0);
897  }
898 
899  grp.HasGroupChanged = false; // Prevent it being saved over and over
900  }
901  else if (DebugLevel > 0)
902  {
903  m_log.DebugFormat(
904  "[ATTACHMENTS MODULE]: Don't need to update asset for unchanged attachment {0}, attachpoint {1}",
905  grp.UUID, grp.AttachmentPoint);
906  }
907  }
908 
921  private void AttachToAgent(
922  IScenePresence sp, SceneObjectGroup so, uint attachmentpoint, Vector3 attachOffset, bool silent)
923  {
924  if (DebugLevel > 0)
925  m_log.DebugFormat(
926  "[ATTACHMENTS MODULE]: Adding attachment {0} to avatar {1} at pt {2} pos {3} {4} in {5}",
927  so.Name, sp.Name, attachmentpoint, attachOffset, so.RootPart.AttachedPos, m_scene.Name);
928 
929  // Remove from database and parcel prim count
930  m_scene.DeleteFromStorage(so.UUID);
931  m_scene.EventManager.TriggerParcelPrimCountTainted();
932 
933  so.AttachedAvatar = sp.UUID;
934 
935  foreach (SceneObjectPart part in so.Parts)
936  {
937 // if (part.KeyframeMotion != null)
938 // part.KeyframeMotion.Suspend();
939 
940  if (part.PhysActor != null)
941  {
942  part.RemoveFromPhysics();
943  }
944  }
945 
946  so.AbsolutePosition = attachOffset;
947  so.RootPart.AttachedPos = attachOffset;
948  so.IsAttachment = true;
949  so.RootPart.SetParentLocalId(sp.LocalId);
950  so.AttachmentPoint = attachmentpoint;
951 
952  sp.AddAttachment(so);
953 
954  if (!silent)
955  {
957  {
958  if (DebugLevel > 0)
959  m_log.DebugFormat(
960  "[ATTACHMENTS MODULE]: Killing private HUD {0} for avatars other than {1} at attachment point {2}",
961  so.Name, sp.Name, so.AttachmentPoint);
962 
963  // As this scene object can now only be seen by the attaching avatar, tell everybody else in the
964  // scene that it's no longer in their awareness.
965  m_scene.ForEachClient(
966  client =>
967  { if (client.AgentId != so.AttachedAvatar)
968  client.SendKillObject(new List<uint>() { so.LocalId });
969  });
970  }
971 
972  // Fudge below is an extremely unhelpful comment. It's probably here so that the scheduled full update
973  // will succeed, as that will not update if an attachment is selected.
974  so.IsSelected = false; // fudge....
975 
976  so.ScheduleGroupForFullUpdate();
977  }
978 
979  // In case it is later dropped again, don't let
980  // it get cleaned up
981  so.RootPart.RemFlag(PrimFlags.TemporaryOnRez);
982  }
983 
990  private InventoryItemBase AddSceneObjectAsNewAttachmentInInv(IScenePresence sp, SceneObjectGroup grp)
991  {
992  if (m_invAccessModule == null)
993  return null;
994 
995  if (DebugLevel > 0)
996  m_log.DebugFormat(
997  "[ATTACHMENTS MODULE]: Called AddSceneObjectAsAttachment for object {0} {1} for {2}",
998  grp.Name, grp.LocalId, sp.Name);
999 
1000  InventoryItemBase newItem
1001  = m_invAccessModule.CopyToInventory(
1002  DeRezAction.TakeCopy,
1003  m_scene.InventoryService.GetFolderForType(sp.UUID, FolderType.Object).ID,
1004  new List<SceneObjectGroup> { grp },
1005  sp.ControllingClient, true)[0];
1006 
1007  // sets itemID so client can show item as 'attached' in inventory
1008  grp.FromItemID = newItem.ID;
1009 
1010  return newItem;
1011  }
1012 
1029  private string PrepareScriptInstanceForSave(SceneObjectGroup grp, bool fireDetachEvent)
1030  {
1031  if (fireDetachEvent)
1032  {
1033  m_scene.EventManager.TriggerOnAttach(grp.LocalId, grp.FromItemID, UUID.Zero);
1034 
1035  // Allow detach event time to do some work before stopping the script
1036  Thread.Sleep(2);
1037  }
1038 
1039  using (StringWriter sw = new StringWriter())
1040  {
1041  using (XmlTextWriter writer = new XmlTextWriter(sw))
1042  {
1043  grp.SaveScriptedState(writer);
1044  }
1045 
1046  return sw.ToString();
1047  }
1048  }
1049 
1050  private void UpdateDetachedObject(IScenePresence sp, SceneObjectGroup so, string scriptedState)
1051  {
1052  // Don't save attachments for HG visitors, it
1053  // messes up their inventory. When a HG visitor logs
1054  // out on a foreign grid, their attachments will be
1055  // reloaded in the state they were in when they left
1056  // the home grid. This is best anyway as the visited
1057  // grid may use an incompatible script engine.
1058  bool saveChanged
1059  = sp.PresenceType != PresenceType.Npc
1060  && (m_scene.UserManagementModule == null
1061  || m_scene.UserManagementModule.IsLocalGridUser(sp.UUID));
1062 
1063  // Remove the object from the scene so no more updates
1064  // are sent. Doing this before the below changes will ensure
1065  // updates can't cause "HUD artefacts"
1066 
1067  m_scene.DeleteSceneObject(so, false, false);
1068 
1069  // Prepare sog for storage
1070  so.AttachedAvatar = UUID.Zero;
1071  so.RootPart.SetParentLocalId(0);
1072  so.IsAttachment = false;
1073 
1074  if (saveChanged)
1075  {
1076  // We cannot use AbsolutePosition here because that would
1077  // attempt to cross the prim as it is detached
1078  so.ForEachPart(x => { x.GroupPosition = so.RootPart.AttachedPos; });
1079 
1080  UpdateKnownItem(sp, so, scriptedState);
1081  }
1082 
1083  // Now, remove the scripts
1084  so.RemoveScriptInstances(true);
1085  so.Clear();
1086  }
1087 
1089  IScenePresence sp, UUID itemID, UUID assetID, uint attachmentPt, bool append, XmlDocument doc)
1090  {
1091  if (m_invAccessModule == null)
1092  return null;
1093 
1094  SceneObjectGroup objatt;
1095 
1096  if (itemID != UUID.Zero)
1097  objatt = m_invAccessModule.RezObject(sp.ControllingClient,
1098  itemID, Vector3.Zero, Vector3.Zero, UUID.Zero, (byte)1, true,
1099  false, false, sp.UUID, true);
1100  else
1101  objatt = m_invAccessModule.RezObject(sp.ControllingClient,
1102  null, assetID, Vector3.Zero, Vector3.Zero, UUID.Zero, (byte)1, true,
1103  false, false, sp.UUID, true);
1104 
1105  if (objatt == null)
1106  {
1107  m_log.WarnFormat(
1108  "[ATTACHMENTS MODULE]: Could not retrieve item {0} for attaching to avatar {1} at point {2}",
1109  itemID, sp.Name, attachmentPt);
1110 
1111  return null;
1112  }
1113  else if (itemID == UUID.Zero)
1114  {
1115  // We need to have a FromItemID for multiple attachments on a single attach point to appear. This is
1116  // true on Singularity 1.8.5 and quite possibly other viewers as well. As NPCs don't have an inventory
1117  // we will satisfy this requirement by inserting a random UUID.
1118  objatt.FromItemID = UUID.Random();
1119  }
1120 
1121  if (DebugLevel > 0)
1122  m_log.DebugFormat(
1123  "[ATTACHMENTS MODULE]: Rezzed single object {0} with {1} prims for attachment to {2} on point {3} in {4}",
1124  objatt.Name, objatt.PrimCount, sp.Name, attachmentPt, m_scene.Name);
1125 
1126  // HasGroupChanged is being set from within RezObject. Ideally it would be set by the caller.
1127  objatt.HasGroupChanged = false;
1128  bool tainted = false;
1129  if (attachmentPt != 0 && attachmentPt != objatt.AttachmentPoint)
1130  tainted = true;
1131 
1132  // FIXME: Detect whether it's really likely for AttachObject to throw an exception in the normal
1133  // course of events. If not, then it's probably not worth trying to recover the situation
1134  // since this is more likely to trigger further exceptions and confuse later debugging. If
1135  // exceptions can be thrown in expected error conditions (not NREs) then make this consistent
1136  // since other normal error conditions will simply return false instead.
1137  // This will throw if the attachment fails
1138  try
1139  {
1140  if (doc != null)
1141  {
1142  objatt.LoadScriptState(doc);
1143  objatt.ResetOwnerChangeFlag();
1144  }
1145 
1146  AttachObjectInternal(sp, objatt, attachmentPt, false, true, true, append);
1147  }
1148  catch (Exception e)
1149  {
1150  m_log.ErrorFormat(
1151  "[ATTACHMENTS MODULE]: Failed to attach {0} {1} for {2}, exception {3}{4}",
1152  objatt.Name, objatt.UUID, sp.Name, e.Message, e.StackTrace);
1153 
1154  // Make sure the object doesn't stick around and bail
1155  sp.RemoveAttachment(objatt);
1156  m_scene.DeleteSceneObject(objatt, false);
1157  return null;
1158  }
1159 
1160  if (tainted)
1161  objatt.HasGroupChanged = true;
1162 
1163  if (ThrottlePer100PrimsRezzed > 0)
1164  {
1165  int throttleMs = (int)Math.Round((float)objatt.PrimCount / 100 * ThrottlePer100PrimsRezzed);
1166 
1167  if (DebugLevel > 0)
1168  m_log.DebugFormat(
1169  "[ATTACHMENTS MODULE]: Throttling by {0}ms after rez of {1} with {2} prims for attachment to {3} on point {4} in {5}",
1170  throttleMs, objatt.Name, objatt.PrimCount, sp.Name, attachmentPt, m_scene.Name);
1171 
1172  Thread.Sleep(throttleMs);
1173  }
1174 
1175  return objatt;
1176  }
1177 
1185  private void ShowAttachInUserInventory(IScenePresence sp, uint AttachmentPt, UUID itemID, SceneObjectGroup att, bool append)
1186  {
1187 // m_log.DebugFormat(
1188 // "[USER INVENTORY]: Updating attachment {0} for {1} at {2} using item ID {3}",
1189 // att.Name, sp.Name, AttachmentPt, itemID);
1190 
1191  if (UUID.Zero == itemID)
1192  {
1193  m_log.Error("[ATTACHMENTS MODULE]: Unable to save attachment. Error inventory item ID.");
1194  return;
1195  }
1196 
1197  if (0 == AttachmentPt)
1198  {
1199  m_log.Error("[ATTACHMENTS MODULE]: Unable to save attachment. Error attachment point.");
1200  return;
1201  }
1202 
1203  InventoryItemBase item = new InventoryItemBase(itemID, sp.UUID);
1204  item = m_scene.InventoryService.GetItem(item);
1205  if (item == null)
1206  return;
1207 
1208  int attFlag = append ? 0x80 : 0;
1209  bool changed = sp.Appearance.SetAttachment((int)AttachmentPt | attFlag, itemID, item.AssetID);
1210  if (changed && m_scene.AvatarFactory != null)
1211  {
1212  if (DebugLevel > 0)
1213  m_log.DebugFormat(
1214  "[ATTACHMENTS MODULE]: Queueing appearance save for {0}, attachment {1} point {2} in ShowAttachInUserInventory()",
1215  sp.Name, att.Name, AttachmentPt);
1216 
1217  m_scene.AvatarFactory.QueueAppearanceSave(sp.UUID);
1218  }
1219  }
1220 
1221  #endregion
1222 
1223  #region Client Event Handlers
1224 
1225  private ISceneEntity Client_OnRezSingleAttachmentFromInv(IClientAPI remoteClient, UUID itemID, uint AttachmentPt)
1226  {
1227  if (!Enabled)
1228  return null;
1229 
1230  if (DebugLevel > 0)
1231  m_log.DebugFormat(
1232  "[ATTACHMENTS MODULE]: Rezzing attachment to point {0} from item {1} for {2}",
1233  (AttachmentPoint)AttachmentPt, itemID, remoteClient.Name);
1234 
1235  ScenePresence sp = m_scene.GetScenePresence(remoteClient.AgentId);
1236 
1237  if (sp == null)
1238  {
1239  m_log.ErrorFormat(
1240  "[ATTACHMENTS MODULE]: Could not find presence for client {0} {1} in RezSingleAttachmentFromInventory()",
1241  remoteClient.Name, remoteClient.AgentId);
1242  return null;
1243  }
1244 
1245  return RezSingleAttachmentFromInventory(sp, itemID, AttachmentPt);
1246  }
1247 
1248  private void Client_OnRezMultipleAttachmentsFromInv(IClientAPI remoteClient, List<KeyValuePair<UUID, uint>> rezlist)
1249  {
1250  if (!Enabled)
1251  return;
1252 
1253  ScenePresence sp = m_scene.GetScenePresence(remoteClient.AgentId);
1254  if (sp != null)
1255  RezMultipleAttachmentsFromInventory(sp, rezlist);
1256  else
1257  m_log.ErrorFormat(
1258  "[ATTACHMENTS MODULE]: Could not find presence for client {0} {1} in RezMultipleAttachmentsFromInventory()",
1259  remoteClient.Name, remoteClient.AgentId);
1260  }
1261 
1262  private void Client_OnObjectAttach(IClientAPI remoteClient, uint objectLocalID, uint AttachmentPt, bool silent)
1263  {
1264  if (DebugLevel > 0)
1265  m_log.DebugFormat(
1266  "[ATTACHMENTS MODULE]: Attaching object local id {0} to {1} point {2} from ground (silent = {3})",
1267  objectLocalID, remoteClient.Name, AttachmentPt, silent);
1268 
1269  if (!Enabled)
1270  return;
1271 
1272  try
1273  {
1274  ScenePresence sp = m_scene.GetScenePresence(remoteClient.AgentId);
1275 
1276  if (sp == null)
1277  {
1278  m_log.ErrorFormat(
1279  "[ATTACHMENTS MODULE]: Could not find presence for client {0} {1}", remoteClient.Name, remoteClient.AgentId);
1280  return;
1281  }
1282 
1283  // If we can't take it, we can't attach it!
1284  SceneObjectPart part = m_scene.GetSceneObjectPart(objectLocalID);
1285  if (part == null)
1286  return;
1287 
1288  if (!m_scene.Permissions.CanTakeObject(part.UUID, remoteClient.AgentId))
1289  {
1290  remoteClient.SendAgentAlertMessage(
1291  "You don't have sufficient permissions to attach this object", false);
1292 
1293  return;
1294  }
1295 
1296  bool append = (AttachmentPt & 0x80) != 0;
1297  AttachmentPt &= 0x7f;
1298 
1299  // Calls attach with a Zero position
1300  if (AttachObject(sp, part.ParentGroup, AttachmentPt, false, true, append))
1301  {
1302  if (DebugLevel > 0)
1303  m_log.Debug(
1304  "[ATTACHMENTS MODULE]: Saving avatar attachment. AgentID: " + remoteClient.AgentId
1305  + ", AttachmentPoint: " + AttachmentPt);
1306 
1307  // Save avatar attachment information
1308  m_scene.AvatarFactory.QueueAppearanceSave(sp.UUID);
1309  }
1310  }
1311  catch (Exception e)
1312  {
1313  m_log.ErrorFormat("[ATTACHMENTS MODULE]: exception upon Attach Object {0}{1}", e.Message, e.StackTrace);
1314  }
1315  }
1316 
1317  private void Client_OnObjectDetach(uint objectLocalID, IClientAPI remoteClient)
1318  {
1319  if (!Enabled)
1320  return;
1321 
1322  ScenePresence sp = m_scene.GetScenePresence(remoteClient.AgentId);
1323  SceneObjectGroup group = m_scene.GetGroupByPrim(objectLocalID);
1324 
1325  if (sp != null && group != null && group.FromItemID != UUID.Zero)
1326  DetachSingleAttachmentToInv(sp, group);
1327  }
1328 
1329  private void Client_OnDetachAttachmentIntoInv(UUID itemID, IClientAPI remoteClient)
1330  {
1331  if (!Enabled)
1332  return;
1333 
1334  ScenePresence sp = m_scene.GetScenePresence(remoteClient.AgentId);
1335  if (sp != null)
1336  {
1337  List<SceneObjectGroup> attachments = sp.GetAttachments();
1338 
1339  foreach (SceneObjectGroup group in attachments)
1340  {
1341  if (group.FromItemID == itemID && group.FromItemID != UUID.Zero)
1342  {
1343  DetachSingleAttachmentToInv(sp, group);
1344  return;
1345  }
1346  }
1347  }
1348  }
1349 
1350  private void Client_OnObjectDrop(uint soLocalId, IClientAPI remoteClient)
1351  {
1352  if (!Enabled)
1353  return;
1354 
1355  ScenePresence sp = m_scene.GetScenePresence(remoteClient.AgentId);
1356  if (sp != null)
1357  DetachSingleAttachmentToGround(sp, soLocalId);
1358  }
1359 
1360  #endregion
1361  }
1362 }
UUID FromItemID
The item ID that this object was rezzed from, if applicable.
SceneObjectGroup RezSingleAttachmentFromInventoryInternal(IScenePresence sp, UUID itemID, UUID assetID, uint attachmentPt, bool append, XmlDocument doc)
bool HasPrivateAttachmentPoint
If this scene object has an attachment point then indicate whether there is a point where attachments...
void Close()
This is the inverse to Initialise. After a Close(), this instance won't be usable anymore...
void DeleteAttachmentsFromScene(IScenePresence sp, bool silent)
Delete all the presence's attachments from the scene This is done when a root agent leaves/is demoted...
OpenSim.Server.Handlers.Simulation.Utils Utils
bool HasGroupChanged
This indicates whether the object has changed such that it needs to be repersisted to permenant stora...
string Name
Returns the full name of the agent/avatar represented by this client
Definition: IClientAPI.cs:754
void UpdateAttachmentPosition(SceneObjectGroup sog, Vector3 pos)
A scene object group is conceptually an object in the scene. The object is constituted of SceneObject...
bool AttachObject(IScenePresence sp, SceneObjectGroup group, uint attachmentPt, bool silent, bool addToInventory, bool append)
Attach an object to an avatar.
void DetachSingleAttachmentToGround(IScenePresence sp, uint soLocalId)
Detach the given item to the ground.
PresenceType
Indicate the type of ScenePresence.
Definition: PresenceType.cs:34
bool IsActive
True if the client is active (sending and receiving new UDP messages). False if the client is being c...
Definition: IClientAPI.cs:759
Asset class. All Assets are reference by this class or a class derived from this class ...
Definition: AssetBase.cs:49
void Initialise(IConfigSource source)
This is called to initialize the region module. For shared modules, this is called exactly once...
Object AttachmentsSyncLock
The AttachmentsModule synchronizes on this to avoid race conditions between commands to add and remov...
void CopyAttachments(AgentData ad, IScenePresence sp)
Copy attachment data from an AgentData structure into a ScenePresence.
List< SceneObjectGroup > GetAttachments()
The scene objects attached to this avatar.
int PrimCount
Number of prims in this group
void RegionLoaded(Scene scene)
This will be called once for every scene loaded. In a shared module this will be multiple times in on...
void CopyAttachments(IScenePresence sp, AgentData ad)
Copy attachment data from a ScenePresence into the AgentData structure for transmission to another si...
List< ISceneObject > AttachmentObjects
ISceneEntity RezSingleAttachmentFromInventory(IScenePresence sp, UUID itemID, uint AttachmentPt)
Rez an attachment from user inventory and change inventory status to match.
void RemoveRegion(Scene scene)
This is called whenever a Scene is removed. For shared modules, this can happen several times...
uint AttachmentPoint
Attachment point of this scene object to an avatar.
Inventory Item - contains all the properties associated with an individual inventory piece...
AvatarAppearance Appearance
Avatar appearance data.
Definition: ISceneAgent.cs:66
PhysicsActor PhysActor
The representation of this part in the physics scene.
bool IsNPC(UUID agentID, Scene scene)
Check if the agent is an NPC.
void AddRegion(Scene scene)
This is called whenever a Scene is added. For shared modules, this can happen several times...
Interactive OpenSim region server
Definition: OpenSim.cs:55
void DetachSingleAttachmentToInv(IScenePresence sp, SceneObjectGroup so)
Detach the given attachment so that it remains in the user's inventory.
void DeRezAttachments(IScenePresence sp)
Derez the attachements for a scene presence that is closing.
int GetSittingAvatarsCount()
Gets the number of sitting avatars.
UUID AttachedAvatar
The avatar to which this scene object is attached.
void RezMultipleAttachmentsFromInventory(IScenePresence sp, List< KeyValuePair< UUID, uint >> rezlist)
Rez multiple attachments from a user's inventory
void RezAttachments(IScenePresence sp)
RezAttachments. This should only be called upon login on the first region. Attachment rezzings on cro...
void DetachSingleAttachmentToGround(IScenePresence sp, uint soLocalId, Vector3 absolutePos, Quaternion absoluteRot)
Detach the given item to the ground at the specified coordinates & rotation
bool IsAttachment
Is this scene object acting as an attachment?
PresenceType PresenceType
What type of presence is this? User, NPC, etc.
Definition: ISceneAgent.cs:49
ISceneEntity RezSingleAttachmentFromInventory(IScenePresence sp, UUID itemID, uint AttachmentPt, XmlDocument doc)