diff options
author | Justin Clark-Casey (justincc) | 2011-09-12 21:57:22 +0100 |
---|---|---|
committer | Justin Clark-Casey (justincc) | 2011-09-12 21:57:22 +0100 |
commit | ea0f78c97152d3aa54822487e5343ca2db0b47b9 (patch) | |
tree | 1496c7d6ebbed346209f1513f2affa3f1ba6f62a | |
parent | lock AvatarAppearance.m_attachments when we use it (diff) | |
download | opensim-SC-ea0f78c97152d3aa54822487e5343ca2db0b47b9.zip opensim-SC-ea0f78c97152d3aa54822487e5343ca2db0b47b9.tar.gz opensim-SC-ea0f78c97152d3aa54822487e5343ca2db0b47b9.tar.bz2 opensim-SC-ea0f78c97152d3aa54822487e5343ca2db0b47b9.tar.xz |
Start locking entire add/remove operations on an IScenePresence.AttachmentsSyncLock object
Attach and detach packets are processed asynchronously when received from a viewer.
Bugs like http://opensimulator.org/mantis/view.php?id=5644 indicate that in some situations (such as attaching/detaching entire folders of objects at once), there are race conditions between these threads.
Since multiple data structures need to be updated on attach/detach, it's not enough to lock the individual collections.
Therefore, this commit introduces a new IScenePresence.AttachmentsSyncLock which add/remove operations lock on.
4 files changed, 228 insertions, 175 deletions
diff --git a/OpenSim/Region/CoreModules/Avatar/Attachments/AttachmentsModule.cs b/OpenSim/Region/CoreModules/Avatar/Attachments/AttachmentsModule.cs index 996e2ab..2fa233b 100644 --- a/OpenSim/Region/CoreModules/Avatar/Attachments/AttachmentsModule.cs +++ b/OpenSim/Region/CoreModules/Avatar/Attachments/AttachmentsModule.cs | |||
@@ -240,80 +240,83 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments | |||
240 | 240 | ||
241 | private bool AttachObject(IScenePresence sp, SceneObjectGroup group, uint attachmentPt, bool silent) | 241 | private bool AttachObject(IScenePresence sp, SceneObjectGroup group, uint attachmentPt, bool silent) |
242 | { | 242 | { |
243 | // m_log.DebugFormat( | 243 | lock (sp.AttachmentsSyncLock) |
244 | // "[ATTACHMENTS MODULE]: Attaching object {0} {1} to {2} point {3} from ground (silent = {4})", | ||
245 | // group.Name, group.LocalId, sp.Name, attachmentPt, silent); | ||
246 | |||
247 | if (sp.GetAttachments(attachmentPt).Contains(group)) | ||
248 | { | ||
249 | // m_log.WarnFormat( | ||
250 | // "[ATTACHMENTS MODULE]: Ignoring request to attach {0} {1} to {2} on {3} since it's already attached", | ||
251 | // group.Name, group.LocalId, sp.Name, AttachmentPt); | ||
252 | |||
253 | return false; | ||
254 | } | ||
255 | |||
256 | Vector3 attachPos = group.AbsolutePosition; | ||
257 | |||
258 | // TODO: this short circuits multiple attachments functionality in LL viewer 2.1+ and should | ||
259 | // be removed when that functionality is implemented in opensim | ||
260 | attachmentPt &= 0x7f; | ||
261 | |||
262 | // If the attachment point isn't the same as the one previously used | ||
263 | // set it's offset position = 0 so that it appears on the attachment point | ||
264 | // and not in a weird location somewhere unknown. | ||
265 | if (attachmentPt != 0 && attachmentPt != group.AttachmentPoint) | ||
266 | { | ||
267 | attachPos = Vector3.Zero; | ||
268 | } | ||
269 | |||
270 | // AttachmentPt 0 means the client chose to 'wear' the attachment. | ||
271 | if (attachmentPt == 0) | ||
272 | { | ||
273 | // Check object for stored attachment point | ||
274 | attachmentPt = group.AttachmentPoint; | ||
275 | } | ||
276 | |||
277 | // if we still didn't find a suitable attachment point....... | ||
278 | if (attachmentPt == 0) | ||
279 | { | 244 | { |
280 | // Stick it on left hand with Zero Offset from the attachment point. | 245 | // m_log.DebugFormat( |
281 | attachmentPt = (uint)AttachmentPoint.LeftHand; | 246 | // "[ATTACHMENTS MODULE]: Attaching object {0} {1} to {2} point {3} from ground (silent = {4})", |
282 | attachPos = Vector3.Zero; | 247 | // group.Name, group.LocalId, sp.Name, attachmentPt, silent); |
283 | } | 248 | |
284 | 249 | if (sp.GetAttachments(attachmentPt).Contains(group)) | |
285 | group.AttachmentPoint = attachmentPt; | ||
286 | group.AbsolutePosition = attachPos; | ||
287 | |||
288 | // We also don't want to do any of the inventory operations for an NPC. | ||
289 | if (sp.PresenceType != PresenceType.Npc) | ||
290 | { | ||
291 | // Remove any previous attachments | ||
292 | List<SceneObjectGroup> attachments = sp.GetAttachments(attachmentPt); | ||
293 | |||
294 | // At the moment we can only deal with a single attachment | ||
295 | if (attachments.Count != 0) | ||
296 | { | 250 | { |
297 | UUID oldAttachmentItemID = attachments[0].GetFromItemID(); | 251 | // m_log.WarnFormat( |
252 | // "[ATTACHMENTS MODULE]: Ignoring request to attach {0} {1} to {2} on {3} since it's already attached", | ||
253 | // group.Name, group.LocalId, sp.Name, AttachmentPt); | ||
298 | 254 | ||
299 | if (oldAttachmentItemID != UUID.Zero) | 255 | return false; |
300 | DetachSingleAttachmentToInv(oldAttachmentItemID, sp); | ||
301 | else | ||
302 | m_log.WarnFormat( | ||
303 | "[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!", | ||
304 | attachments[0].Name, attachments[0].LocalId, attachmentPt, group.Name, group.LocalId, sp.Name); | ||
305 | } | 256 | } |
306 | |||
307 | // Add the new attachment to inventory if we don't already have it. | ||
308 | UUID newAttachmentItemID = group.GetFromItemID(); | ||
309 | if (newAttachmentItemID == UUID.Zero) | ||
310 | newAttachmentItemID = AddSceneObjectAsNewAttachmentInInv(sp.ControllingClient, group).ID; | ||
311 | 257 | ||
312 | ShowAttachInUserInventory(sp, attachmentPt, newAttachmentItemID, group); | 258 | Vector3 attachPos = group.AbsolutePosition; |
259 | |||
260 | // TODO: this short circuits multiple attachments functionality in LL viewer 2.1+ and should | ||
261 | // be removed when that functionality is implemented in opensim | ||
262 | attachmentPt &= 0x7f; | ||
263 | |||
264 | // If the attachment point isn't the same as the one previously used | ||
265 | // set it's offset position = 0 so that it appears on the attachment point | ||
266 | // and not in a weird location somewhere unknown. | ||
267 | if (attachmentPt != 0 && attachmentPt != group.AttachmentPoint) | ||
268 | { | ||
269 | attachPos = Vector3.Zero; | ||
270 | } | ||
271 | |||
272 | // AttachmentPt 0 means the client chose to 'wear' the attachment. | ||
273 | if (attachmentPt == 0) | ||
274 | { | ||
275 | // Check object for stored attachment point | ||
276 | attachmentPt = group.AttachmentPoint; | ||
277 | } | ||
278 | |||
279 | // if we still didn't find a suitable attachment point....... | ||
280 | if (attachmentPt == 0) | ||
281 | { | ||
282 | // Stick it on left hand with Zero Offset from the attachment point. | ||
283 | attachmentPt = (uint)AttachmentPoint.LeftHand; | ||
284 | attachPos = Vector3.Zero; | ||
285 | } | ||
286 | |||
287 | group.AttachmentPoint = attachmentPt; | ||
288 | group.AbsolutePosition = attachPos; | ||
289 | |||
290 | // We also don't want to do any of the inventory operations for an NPC. | ||
291 | if (sp.PresenceType != PresenceType.Npc) | ||
292 | { | ||
293 | // Remove any previous attachments | ||
294 | List<SceneObjectGroup> attachments = sp.GetAttachments(attachmentPt); | ||
295 | |||
296 | // At the moment we can only deal with a single attachment | ||
297 | if (attachments.Count != 0) | ||
298 | { | ||
299 | UUID oldAttachmentItemID = attachments[0].GetFromItemID(); | ||
300 | |||
301 | if (oldAttachmentItemID != UUID.Zero) | ||
302 | DetachSingleAttachmentToInv(oldAttachmentItemID, sp); | ||
303 | else | ||
304 | m_log.WarnFormat( | ||
305 | "[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!", | ||
306 | attachments[0].Name, attachments[0].LocalId, attachmentPt, group.Name, group.LocalId, sp.Name); | ||
307 | } | ||
308 | |||
309 | // Add the new attachment to inventory if we don't already have it. | ||
310 | UUID newAttachmentItemID = group.GetFromItemID(); | ||
311 | if (newAttachmentItemID == UUID.Zero) | ||
312 | newAttachmentItemID = AddSceneObjectAsNewAttachmentInInv(sp.ControllingClient, group).ID; | ||
313 | |||
314 | ShowAttachInUserInventory(sp, attachmentPt, newAttachmentItemID, group); | ||
315 | } | ||
316 | |||
317 | AttachToAgent(sp, group, attachmentPt, attachPos, silent); | ||
313 | } | 318 | } |
314 | 319 | ||
315 | AttachToAgent(sp, group, attachmentPt, attachPos, silent); | ||
316 | |||
317 | return true; | 320 | return true; |
318 | } | 321 | } |
319 | 322 | ||
@@ -322,14 +325,26 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments | |||
322 | RezMultipleAttachmentsFromInvPacket.HeaderDataBlock header, | 325 | RezMultipleAttachmentsFromInvPacket.HeaderDataBlock header, |
323 | RezMultipleAttachmentsFromInvPacket.ObjectDataBlock[] objects) | 326 | RezMultipleAttachmentsFromInvPacket.ObjectDataBlock[] objects) |
324 | { | 327 | { |
325 | foreach (RezMultipleAttachmentsFromInvPacket.ObjectDataBlock obj in objects) | 328 | ScenePresence sp = m_scene.GetScenePresence(remoteClient.AgentId); |
329 | |||
330 | if (sp == null) | ||
326 | { | 331 | { |
327 | RezSingleAttachmentFromInventory(remoteClient, obj.ItemID, obj.AttachmentPt); | 332 | m_log.ErrorFormat( |
333 | "[ATTACHMENTS MODULE]: Could not find presence for client {0} {1} in RezMultipleAttachmentsFromInventory()", | ||
334 | remoteClient.Name, remoteClient.AgentId); | ||
335 | return; | ||
336 | } | ||
337 | |||
338 | lock (sp.AttachmentsSyncLock) | ||
339 | { | ||
340 | foreach (RezMultipleAttachmentsFromInvPacket.ObjectDataBlock obj in objects) | ||
341 | { | ||
342 | RezSingleAttachmentFromInventory(sp, obj.ItemID, obj.AttachmentPt); | ||
343 | } | ||
328 | } | 344 | } |
329 | } | 345 | } |
330 | 346 | ||
331 | public ISceneEntity RezSingleAttachmentFromInventory( | 347 | public ISceneEntity RezSingleAttachmentFromInventory(IClientAPI remoteClient, UUID itemID, uint AttachmentPt) |
332 | IClientAPI remoteClient, UUID itemID, uint AttachmentPt) | ||
333 | { | 348 | { |
334 | // m_log.DebugFormat( | 349 | // m_log.DebugFormat( |
335 | // "[ATTACHMENTS MODULE]: Rezzing attachment to point {0} from item {1} for {2}", | 350 | // "[ATTACHMENTS MODULE]: Rezzing attachment to point {0} from item {1} for {2}", |
@@ -344,7 +359,12 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments | |||
344 | remoteClient.Name, remoteClient.AgentId); | 359 | remoteClient.Name, remoteClient.AgentId); |
345 | return null; | 360 | return null; |
346 | } | 361 | } |
347 | 362 | ||
363 | return RezSingleAttachmentFromInventory(sp, itemID, AttachmentPt); | ||
364 | } | ||
365 | |||
366 | public ISceneEntity RezSingleAttachmentFromInventory(ScenePresence sp, UUID itemID, uint AttachmentPt) | ||
367 | { | ||
348 | // TODO: this short circuits multiple attachments functionality in LL viewer 2.1+ and should | 368 | // TODO: this short circuits multiple attachments functionality in LL viewer 2.1+ and should |
349 | // be removed when that functionality is implemented in opensim | 369 | // be removed when that functionality is implemented in opensim |
350 | AttachmentPt &= 0x7f; | 370 | AttachmentPt &= 0x7f; |
@@ -363,65 +383,68 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments | |||
363 | IInventoryAccessModule invAccess = m_scene.RequestModuleInterface<IInventoryAccessModule>(); | 383 | IInventoryAccessModule invAccess = m_scene.RequestModuleInterface<IInventoryAccessModule>(); |
364 | if (invAccess != null) | 384 | if (invAccess != null) |
365 | { | 385 | { |
366 | SceneObjectGroup objatt; | 386 | lock (sp.AttachmentsSyncLock) |
367 | |||
368 | if (itemID != UUID.Zero) | ||
369 | objatt = invAccess.RezObject(sp.ControllingClient, | ||
370 | itemID, Vector3.Zero, Vector3.Zero, UUID.Zero, (byte)1, true, | ||
371 | false, false, sp.UUID, true); | ||
372 | else | ||
373 | objatt = invAccess.RezObject(sp.ControllingClient, | ||
374 | null, assetID, Vector3.Zero, Vector3.Zero, UUID.Zero, (byte)1, true, | ||
375 | false, false, sp.UUID, true); | ||
376 | |||
377 | // m_log.DebugFormat( | ||
378 | // "[ATTACHMENTS MODULE]: Retrieved single object {0} for attachment to {1} on point {2}", | ||
379 | // objatt.Name, remoteClient.Name, AttachmentPt); | ||
380 | |||
381 | if (objatt != null) | ||
382 | { | 387 | { |
383 | // HasGroupChanged is being set from within RezObject. Ideally it would be set by the caller. | 388 | SceneObjectGroup objatt; |
384 | objatt.HasGroupChanged = false; | 389 | |
385 | bool tainted = false; | 390 | if (itemID != UUID.Zero) |
386 | if (attachmentPt != 0 && attachmentPt != objatt.AttachmentPoint) | 391 | objatt = invAccess.RezObject(sp.ControllingClient, |
387 | tainted = true; | 392 | itemID, Vector3.Zero, Vector3.Zero, UUID.Zero, (byte)1, true, |
388 | 393 | false, false, sp.UUID, true); | |
389 | // This will throw if the attachment fails | 394 | else |
390 | try | 395 | objatt = invAccess.RezObject(sp.ControllingClient, |
396 | null, assetID, Vector3.Zero, Vector3.Zero, UUID.Zero, (byte)1, true, | ||
397 | false, false, sp.UUID, true); | ||
398 | |||
399 | // m_log.DebugFormat( | ||
400 | // "[ATTACHMENTS MODULE]: Retrieved single object {0} for attachment to {1} on point {2}", | ||
401 | // objatt.Name, remoteClient.Name, AttachmentPt); | ||
402 | |||
403 | if (objatt != null) | ||
391 | { | 404 | { |
392 | AttachObject(sp, objatt, attachmentPt, false); | 405 | // HasGroupChanged is being set from within RezObject. Ideally it would be set by the caller. |
406 | objatt.HasGroupChanged = false; | ||
407 | bool tainted = false; | ||
408 | if (attachmentPt != 0 && attachmentPt != objatt.AttachmentPoint) | ||
409 | tainted = true; | ||
410 | |||
411 | // This will throw if the attachment fails | ||
412 | try | ||
413 | { | ||
414 | AttachObject(sp, objatt, attachmentPt, false); | ||
415 | } | ||
416 | catch (Exception e) | ||
417 | { | ||
418 | m_log.ErrorFormat( | ||
419 | "[ATTACHMENTS MODULE]: Failed to attach {0} {1} for {2}, exception {3}{4}", | ||
420 | objatt.Name, objatt.UUID, sp.Name, e.Message, e.StackTrace); | ||
421 | |||
422 | // Make sure the object doesn't stick around and bail | ||
423 | sp.RemoveAttachment(objatt); | ||
424 | m_scene.DeleteSceneObject(objatt, false); | ||
425 | return null; | ||
426 | } | ||
427 | |||
428 | if (tainted) | ||
429 | objatt.HasGroupChanged = true; | ||
430 | |||
431 | // Fire after attach, so we don't get messy perms dialogs | ||
432 | // 4 == AttachedRez | ||
433 | objatt.CreateScriptInstances(0, true, m_scene.DefaultScriptEngine, 4); | ||
434 | objatt.ResumeScripts(); | ||
435 | |||
436 | // Do this last so that event listeners have access to all the effects of the attachment | ||
437 | m_scene.EventManager.TriggerOnAttach(objatt.LocalId, itemID, sp.UUID); | ||
438 | |||
439 | return objatt; | ||
393 | } | 440 | } |
394 | catch (Exception e) | 441 | else |
395 | { | 442 | { |
396 | m_log.ErrorFormat( | 443 | m_log.WarnFormat( |
397 | "[ATTACHMENTS MODULE]: Failed to attach {0} {1} for {2}, exception {3}{4}", | 444 | "[ATTACHMENTS MODULE]: Could not retrieve item {0} for attaching to avatar {1} at point {2}", |
398 | objatt.Name, objatt.UUID, sp.Name, e.Message, e.StackTrace); | 445 | itemID, sp.Name, attachmentPt); |
399 | |||
400 | // Make sure the object doesn't stick around and bail | ||
401 | sp.RemoveAttachment(objatt); | ||
402 | m_scene.DeleteSceneObject(objatt, false); | ||
403 | return null; | ||
404 | } | 446 | } |
405 | |||
406 | if (tainted) | ||
407 | objatt.HasGroupChanged = true; | ||
408 | |||
409 | // Fire after attach, so we don't get messy perms dialogs | ||
410 | // 4 == AttachedRez | ||
411 | objatt.CreateScriptInstances(0, true, m_scene.DefaultScriptEngine, 4); | ||
412 | objatt.ResumeScripts(); | ||
413 | |||
414 | // Do this last so that event listeners have access to all the effects of the attachment | ||
415 | m_scene.EventManager.TriggerOnAttach(objatt.LocalId, itemID, sp.UUID); | ||
416 | } | 447 | } |
417 | else | ||
418 | { | ||
419 | m_log.WarnFormat( | ||
420 | "[ATTACHMENTS MODULE]: Could not retrieve item {0} for attaching to avatar {1} at point {2}", | ||
421 | itemID, sp.Name, attachmentPt); | ||
422 | } | ||
423 | |||
424 | return objatt; | ||
425 | } | 448 | } |
426 | 449 | ||
427 | return null; | 450 | return null; |
@@ -474,14 +497,17 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments | |||
474 | ScenePresence presence; | 497 | ScenePresence presence; |
475 | if (m_scene.TryGetScenePresence(remoteClient.AgentId, out presence)) | 498 | if (m_scene.TryGetScenePresence(remoteClient.AgentId, out presence)) |
476 | { | 499 | { |
477 | // Save avatar attachment information | 500 | lock (presence.AttachmentsSyncLock) |
478 | m_log.Debug("[ATTACHMENTS MODULE]: Detaching from UserID: " + remoteClient.AgentId + ", ItemID: " + itemID); | 501 | { |
479 | 502 | // Save avatar attachment information | |
480 | bool changed = presence.Appearance.DetachAttachment(itemID); | 503 | m_log.Debug("[ATTACHMENTS MODULE]: Detaching from UserID: " + remoteClient.AgentId + ", ItemID: " + itemID); |
481 | if (changed && m_scene.AvatarFactory != null) | ||
482 | m_scene.AvatarFactory.QueueAppearanceSave(remoteClient.AgentId); | ||
483 | 504 | ||
484 | DetachSingleAttachmentToInv(itemID, presence); | 505 | bool changed = presence.Appearance.DetachAttachment(itemID); |
506 | if (changed && m_scene.AvatarFactory != null) | ||
507 | m_scene.AvatarFactory.QueueAppearanceSave(remoteClient.AgentId); | ||
508 | |||
509 | DetachSingleAttachmentToInv(itemID, presence); | ||
510 | } | ||
485 | } | 511 | } |
486 | } | 512 | } |
487 | 513 | ||
@@ -508,24 +534,27 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments | |||
508 | ScenePresence presence; | 534 | ScenePresence presence; |
509 | if (m_scene.TryGetScenePresence(remoteClient.AgentId, out presence)) | 535 | if (m_scene.TryGetScenePresence(remoteClient.AgentId, out presence)) |
510 | { | 536 | { |
511 | if (!m_scene.Permissions.CanRezObject( | 537 | lock (presence.AttachmentsSyncLock) |
512 | so.PrimCount, remoteClient.AgentId, presence.AbsolutePosition)) | 538 | { |
513 | return; | 539 | if (!m_scene.Permissions.CanRezObject( |
540 | so.PrimCount, remoteClient.AgentId, presence.AbsolutePosition)) | ||
541 | return; | ||
514 | 542 | ||
515 | bool changed = presence.Appearance.DetachAttachment(inventoryID); | 543 | bool changed = presence.Appearance.DetachAttachment(inventoryID); |
516 | if (changed && m_scene.AvatarFactory != null) | 544 | if (changed && m_scene.AvatarFactory != null) |
517 | m_scene.AvatarFactory.QueueAppearanceSave(remoteClient.AgentId); | 545 | m_scene.AvatarFactory.QueueAppearanceSave(remoteClient.AgentId); |
518 | 546 | ||
519 | presence.RemoveAttachment(so); | 547 | presence.RemoveAttachment(so); |
520 | DetachSceneObjectToGround(so, presence); | 548 | DetachSceneObjectToGround(so, presence); |
521 | 549 | ||
522 | List<UUID> uuids = new List<UUID>(); | 550 | List<UUID> uuids = new List<UUID>(); |
523 | uuids.Add(inventoryID); | 551 | uuids.Add(inventoryID); |
524 | m_scene.InventoryService.DeleteItems(remoteClient.AgentId, uuids); | 552 | m_scene.InventoryService.DeleteItems(remoteClient.AgentId, uuids); |
525 | remoteClient.SendRemoveInventoryItem(inventoryID); | 553 | remoteClient.SendRemoveInventoryItem(inventoryID); |
526 | } | 554 | } |
527 | 555 | ||
528 | m_scene.EventManager.TriggerOnAttach(so.LocalId, so.UUID, UUID.Zero); | 556 | m_scene.EventManager.TriggerOnAttach(so.LocalId, so.UUID, UUID.Zero); |
557 | } | ||
529 | } | 558 | } |
530 | 559 | ||
531 | /// <summary> | 560 | /// <summary> |
@@ -567,37 +596,40 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments | |||
567 | EntityBase[] detachEntities = m_scene.GetEntities(); | 596 | EntityBase[] detachEntities = m_scene.GetEntities(); |
568 | SceneObjectGroup group; | 597 | SceneObjectGroup group; |
569 | 598 | ||
570 | foreach (EntityBase entity in detachEntities) | 599 | lock (sp.AttachmentsSyncLock) |
571 | { | 600 | { |
572 | if (entity is SceneObjectGroup) | 601 | foreach (EntityBase entity in detachEntities) |
573 | { | 602 | { |
574 | group = (SceneObjectGroup)entity; | 603 | if (entity is SceneObjectGroup) |
575 | if (group.GetFromItemID() == itemID) | ||
576 | { | 604 | { |
577 | m_scene.EventManager.TriggerOnAttach(group.LocalId, itemID, UUID.Zero); | 605 | group = (SceneObjectGroup)entity; |
578 | sp.RemoveAttachment(group); | 606 | if (group.GetFromItemID() == itemID) |
579 | 607 | { | |
580 | // Prepare sog for storage | 608 | m_scene.EventManager.TriggerOnAttach(group.LocalId, itemID, UUID.Zero); |
581 | group.AttachedAvatar = UUID.Zero; | 609 | sp.RemoveAttachment(group); |
582 | 610 | ||
583 | group.ForEachPart( | 611 | // Prepare sog for storage |
584 | delegate(SceneObjectPart part) | 612 | group.AttachedAvatar = UUID.Zero; |
585 | { | 613 | |
586 | // If there are any scripts, | 614 | group.ForEachPart( |
587 | // then always trigger a new object and state persistence in UpdateKnownItem() | 615 | delegate(SceneObjectPart part) |
588 | if (part.Inventory.ContainsScripts()) | 616 | { |
589 | group.HasGroupChanged = true; | 617 | // If there are any scripts, |
590 | } | 618 | // then always trigger a new object and state persistence in UpdateKnownItem() |
591 | ); | 619 | if (part.Inventory.ContainsScripts()) |
592 | 620 | group.HasGroupChanged = true; | |
593 | group.RootPart.SetParentLocalId(0); | 621 | } |
594 | group.IsAttachment = false; | 622 | ); |
595 | group.AbsolutePosition = group.RootPart.AttachedPos; | 623 | |
596 | 624 | group.RootPart.SetParentLocalId(0); | |
597 | UpdateKnownItem(sp.ControllingClient, group, group.GetFromItemID(), group.OwnerID); | 625 | group.IsAttachment = false; |
598 | m_scene.DeleteSceneObject(group, false); | 626 | group.AbsolutePosition = group.RootPart.AttachedPos; |
599 | 627 | ||
600 | return; | 628 | UpdateKnownItem(sp.ControllingClient, group, group.GetFromItemID(), group.OwnerID); |
629 | m_scene.DeleteSceneObject(group, false); | ||
630 | |||
631 | return; | ||
632 | } | ||
601 | } | 633 | } |
602 | } | 634 | } |
603 | } | 635 | } |
@@ -829,4 +861,4 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments | |||
829 | return item; | 861 | return item; |
830 | } | 862 | } |
831 | } | 863 | } |
832 | } | 864 | } \ No newline at end of file |
diff --git a/OpenSim/Region/Framework/Interfaces/IAttachmentsModule.cs b/OpenSim/Region/Framework/Interfaces/IAttachmentsModule.cs index 73d15a5..e6ac6b5 100644 --- a/OpenSim/Region/Framework/Interfaces/IAttachmentsModule.cs +++ b/OpenSim/Region/Framework/Interfaces/IAttachmentsModule.cs | |||
@@ -88,6 +88,15 @@ namespace OpenSim.Region.Framework.Interfaces | |||
88 | ISceneEntity RezSingleAttachmentFromInventory(IClientAPI remoteClient, UUID itemID, uint AttachmentPt); | 88 | ISceneEntity RezSingleAttachmentFromInventory(IClientAPI remoteClient, UUID itemID, uint AttachmentPt); |
89 | 89 | ||
90 | /// <summary> | 90 | /// <summary> |
91 | /// Rez an attachment from user inventory and change inventory status to match. | ||
92 | /// </summary> | ||
93 | /// <param name="sp"></param> | ||
94 | /// <param name="itemID"></param> | ||
95 | /// <param name="AttachmentPt"></param> | ||
96 | /// <returns>The scene object that was attached. Null if the scene object could not be found</returns> | ||
97 | ISceneEntity RezSingleAttachmentFromInventory(ScenePresence sp, UUID itemID, uint AttachmentPt); | ||
98 | |||
99 | /// <summary> | ||
91 | /// Rez multiple attachments from a user's inventory | 100 | /// Rez multiple attachments from a user's inventory |
92 | /// </summary> | 101 | /// </summary> |
93 | /// <param name="remoteClient"></param> | 102 | /// <param name="remoteClient"></param> |
diff --git a/OpenSim/Region/Framework/Interfaces/IScenePresence.cs b/OpenSim/Region/Framework/Interfaces/IScenePresence.cs index 8913133..95688ab 100644 --- a/OpenSim/Region/Framework/Interfaces/IScenePresence.cs +++ b/OpenSim/Region/Framework/Interfaces/IScenePresence.cs | |||
@@ -61,6 +61,14 @@ namespace OpenSim.Region.Framework.Interfaces | |||
61 | AvatarAppearance Appearance { get; set; } | 61 | AvatarAppearance Appearance { get; set; } |
62 | 62 | ||
63 | /// <summary> | 63 | /// <summary> |
64 | /// The AttachmentsModule synchronizes on this to avoid race conditions between commands to add and remove attachments. | ||
65 | /// </summary> | ||
66 | /// <remarks> | ||
67 | /// All add and remove attachment operations must synchronize on this for the lifetime of their operations. | ||
68 | /// </remarks> | ||
69 | Object AttachmentsSyncLock { get; } | ||
70 | |||
71 | /// <summary> | ||
64 | /// The scene objects attached to this avatar. | 72 | /// The scene objects attached to this avatar. |
65 | /// </summary> | 73 | /// </summary> |
66 | /// <returns> | 74 | /// <returns> |
diff --git a/OpenSim/Region/Framework/Scenes/ScenePresence.cs b/OpenSim/Region/Framework/Scenes/ScenePresence.cs index d65d78d..86e1e11 100644 --- a/OpenSim/Region/Framework/Scenes/ScenePresence.cs +++ b/OpenSim/Region/Framework/Scenes/ScenePresence.cs | |||
@@ -120,6 +120,8 @@ namespace OpenSim.Region.Framework.Scenes | |||
120 | /// </remarks> | 120 | /// </remarks> |
121 | protected List<SceneObjectGroup> m_attachments = new List<SceneObjectGroup>(); | 121 | protected List<SceneObjectGroup> m_attachments = new List<SceneObjectGroup>(); |
122 | 122 | ||
123 | public Object AttachmentsSyncLock { get; private set; } | ||
124 | |||
123 | private Dictionary<UUID, ScriptControllers> scriptedcontrols = new Dictionary<UUID, ScriptControllers>(); | 125 | private Dictionary<UUID, ScriptControllers> scriptedcontrols = new Dictionary<UUID, ScriptControllers>(); |
124 | private ScriptControlled IgnoredControls = ScriptControlled.CONTROL_ZERO; | 126 | private ScriptControlled IgnoredControls = ScriptControlled.CONTROL_ZERO; |
125 | private ScriptControlled LastCommands = ScriptControlled.CONTROL_ZERO; | 127 | private ScriptControlled LastCommands = ScriptControlled.CONTROL_ZERO; |
@@ -709,6 +711,8 @@ namespace OpenSim.Region.Framework.Scenes | |||
709 | public ScenePresence( | 711 | public ScenePresence( |
710 | IClientAPI client, Scene world, RegionInfo reginfo, AvatarAppearance appearance, PresenceType type) | 712 | IClientAPI client, Scene world, RegionInfo reginfo, AvatarAppearance appearance, PresenceType type) |
711 | { | 713 | { |
714 | AttachmentsSyncLock = new Object(); | ||
715 | |||
712 | m_sendCourseLocationsMethod = SendCoarseLocationsDefault; | 716 | m_sendCourseLocationsMethod = SendCoarseLocationsDefault; |
713 | m_sceneViewer = new SceneViewer(this); | 717 | m_sceneViewer = new SceneViewer(this); |
714 | m_animator = new ScenePresenceAnimator(this); | 718 | m_animator = new ScenePresenceAnimator(this); |