aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/CoreModules
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--OpenSim/Region/CoreModules/Avatar/Attachments/AttachmentsModule.cs734
-rw-r--r--OpenSim/Region/CoreModules/Avatar/Attachments/Tests/AttachmentsModuleTests.cs2
2 files changed, 375 insertions, 361 deletions
diff --git a/OpenSim/Region/CoreModules/Avatar/Attachments/AttachmentsModule.cs b/OpenSim/Region/CoreModules/Avatar/Attachments/AttachmentsModule.cs
index 78b9afc..f893eb3 100644
--- a/OpenSim/Region/CoreModules/Avatar/Attachments/AttachmentsModule.cs
+++ b/OpenSim/Region/CoreModules/Avatar/Attachments/AttachmentsModule.cs
@@ -44,6 +44,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments
44 [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "AttachmentsModule")] 44 [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "AttachmentsModule")]
45 public class AttachmentsModule : IAttachmentsModule, INonSharedRegionModule 45 public class AttachmentsModule : IAttachmentsModule, INonSharedRegionModule
46 { 46 {
47 #region INonSharedRegionModule
47 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); 48 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
48 49
49 private Scene m_scene; 50 private Scene m_scene;
@@ -92,26 +93,10 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments
92 { 93 {
93 RemoveRegion(m_scene); 94 RemoveRegion(m_scene);
94 } 95 }
95 96
96 public void SubscribeToClientEvents(IClientAPI client) 97 #endregion
97 { 98
98 client.OnRezSingleAttachmentFromInv += RezSingleAttachmentFromInventory; 99 #region IAttachmentsModule
99 client.OnRezMultipleAttachmentsFromInv += RezMultipleAttachmentsFromInventory;
100 client.OnObjectAttach += AttachObject;
101 client.OnObjectDetach += DetachObject;
102 client.OnDetachAttachmentIntoInv += DetachSingleAttachmentToInv;
103 client.OnObjectDrop += DetachSingleAttachmentToGround;
104 }
105
106 public void UnsubscribeFromClientEvents(IClientAPI client)
107 {
108 client.OnRezSingleAttachmentFromInv -= RezSingleAttachmentFromInventory;
109 client.OnRezMultipleAttachmentsFromInv -= RezMultipleAttachmentsFromInventory;
110 client.OnObjectAttach -= AttachObject;
111 client.OnObjectDetach -= DetachObject;
112 client.OnDetachAttachmentIntoInv -= DetachSingleAttachmentToInv;
113 client.OnObjectDrop -= DetachSingleAttachmentToGround;
114 }
115 100
116 /// <summary> 101 /// <summary>
117 /// RezAttachments. This should only be called upon login on the first region. 102 /// RezAttachments. This should only be called upon login on the first region.
@@ -174,7 +159,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments
174// { 159// {
175 grp.IsAttachment = false; 160 grp.IsAttachment = false;
176 grp.AbsolutePosition = grp.RootPart.AttachedPos; 161 grp.AbsolutePosition = grp.RootPart.AttachedPos;
177 UpdateKnownItem(sp.ControllingClient, grp); 162 UpdateKnownItem(sp, grp);
178 grp.IsAttachment = true; 163 grp.IsAttachment = true;
179// } 164// }
180 } 165 }
@@ -197,68 +182,6 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments
197 sp.ClearAttachments(); 182 sp.ClearAttachments();
198 } 183 }
199 184
200 /// <summary>
201 /// Called by client
202 /// </summary>
203 /// <param name="remoteClient"></param>
204 /// <param name="objectLocalID"></param>
205 /// <param name="AttachmentPt"></param>
206 /// <param name="silent"></param>
207 private void AttachObject(IClientAPI remoteClient, uint objectLocalID, uint AttachmentPt, bool silent)
208 {
209// m_log.DebugFormat(
210// "[ATTACHMENTS MODULE]: Attaching object local id {0} to {1} point {2} from ground (silent = {3})",
211// objectLocalID, remoteClient.Name, AttachmentPt, silent);
212
213 if (!Enabled)
214 return;
215
216 try
217 {
218 ScenePresence sp = m_scene.GetScenePresence(remoteClient.AgentId);
219
220 if (sp == null)
221 {
222 m_log.ErrorFormat(
223 "[ATTACHMENTS MODULE]: Could not find presence for client {0} {1}", remoteClient.Name, remoteClient.AgentId);
224 return;
225 }
226
227 // If we can't take it, we can't attach it!
228 SceneObjectPart part = m_scene.GetSceneObjectPart(objectLocalID);
229 if (part == null)
230 return;
231
232 if (!m_scene.Permissions.CanTakeObject(part.UUID, remoteClient.AgentId))
233 {
234 remoteClient.SendAgentAlertMessage(
235 "You don't have sufficient permissions to attach this object", false);
236
237 return;
238 }
239
240 // TODO: this short circuits multiple attachments functionality in LL viewer 2.1+ and should
241 // be removed when that functionality is implemented in opensim
242 AttachmentPt &= 0x7f;
243
244 // Calls attach with a Zero position
245 if (AttachObject(sp, part.ParentGroup, AttachmentPt, false))
246 {
247 m_scene.EventManager.TriggerOnAttach(objectLocalID, part.ParentGroup.GetFromItemID(), remoteClient.AgentId);
248
249 // Save avatar attachment information
250 m_log.Debug(
251 "[ATTACHMENTS MODULE]: Saving avatar attachment. AgentID: " + remoteClient.AgentId
252 + ", AttachmentPoint: " + AttachmentPt);
253
254 }
255 }
256 catch (Exception e)
257 {
258 m_log.ErrorFormat("[ATTACHMENTS MODULE]: exception upon Attach Object {0}{1}", e.Message, e.StackTrace);
259 }
260 }
261
262 public bool AttachObject(IScenePresence sp, SceneObjectGroup group, uint attachmentPt, bool silent) 185 public bool AttachObject(IScenePresence sp, SceneObjectGroup group, uint attachmentPt, bool silent)
263 { 186 {
264 lock (sp.AttachmentsSyncLock) 187 lock (sp.AttachmentsSyncLock)
@@ -320,7 +243,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments
320 UUID oldAttachmentItemID = attachments[0].GetFromItemID(); 243 UUID oldAttachmentItemID = attachments[0].GetFromItemID();
321 244
322 if (oldAttachmentItemID != UUID.Zero) 245 if (oldAttachmentItemID != UUID.Zero)
323 DetachSingleAttachmentToInv(oldAttachmentItemID, sp); 246 DetachSingleAttachmentToInvInternal(sp, oldAttachmentItemID);
324 else 247 else
325 m_log.WarnFormat( 248 m_log.WarnFormat(
326 "[ATTACHMENTS MODULE]: When detaching existing attachment {0} {1} at point {2} to make way for {3} {4} for {5}, couldn't find the associated item ID to adjust inventory attachment record!", 249 "[ATTACHMENTS MODULE]: When detaching existing attachment {0} {1} at point {2} to make way for {3} {4} for {5}, couldn't find the associated item ID to adjust inventory attachment record!",
@@ -330,7 +253,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments
330 // Add the new attachment to inventory if we don't already have it. 253 // Add the new attachment to inventory if we don't already have it.
331 UUID newAttachmentItemID = group.GetFromItemID(); 254 UUID newAttachmentItemID = group.GetFromItemID();
332 if (newAttachmentItemID == UUID.Zero) 255 if (newAttachmentItemID == UUID.Zero)
333 newAttachmentItemID = AddSceneObjectAsNewAttachmentInInv(sp.ControllingClient, group).ID; 256 newAttachmentItemID = AddSceneObjectAsNewAttachmentInInv(sp, group).ID;
334 257
335 ShowAttachInUserInventory(sp, attachmentPt, newAttachmentItemID, group); 258 ShowAttachInUserInventory(sp, attachmentPt, newAttachmentItemID, group);
336 } 259 }
@@ -339,58 +262,6 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments
339 } 262 }
340 263
341 return true; 264 return true;
342 }
343
344 private void RezMultipleAttachmentsFromInventory(IClientAPI remoteClient, List<KeyValuePair<UUID, uint>> rezlist)
345 {
346 if (!Enabled)
347 return;
348
349 ScenePresence sp;
350 if (m_scene.TryGetScenePresence(remoteClient.AgentId, out sp))
351 RezMultipleAttachmentsFromInventory(sp, rezlist);
352 else
353 m_log.ErrorFormat(
354 "[ATTACHMENTS MODULE]: Could not find presence for client {0} {1} in RezMultipleAttachmentsFromInventory()",
355 remoteClient.Name, remoteClient.AgentId);
356 return;
357 }
358
359 public void RezMultipleAttachmentsFromInventory(IScenePresence sp, List<KeyValuePair<UUID, uint>> rezlist)
360 {
361 if (!Enabled)
362 return;
363
364// m_log.DebugFormat("[ATTACHMENTS MODULE]: Rezzing multiple attachments from inventory for {0}", sp.Name);
365 lock (sp.AttachmentsSyncLock)
366 {
367 foreach (KeyValuePair<UUID, uint> rez in rezlist)
368 {
369 RezSingleAttachmentFromInventory(sp, rez.Key, rez.Value);
370 }
371 }
372 }
373
374 private ISceneEntity RezSingleAttachmentFromInventory(IClientAPI remoteClient, UUID itemID, uint AttachmentPt)
375 {
376 if (!Enabled)
377 return null;
378
379// m_log.DebugFormat(
380// "[ATTACHMENTS MODULE]: Rezzing attachment to point {0} from item {1} for {2}",
381// (AttachmentPoint)AttachmentPt, itemID, remoteClient.Name);
382
383 ScenePresence sp = m_scene.GetScenePresence(remoteClient.AgentId);
384
385 if (sp == null)
386 {
387 m_log.ErrorFormat(
388 "[ATTACHMENTS MODULE]: Could not find presence for client {0} {1} in RezSingleAttachmentFromInventory()",
389 remoteClient.Name, remoteClient.AgentId);
390 return null;
391 }
392
393 return RezSingleAttachmentFromInventory(sp, itemID, AttachmentPt);
394 } 265 }
395 266
396 public ISceneEntity RezSingleAttachmentFromInventory(IScenePresence sp, UUID itemID, uint AttachmentPt) 267 public ISceneEntity RezSingleAttachmentFromInventory(IScenePresence sp, UUID itemID, uint AttachmentPt)
@@ -435,159 +306,24 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments
435 SceneObjectGroup att = RezSingleAttachmentFromInventoryInternal(sp, itemID, UUID.Zero, AttachmentPt); 306 SceneObjectGroup att = RezSingleAttachmentFromInventoryInternal(sp, itemID, UUID.Zero, AttachmentPt);
436 307
437 if (att == null) 308 if (att == null)
438 DetachSingleAttachmentToInv(itemID, sp.ControllingClient); 309 DetachSingleAttachmentToInv(sp, itemID);
439 310
440 return att; 311 return att;
441 } 312 }
442 313
443 private SceneObjectGroup RezSingleAttachmentFromInventoryInternal( 314 public void RezMultipleAttachmentsFromInventory(IScenePresence sp, List<KeyValuePair<UUID, uint>> rezlist)
444 IScenePresence sp, UUID itemID, UUID assetID, uint attachmentPt)
445 {
446 IInventoryAccessModule invAccess = m_scene.RequestModuleInterface<IInventoryAccessModule>();
447 if (invAccess != null)
448 {
449 lock (sp.AttachmentsSyncLock)
450 {
451 SceneObjectGroup objatt;
452
453 if (itemID != UUID.Zero)
454 objatt = invAccess.RezObject(sp.ControllingClient,
455 itemID, Vector3.Zero, Vector3.Zero, UUID.Zero, (byte)1, true,
456 false, false, sp.UUID, true);
457 else
458 objatt = invAccess.RezObject(sp.ControllingClient,
459 null, assetID, Vector3.Zero, Vector3.Zero, UUID.Zero, (byte)1, true,
460 false, false, sp.UUID, true);
461
462 // m_log.DebugFormat(
463 // "[ATTACHMENTS MODULE]: Retrieved single object {0} for attachment to {1} on point {2}",
464 // objatt.Name, remoteClient.Name, AttachmentPt);
465
466 if (objatt != null)
467 {
468 // HasGroupChanged is being set from within RezObject. Ideally it would be set by the caller.
469 objatt.HasGroupChanged = false;
470 bool tainted = false;
471 if (attachmentPt != 0 && attachmentPt != objatt.AttachmentPoint)
472 tainted = true;
473
474 // This will throw if the attachment fails
475 try
476 {
477 AttachObject(sp, objatt, attachmentPt, false);
478 }
479 catch (Exception e)
480 {
481 m_log.ErrorFormat(
482 "[ATTACHMENTS MODULE]: Failed to attach {0} {1} for {2}, exception {3}{4}",
483 objatt.Name, objatt.UUID, sp.Name, e.Message, e.StackTrace);
484
485 // Make sure the object doesn't stick around and bail
486 sp.RemoveAttachment(objatt);
487 m_scene.DeleteSceneObject(objatt, false);
488 return null;
489 }
490
491 if (tainted)
492 objatt.HasGroupChanged = true;
493
494 // Fire after attach, so we don't get messy perms dialogs
495 // 4 == AttachedRez
496 objatt.CreateScriptInstances(0, true, m_scene.DefaultScriptEngine, 4);
497 objatt.ResumeScripts();
498
499 // Do this last so that event listeners have access to all the effects of the attachment
500 m_scene.EventManager.TriggerOnAttach(objatt.LocalId, itemID, sp.UUID);
501
502 return objatt;
503 }
504 else
505 {
506 m_log.WarnFormat(
507 "[ATTACHMENTS MODULE]: Could not retrieve item {0} for attaching to avatar {1} at point {2}",
508 itemID, sp.Name, attachmentPt);
509 }
510 }
511 }
512
513 return null;
514 }
515
516 /// <summary>
517 /// Update the user inventory to reflect an attachment
518 /// </summary>
519 /// <param name="sp"></param>
520 /// <param name="AttachmentPt"></param>
521 /// <param name="itemID"></param>
522 /// <param name="att"></param>
523 private void ShowAttachInUserInventory(
524 IScenePresence sp, uint AttachmentPt, UUID itemID, SceneObjectGroup att)
525 {
526// m_log.DebugFormat(
527// "[USER INVENTORY]: Updating attachment {0} for {1} at {2} using item ID {3}",
528// att.Name, sp.Name, AttachmentPt, itemID);
529
530 if (UUID.Zero == itemID)
531 {
532 m_log.Error("[ATTACHMENTS MODULE]: Unable to save attachment. Error inventory item ID.");
533 return;
534 }
535
536 if (0 == AttachmentPt)
537 {
538 m_log.Error("[ATTACHMENTS MODULE]: Unable to save attachment. Error attachment point.");
539 return;
540 }
541
542 InventoryItemBase item = new InventoryItemBase(itemID, sp.UUID);
543 item = m_scene.InventoryService.GetItem(item);
544 bool changed = sp.Appearance.SetAttachment((int)AttachmentPt, itemID, item.AssetID);
545 if (changed && m_scene.AvatarFactory != null)
546 m_scene.AvatarFactory.QueueAppearanceSave(sp.UUID);
547 }
548
549 public void DetachObject(uint objectLocalID, IClientAPI remoteClient)
550 {
551// m_log.DebugFormat(
552// "[ATTACHMENTS MODULE]: DetachObject() for object {0} on {1}", objectLocalID, remoteClient.Name);
553
554 SceneObjectGroup group = m_scene.GetGroupByPrim(objectLocalID);
555 if (group != null)
556 {
557 DetachSingleAttachmentToInv(group.GetFromItemID(), remoteClient);
558 }
559 }
560
561 public void DetachSingleAttachmentToInv(UUID itemID, IClientAPI remoteClient)
562 { 315 {
563 if (!Enabled) 316 if (!Enabled)
564 return; 317 return;
565 318
566 ScenePresence presence; 319 // m_log.DebugFormat("[ATTACHMENTS MODULE]: Rezzing multiple attachments from inventory for {0}", sp.Name);
567 if (m_scene.TryGetScenePresence(remoteClient.AgentId, out presence)) 320 lock (sp.AttachmentsSyncLock)
568 { 321 {
569 lock (presence.AttachmentsSyncLock) 322 foreach (KeyValuePair<UUID, uint> rez in rezlist)
570 { 323 {
571 // Save avatar attachment information 324 RezSingleAttachmentFromInventory(sp, rez.Key, rez.Value);
572 m_log.Debug("[ATTACHMENTS MODULE]: Detaching from UserID: " + remoteClient.AgentId + ", ItemID: " + itemID);
573
574 bool changed = presence.Appearance.DetachAttachment(itemID);
575 if (changed && m_scene.AvatarFactory != null)
576 m_scene.AvatarFactory.QueueAppearanceSave(remoteClient.AgentId);
577
578 DetachSingleAttachmentToInv(itemID, presence);
579 } 325 }
580 } 326 }
581 }
582
583 private void DetachSingleAttachmentToGround(uint soLocalId, IClientAPI remoteClient)
584 {
585 if (!Enabled)
586 return;
587
588 ScenePresence sp;
589 if (m_scene.TryGetScenePresence(remoteClient.AgentId, out sp))
590 DetachSingleAttachmentToGround(sp, soLocalId);
591 } 327 }
592 328
593 public void DetachSingleAttachmentToGround(IScenePresence sp, uint soLocalId) 329 public void DetachSingleAttachmentToGround(IScenePresence sp, uint soLocalId)
@@ -617,27 +353,27 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments
617 { 353 {
618 if (!m_scene.Permissions.CanRezObject( 354 if (!m_scene.Permissions.CanRezObject(
619 so.PrimCount, sp.UUID, sp.AbsolutePosition)) 355 so.PrimCount, sp.UUID, sp.AbsolutePosition))
620 return; 356 return;
621 357
622 bool changed = sp.Appearance.DetachAttachment(inventoryID); 358 bool changed = sp.Appearance.DetachAttachment(inventoryID);
623 if (changed && m_scene.AvatarFactory != null) 359 if (changed && m_scene.AvatarFactory != null)
624 m_scene.AvatarFactory.QueueAppearanceSave(sp.UUID); 360 m_scene.AvatarFactory.QueueAppearanceSave(sp.UUID);
625 361
626 sp.RemoveAttachment(so); 362 sp.RemoveAttachment(so);
627 363
628 SceneObjectPart rootPart = so.RootPart; 364 SceneObjectPart rootPart = so.RootPart;
629 rootPart.FromItemID = UUID.Zero; 365 rootPart.FromItemID = UUID.Zero;
630 so.AbsolutePosition = sp.AbsolutePosition; 366 so.AbsolutePosition = sp.AbsolutePosition;
631 so.AttachedAvatar = UUID.Zero; 367 so.AttachedAvatar = UUID.Zero;
632 rootPart.SetParentLocalId(0); 368 rootPart.SetParentLocalId(0);
633 so.ClearPartAttachmentData(); 369 so.ClearPartAttachmentData();
634 rootPart.ApplyPhysics(rootPart.GetEffectiveObjectFlags(), rootPart.VolumeDetectActive, m_scene.m_physicalPrim); 370 rootPart.ApplyPhysics(rootPart.GetEffectiveObjectFlags(), rootPart.VolumeDetectActive, m_scene.m_physicalPrim);
635 so.HasGroupChanged = true; 371 so.HasGroupChanged = true;
636 rootPart.Rezzed = DateTime.Now; 372 rootPart.Rezzed = DateTime.Now;
637 rootPart.RemFlag(PrimFlags.TemporaryOnRez); 373 rootPart.RemFlag(PrimFlags.TemporaryOnRez);
638 so.AttachToBackup(); 374 so.AttachToBackup();
639 m_scene.EventManager.TriggerParcelPrimCountTainted(); 375 m_scene.EventManager.TriggerParcelPrimCountTainted();
640 rootPart.ScheduleFullUpdate(); 376 rootPart.ScheduleFullUpdate();
641 rootPart.ClearUndoState(); 377 rootPart.ClearUndoState();
642 378
643 List<UUID> uuids = new List<UUID>(); 379 List<UUID> uuids = new List<UUID>();
@@ -648,46 +384,19 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments
648 384
649 m_scene.EventManager.TriggerOnAttach(so.LocalId, so.UUID, UUID.Zero); 385 m_scene.EventManager.TriggerOnAttach(so.LocalId, so.UUID, UUID.Zero);
650 } 386 }
651
652 // What makes this method odd and unique is it tries to detach using an UUID.... Yay for standards.
653 // To LocalId or UUID, *THAT* is the question. How now Brown UUID??
654 private void DetachSingleAttachmentToInv(UUID itemID, IScenePresence sp)
655 {
656// m_log.DebugFormat("[ATTACHMENTS MODULE]: Detaching item {0} to inventory for {1}", itemID, sp.Name);
657
658 if (itemID == UUID.Zero) // If this happened, someone made a mistake....
659 return;
660
661 // We can NOT use the dictionries here, as we are looking
662 // for an entity by the fromAssetID, which is NOT the prim UUID
663 EntityBase[] detachEntities = m_scene.GetEntities();
664 SceneObjectGroup group;
665 387
388 public void DetachSingleAttachmentToInv(IScenePresence sp, UUID itemID)
389 {
666 lock (sp.AttachmentsSyncLock) 390 lock (sp.AttachmentsSyncLock)
667 { 391 {
668 foreach (EntityBase entity in detachEntities) 392 // Save avatar attachment information
669 { 393 m_log.Debug("[ATTACHMENTS MODULE]: Detaching from UserID: " + sp.UUID + ", ItemID: " + itemID);
670 if (entity is SceneObjectGroup)
671 {
672 group = (SceneObjectGroup)entity;
673 if (group.GetFromItemID() == itemID)
674 {
675 m_scene.EventManager.TriggerOnAttach(group.LocalId, itemID, UUID.Zero);
676 sp.RemoveAttachment(group);
677
678 // Prepare sog for storage
679 group.AttachedAvatar = UUID.Zero;
680 group.RootPart.SetParentLocalId(0);
681 group.IsAttachment = false;
682 group.AbsolutePosition = group.RootPart.AttachedPos;
683 394
684 UpdateKnownItem(sp.ControllingClient, group); 395 bool changed = sp.Appearance.DetachAttachment(itemID);
685 m_scene.DeleteSceneObject(group, false); 396 if (changed && m_scene.AvatarFactory != null)
397 m_scene.AvatarFactory.QueueAppearanceSave(sp.UUID);
686 398
687 return; 399 DetachSingleAttachmentToInvInternal(sp, itemID);
688 }
689 }
690 }
691 } 400 }
692 } 401 }
693 402
@@ -709,7 +418,37 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments
709 sog.AttachmentPoint = attachmentPoint; 418 sog.AttachmentPoint = attachmentPoint;
710 sog.HasGroupChanged = true; 419 sog.HasGroupChanged = true;
711 } 420 }
712 421
422 #endregion
423
424 #region AttachmentModule private methods
425
426 // This is public but is not part of the IAttachmentsModule interface.
427 // RegionCombiner module needs to poke at it to deliver client events.
428 // This breaks the encapsulation of the module and should get fixed somehow.
429 public void SubscribeToClientEvents(IClientAPI client)
430 {
431 client.OnRezSingleAttachmentFromInv += Client_OnRezSingleAttachmentFromInv;
432 client.OnRezMultipleAttachmentsFromInv += Client_OnRezMultipleAttachmentsFromInv;
433 client.OnObjectAttach += Client_OnObjectAttach;
434 client.OnObjectDetach += Client_OnObjectDetach;
435 client.OnDetachAttachmentIntoInv += Client_OnDetachAttachmentIntoInv;
436 client.OnObjectDrop += Client_OnObjectDrop;
437 }
438
439 // This is public but is not part of the IAttachmentsModule interface.
440 // RegionCombiner module needs to poke at it to deliver client events.
441 // This breaks the encapsulation of the module and should get fixed somehow.
442 public void UnsubscribeFromClientEvents(IClientAPI client)
443 {
444 client.OnRezSingleAttachmentFromInv -= Client_OnRezSingleAttachmentFromInv;
445 client.OnRezMultipleAttachmentsFromInv -= Client_OnRezMultipleAttachmentsFromInv;
446 client.OnObjectAttach -= Client_OnObjectAttach;
447 client.OnObjectDetach -= Client_OnObjectDetach;
448 client.OnDetachAttachmentIntoInv -= Client_OnDetachAttachmentIntoInv;
449 client.OnObjectDrop -= Client_OnObjectDrop;
450 }
451
713 /// <summary> 452 /// <summary>
714 /// Update the attachment asset for the new sog details if they have changed. 453 /// Update the attachment asset for the new sog details if they have changed.
715 /// </summary> 454 /// </summary>
@@ -717,9 +456,9 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments
717 /// This is essential for preserving attachment attributes such as permission. Unlike normal scene objects, 456 /// This is essential for preserving attachment attributes such as permission. Unlike normal scene objects,
718 /// these details are not stored on the region. 457 /// these details are not stored on the region.
719 /// </remarks> 458 /// </remarks>
720 /// <param name="remoteClient"></param> 459 /// <param name="sp"></param>
721 /// <param name="grp"></param> 460 /// <param name="grp"></param>
722 private void UpdateKnownItem(IClientAPI remoteClient, SceneObjectGroup grp) 461 private void UpdateKnownItem(IScenePresence sp, SceneObjectGroup grp)
723 { 462 {
724 if (grp.HasGroupChanged || grp.ContainsScripts()) 463 if (grp.HasGroupChanged || grp.ContainsScripts())
725 { 464 {
@@ -729,7 +468,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments
729 468
730 string sceneObjectXml = SceneObjectSerializer.ToOriginalXmlFormat(grp); 469 string sceneObjectXml = SceneObjectSerializer.ToOriginalXmlFormat(grp);
731 470
732 InventoryItemBase item = new InventoryItemBase(grp.GetFromItemID(), remoteClient.AgentId); 471 InventoryItemBase item = new InventoryItemBase(grp.GetFromItemID(), sp.UUID);
733 item = m_scene.InventoryService.GetItem(item); 472 item = m_scene.InventoryService.GetItem(item);
734 473
735 if (item != null) 474 if (item != null)
@@ -739,7 +478,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments
739 grp.GetPartDescription(grp.LocalId), 478 grp.GetPartDescription(grp.LocalId),
740 (sbyte)AssetType.Object, 479 (sbyte)AssetType.Object,
741 Utils.StringToBytes(sceneObjectXml), 480 Utils.StringToBytes(sceneObjectXml),
742 remoteClient.AgentId); 481 sp.UUID);
743 m_scene.AssetService.Store(asset); 482 m_scene.AssetService.Store(asset);
744 483
745 item.AssetID = asset.FullID; 484 item.AssetID = asset.FullID;
@@ -751,8 +490,8 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments
751 m_scene.InventoryService.UpdateItem(item); 490 m_scene.InventoryService.UpdateItem(item);
752 491
753 // this gets called when the agent logs off! 492 // this gets called when the agent logs off!
754 if (remoteClient != null) 493 if (sp.ControllingClient != null)
755 remoteClient.SendInventoryItemCreateUpdate(item, 0); 494 sp.ControllingClient.SendInventoryItemCreateUpdate(item, 0);
756 } 495 }
757 } 496 }
758 else 497 else
@@ -761,8 +500,8 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments
761 "[ATTACHMENTS MODULE]: Don't need to update asset for unchanged attachment {0}, attachpoint {1}", 500 "[ATTACHMENTS MODULE]: Don't need to update asset for unchanged attachment {0}, attachpoint {1}",
762 grp.UUID, grp.AttachmentPoint); 501 grp.UUID, grp.AttachmentPoint);
763 } 502 }
764 } 503 }
765 504
766 /// <summary> 505 /// <summary>
767 /// Attach this scene object to the given avatar. 506 /// Attach this scene object to the given avatar.
768 /// </summary> 507 /// </summary>
@@ -776,19 +515,19 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments
776 /// <param name="attachOffset"></param> 515 /// <param name="attachOffset"></param>
777 /// <param name="silent"></param> 516 /// <param name="silent"></param>
778 private void AttachToAgent( 517 private void AttachToAgent(
779 IScenePresence avatar, SceneObjectGroup so, uint attachmentpoint, Vector3 attachOffset, bool silent) 518 IScenePresence sp, SceneObjectGroup so, uint attachmentpoint, Vector3 attachOffset, bool silent)
780 { 519 {
781// m_log.DebugFormat( 520 // m_log.DebugFormat(
782// "[ATTACHMENTS MODULE]: Adding attachment {0} to avatar {1} in pt {2} pos {3} {4}", 521 // "[ATTACHMENTS MODULE]: Adding attachment {0} to avatar {1} in pt {2} pos {3} {4}",
783// so.Name, avatar.Name, attachmentpoint, attachOffset, so.RootPart.AttachedPos); 522 // so.Name, avatar.Name, attachmentpoint, attachOffset, so.RootPart.AttachedPos);
784 523
785 so.DetachFromBackup(); 524 so.DetachFromBackup();
786 525
787 // Remove from database and parcel prim count 526 // Remove from database and parcel prim count
788 m_scene.DeleteFromStorage(so.UUID); 527 m_scene.DeleteFromStorage(so.UUID);
789 m_scene.EventManager.TriggerParcelPrimCountTainted(); 528 m_scene.EventManager.TriggerParcelPrimCountTainted();
790 529
791 so.AttachedAvatar = avatar.UUID; 530 so.AttachedAvatar = sp.UUID;
792 531
793 if (so.RootPart.PhysActor != null) 532 if (so.RootPart.PhysActor != null)
794 { 533 {
@@ -799,10 +538,10 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments
799 so.AbsolutePosition = attachOffset; 538 so.AbsolutePosition = attachOffset;
800 so.RootPart.AttachedPos = attachOffset; 539 so.RootPart.AttachedPos = attachOffset;
801 so.IsAttachment = true; 540 so.IsAttachment = true;
802 so.RootPart.SetParentLocalId(avatar.LocalId); 541 so.RootPart.SetParentLocalId(sp.LocalId);
803 so.AttachmentPoint = attachmentpoint; 542 so.AttachmentPoint = attachmentpoint;
804 543
805 avatar.AddAttachment(so); 544 sp.AddAttachment(so);
806 545
807 if (!silent) 546 if (!silent)
808 { 547 {
@@ -818,7 +557,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments
818 so.IsSelected = false; // fudge.... 557 so.IsSelected = false; // fudge....
819 so.ScheduleGroupForFullUpdate(); 558 so.ScheduleGroupForFullUpdate();
820 } 559 }
821 560
822 // In case it is later dropped again, don't let 561 // In case it is later dropped again, don't let
823 // it get cleaned up 562 // it get cleaned up
824 so.RootPart.RemFlag(PrimFlags.TemporaryOnRez); 563 so.RootPart.RemFlag(PrimFlags.TemporaryOnRez);
@@ -830,11 +569,11 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments
830 /// <param name="remoteClient"></param> 569 /// <param name="remoteClient"></param>
831 /// <param name="grp"></param> 570 /// <param name="grp"></param>
832 /// <returns>The user inventory item created that holds the attachment.</returns> 571 /// <returns>The user inventory item created that holds the attachment.</returns>
833 private InventoryItemBase AddSceneObjectAsNewAttachmentInInv(IClientAPI remoteClient, SceneObjectGroup grp) 572 private InventoryItemBase AddSceneObjectAsNewAttachmentInInv(IScenePresence sp, SceneObjectGroup grp)
834 { 573 {
835// m_log.DebugFormat( 574 // m_log.DebugFormat(
836// "[ATTACHMENTS MODULE]: Called AddSceneObjectAsAttachment for object {0} {1} for {2}", 575 // "[ATTACHMENTS MODULE]: Called AddSceneObjectAsAttachment for object {0} {1} for {2}",
837// grp.Name, grp.LocalId, remoteClient.Name); 576 // grp.Name, grp.LocalId, remoteClient.Name);
838 577
839 Vector3 inventoryStoredPosition = new Vector3 578 Vector3 inventoryStoredPosition = new Vector3
840 (((grp.AbsolutePosition.X > (int)Constants.RegionSize) 579 (((grp.AbsolutePosition.X > (int)Constants.RegionSize)
@@ -863,14 +602,14 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments
863 grp.GetPartDescription(grp.LocalId), 602 grp.GetPartDescription(grp.LocalId),
864 (sbyte)AssetType.Object, 603 (sbyte)AssetType.Object,
865 Utils.StringToBytes(sceneObjectXml), 604 Utils.StringToBytes(sceneObjectXml),
866 remoteClient.AgentId); 605 sp.UUID);
867 606
868 m_scene.AssetService.Store(asset); 607 m_scene.AssetService.Store(asset);
869 608
870 InventoryItemBase item = new InventoryItemBase(); 609 InventoryItemBase item = new InventoryItemBase();
871 item.CreatorId = grp.RootPart.CreatorID.ToString(); 610 item.CreatorId = grp.RootPart.CreatorID.ToString();
872 item.CreatorData = grp.RootPart.CreatorData; 611 item.CreatorData = grp.RootPart.CreatorData;
873 item.Owner = remoteClient.AgentId; 612 item.Owner = sp.UUID;
874 item.ID = UUID.Random(); 613 item.ID = UUID.Random();
875 item.AssetID = asset.FullID; 614 item.AssetID = asset.FullID;
876 item.Description = asset.Description; 615 item.Description = asset.Description;
@@ -878,13 +617,13 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments
878 item.AssetType = asset.Type; 617 item.AssetType = asset.Type;
879 item.InvType = (int)InventoryType.Object; 618 item.InvType = (int)InventoryType.Object;
880 619
881 InventoryFolderBase folder = m_scene.InventoryService.GetFolderForType(remoteClient.AgentId, AssetType.Object); 620 InventoryFolderBase folder = m_scene.InventoryService.GetFolderForType(sp.UUID, AssetType.Object);
882 if (folder != null) 621 if (folder != null)
883 item.Folder = folder.ID; 622 item.Folder = folder.ID;
884 else // oopsies 623 else // oopsies
885 item.Folder = UUID.Zero; 624 item.Folder = UUID.Zero;
886 625
887 if ((remoteClient.AgentId != grp.RootPart.OwnerID) && m_scene.Permissions.PropagatePermissions()) 626 if ((sp.UUID != grp.RootPart.OwnerID) && m_scene.Permissions.PropagatePermissions())
888 { 627 {
889 item.BasePermissions = grp.RootPart.NextOwnerMask; 628 item.BasePermissions = grp.RootPart.NextOwnerMask;
890 item.CurrentPermissions = grp.RootPart.NextOwnerMask; 629 item.CurrentPermissions = grp.RootPart.NextOwnerMask;
@@ -907,15 +646,290 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments
907 646
908 if (m_scene.AddInventoryItem(item)) 647 if (m_scene.AddInventoryItem(item))
909 { 648 {
910 remoteClient.SendInventoryItemCreateUpdate(item, 0); 649 sp.ControllingClient.SendInventoryItemCreateUpdate(item, 0);
911 } 650 }
912 else 651 else
913 { 652 {
914 if (m_dialogModule != null) 653 if (m_dialogModule != null)
915 m_dialogModule.SendAlertToUser(remoteClient, "Operation failed"); 654 m_dialogModule.SendAlertToUser(sp.ControllingClient, "Operation failed");
916 } 655 }
917 656
918 return item; 657 return item;
919 } 658 }
659
660 // What makes this method odd and unique is it tries to detach using an UUID.... Yay for standards.
661 // To LocalId or UUID, *THAT* is the question. How now Brown UUID??
662 private void DetachSingleAttachmentToInvInternal(IScenePresence sp, UUID itemID)
663 {
664 // m_log.DebugFormat("[ATTACHMENTS MODULE]: Detaching item {0} to inventory for {1}", itemID, sp.Name);
665
666 if (itemID == UUID.Zero) // If this happened, someone made a mistake....
667 return;
668
669 // We can NOT use the dictionries here, as we are looking
670 // for an entity by the fromAssetID, which is NOT the prim UUID
671 EntityBase[] detachEntities = m_scene.GetEntities();
672 SceneObjectGroup group;
673
674 lock (sp.AttachmentsSyncLock)
675 {
676 foreach (EntityBase entity in detachEntities)
677 {
678 if (entity is SceneObjectGroup)
679 {
680 group = (SceneObjectGroup)entity;
681 if (group.GetFromItemID() == itemID)
682 {
683 m_scene.EventManager.TriggerOnAttach(group.LocalId, itemID, UUID.Zero);
684 sp.RemoveAttachment(group);
685
686 // Prepare sog for storage
687 group.AttachedAvatar = UUID.Zero;
688 group.RootPart.SetParentLocalId(0);
689 group.IsAttachment = false;
690 group.AbsolutePosition = group.RootPart.AttachedPos;
691
692 UpdateKnownItem(sp, group);
693 m_scene.DeleteSceneObject(group, false);
694
695 return;
696 }
697 }
698 }
699 }
700 }
701
702 private SceneObjectGroup RezSingleAttachmentFromInventoryInternal(
703 IScenePresence sp, UUID itemID, UUID assetID, uint attachmentPt)
704 {
705 IInventoryAccessModule invAccess = m_scene.RequestModuleInterface<IInventoryAccessModule>();
706 if (invAccess != null)
707 {
708 lock (sp.AttachmentsSyncLock)
709 {
710 SceneObjectGroup objatt;
711
712 if (itemID != UUID.Zero)
713 objatt = invAccess.RezObject(sp.ControllingClient,
714 itemID, Vector3.Zero, Vector3.Zero, UUID.Zero, (byte)1, true,
715 false, false, sp.UUID, true);
716 else
717 objatt = invAccess.RezObject(sp.ControllingClient,
718 null, assetID, Vector3.Zero, Vector3.Zero, UUID.Zero, (byte)1, true,
719 false, false, sp.UUID, true);
720
721 // m_log.DebugFormat(
722 // "[ATTACHMENTS MODULE]: Retrieved single object {0} for attachment to {1} on point {2}",
723 // objatt.Name, remoteClient.Name, AttachmentPt);
724
725 if (objatt != null)
726 {
727 // HasGroupChanged is being set from within RezObject. Ideally it would be set by the caller.
728 objatt.HasGroupChanged = false;
729 bool tainted = false;
730 if (attachmentPt != 0 && attachmentPt != objatt.AttachmentPoint)
731 tainted = true;
732
733 // This will throw if the attachment fails
734 try
735 {
736 AttachObject(sp, objatt, attachmentPt, false);
737 }
738 catch (Exception e)
739 {
740 m_log.ErrorFormat(
741 "[ATTACHMENTS MODULE]: Failed to attach {0} {1} for {2}, exception {3}{4}",
742 objatt.Name, objatt.UUID, sp.Name, e.Message, e.StackTrace);
743
744 // Make sure the object doesn't stick around and bail
745 sp.RemoveAttachment(objatt);
746 m_scene.DeleteSceneObject(objatt, false);
747 return null;
748 }
749
750 if (tainted)
751 objatt.HasGroupChanged = true;
752
753 // Fire after attach, so we don't get messy perms dialogs
754 // 4 == AttachedRez
755 objatt.CreateScriptInstances(0, true, m_scene.DefaultScriptEngine, 4);
756 objatt.ResumeScripts();
757
758 // Do this last so that event listeners have access to all the effects of the attachment
759 m_scene.EventManager.TriggerOnAttach(objatt.LocalId, itemID, sp.UUID);
760
761 return objatt;
762 }
763 else
764 {
765 m_log.WarnFormat(
766 "[ATTACHMENTS MODULE]: Could not retrieve item {0} for attaching to avatar {1} at point {2}",
767 itemID, sp.Name, attachmentPt);
768 }
769 }
770 }
771
772 return null;
773 }
774
775 /// <summary>
776 /// Update the user inventory to reflect an attachment
777 /// </summary>
778 /// <param name="sp"></param>
779 /// <param name="AttachmentPt"></param>
780 /// <param name="itemID"></param>
781 /// <param name="att"></param>
782 private void ShowAttachInUserInventory(IScenePresence sp, uint AttachmentPt, UUID itemID, SceneObjectGroup att)
783 {
784 // m_log.DebugFormat(
785 // "[USER INVENTORY]: Updating attachment {0} for {1} at {2} using item ID {3}",
786 // att.Name, sp.Name, AttachmentPt, itemID);
787
788 if (UUID.Zero == itemID)
789 {
790 m_log.Error("[ATTACHMENTS MODULE]: Unable to save attachment. Error inventory item ID.");
791 return;
792 }
793
794 if (0 == AttachmentPt)
795 {
796 m_log.Error("[ATTACHMENTS MODULE]: Unable to save attachment. Error attachment point.");
797 return;
798 }
799
800 InventoryItemBase item = new InventoryItemBase(itemID, sp.UUID);
801 item = m_scene.InventoryService.GetItem(item);
802 bool changed = sp.Appearance.SetAttachment((int)AttachmentPt, itemID, item.AssetID);
803 if (changed && m_scene.AvatarFactory != null)
804 m_scene.AvatarFactory.QueueAppearanceSave(sp.UUID);
805 }
806
807 #endregion
808
809 #region Client Event Handlers
810
811 private ISceneEntity Client_OnRezSingleAttachmentFromInv(IClientAPI remoteClient, UUID itemID, uint AttachmentPt)
812 {
813 if (!Enabled)
814 return null;
815
816 // m_log.DebugFormat(
817 // "[ATTACHMENTS MODULE]: Rezzing attachment to point {0} from item {1} for {2}",
818 // (AttachmentPoint)AttachmentPt, itemID, remoteClient.Name);
819
820 ScenePresence sp = m_scene.GetScenePresence(remoteClient.AgentId);
821
822 if (sp == null)
823 {
824 m_log.ErrorFormat(
825 "[ATTACHMENTS MODULE]: Could not find presence for client {0} {1} in RezSingleAttachmentFromInventory()",
826 remoteClient.Name, remoteClient.AgentId);
827 return null;
828 }
829
830 return RezSingleAttachmentFromInventory(sp, itemID, AttachmentPt);
831 }
832
833 private void Client_OnRezMultipleAttachmentsFromInv(IClientAPI remoteClient, List<KeyValuePair<UUID, uint>> rezlist)
834 {
835 if (!Enabled)
836 return;
837
838 ScenePresence sp = m_scene.GetScenePresence(remoteClient.AgentId);
839 if (sp != null)
840 RezMultipleAttachmentsFromInventory(sp, rezlist);
841 else
842 m_log.ErrorFormat(
843 "[ATTACHMENTS MODULE]: Could not find presence for client {0} {1} in RezMultipleAttachmentsFromInventory()",
844 remoteClient.Name, remoteClient.AgentId);
845 }
846
847 private void Client_OnObjectAttach(IClientAPI remoteClient, uint objectLocalID, uint AttachmentPt, bool silent)
848 {
849 // m_log.DebugFormat(
850 // "[ATTACHMENTS MODULE]: Attaching object local id {0} to {1} point {2} from ground (silent = {3})",
851 // objectLocalID, remoteClient.Name, AttachmentPt, silent);
852
853 if (!Enabled)
854 return;
855
856 try
857 {
858 ScenePresence sp = m_scene.GetScenePresence(remoteClient.AgentId);
859
860 if (sp == null)
861 {
862 m_log.ErrorFormat(
863 "[ATTACHMENTS MODULE]: Could not find presence for client {0} {1}", remoteClient.Name, remoteClient.AgentId);
864 return;
865 }
866
867 // If we can't take it, we can't attach it!
868 SceneObjectPart part = m_scene.GetSceneObjectPart(objectLocalID);
869 if (part == null)
870 return;
871
872 if (!m_scene.Permissions.CanTakeObject(part.UUID, remoteClient.AgentId))
873 {
874 remoteClient.SendAgentAlertMessage(
875 "You don't have sufficient permissions to attach this object", false);
876
877 return;
878 }
879
880 // TODO: this short circuits multiple attachments functionality in LL viewer 2.1+ and should
881 // be removed when that functionality is implemented in opensim
882 AttachmentPt &= 0x7f;
883
884 // Calls attach with a Zero position
885 if (AttachObject(sp, part.ParentGroup, AttachmentPt, false))
886 {
887 m_scene.EventManager.TriggerOnAttach(objectLocalID, part.ParentGroup.GetFromItemID(), remoteClient.AgentId);
888
889 // Save avatar attachment information
890 m_log.Debug(
891 "[ATTACHMENTS MODULE]: Saving avatar attachment. AgentID: " + remoteClient.AgentId
892 + ", AttachmentPoint: " + AttachmentPt);
893
894 }
895 }
896 catch (Exception e)
897 {
898 m_log.ErrorFormat("[ATTACHMENTS MODULE]: exception upon Attach Object {0}{1}", e.Message, e.StackTrace);
899 }
900 }
901
902 private void Client_OnObjectDetach(uint objectLocalID, IClientAPI remoteClient)
903 {
904 if (!Enabled)
905 return;
906
907 ScenePresence sp = m_scene.GetScenePresence(remoteClient.AgentId);
908 SceneObjectGroup group = m_scene.GetGroupByPrim(objectLocalID);
909 if (sp != null && group != null)
910 DetachSingleAttachmentToInv(sp, group.GetFromItemID());
911 }
912
913 private void Client_OnDetachAttachmentIntoInv(UUID itemID, IClientAPI remoteClient)
914 {
915 if (!Enabled)
916 return;
917
918 ScenePresence sp = m_scene.GetScenePresence(remoteClient.AgentId);
919 if (sp != null)
920 DetachSingleAttachmentToInv(sp, itemID);
921 }
922
923 private void Client_OnObjectDrop(uint soLocalId, IClientAPI remoteClient)
924 {
925 if (!Enabled)
926 return;
927
928 ScenePresence sp = m_scene.GetScenePresence(remoteClient.AgentId);
929 if (sp != null)
930 DetachSingleAttachmentToGround(sp, soLocalId);
931 }
932
933 #endregion
920 } 934 }
921} 935}
diff --git a/OpenSim/Region/CoreModules/Avatar/Attachments/Tests/AttachmentsModuleTests.cs b/OpenSim/Region/CoreModules/Avatar/Attachments/Tests/AttachmentsModuleTests.cs
index 832c6eb..86cfb32 100644
--- a/OpenSim/Region/CoreModules/Avatar/Attachments/Tests/AttachmentsModuleTests.cs
+++ b/OpenSim/Region/CoreModules/Avatar/Attachments/Tests/AttachmentsModuleTests.cs
@@ -209,7 +209,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments.Tests
209 209
210 m_attMod.RezSingleAttachmentFromInventory( 210 m_attMod.RezSingleAttachmentFromInventory(
211 m_presence, attItemId, (uint)AttachmentPoint.Chest); 211 m_presence, attItemId, (uint)AttachmentPoint.Chest);
212 m_attMod.DetachSingleAttachmentToInv(attItemId, m_presence.ControllingClient); 212 m_attMod.DetachSingleAttachmentToInv(m_presence, attItemId);
213 213
214 // Check status on scene presence 214 // Check status on scene presence
215 Assert.That(m_presence.HasAttachments(), Is.False); 215 Assert.That(m_presence.HasAttachments(), Is.False);