aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/CoreModules/Avatar/Attachments
diff options
context:
space:
mode:
authorDavid Walter Seikel2016-11-03 21:44:39 +1000
committerDavid Walter Seikel2016-11-03 21:44:39 +1000
commit134f86e8d5c414409631b25b8c6f0ee45fbd8631 (patch)
tree216b89d3fb89acfb81be1e440c25c41ab09fa96d /OpenSim/Region/CoreModules/Avatar/Attachments
parentMore changing to production grid. Double oops. (diff)
downloadopensim-SC_OLD-134f86e8d5c414409631b25b8c6f0ee45fbd8631.zip
opensim-SC_OLD-134f86e8d5c414409631b25b8c6f0ee45fbd8631.tar.gz
opensim-SC_OLD-134f86e8d5c414409631b25b8c6f0ee45fbd8631.tar.bz2
opensim-SC_OLD-134f86e8d5c414409631b25b8c6f0ee45fbd8631.tar.xz
Initial update to OpenSim 0.8.2.1 source code.
Diffstat (limited to 'OpenSim/Region/CoreModules/Avatar/Attachments')
-rw-r--r--OpenSim/Region/CoreModules/Avatar/Attachments/AttachmentsModule.cs792
-rw-r--r--OpenSim/Region/CoreModules/Avatar/Attachments/Tests/AttachmentsModuleTests.cs430
2 files changed, 908 insertions, 314 deletions
diff --git a/OpenSim/Region/CoreModules/Avatar/Attachments/AttachmentsModule.cs b/OpenSim/Region/CoreModules/Avatar/Attachments/AttachmentsModule.cs
index 58ed554..2f67c4e 100644
--- a/OpenSim/Region/CoreModules/Avatar/Attachments/AttachmentsModule.cs
+++ b/OpenSim/Region/CoreModules/Avatar/Attachments/AttachmentsModule.cs
@@ -29,6 +29,7 @@ using System;
29using System.Collections.Generic; 29using System.Collections.Generic;
30using System.Reflection; 30using System.Reflection;
31using System.IO; 31using System.IO;
32using System.Threading;
32using System.Xml; 33using System.Xml;
33using log4net; 34using log4net;
34using Mono.Addins; 35using Mono.Addins;
@@ -48,6 +49,17 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments
48 { 49 {
49 #region INonSharedRegionModule 50 #region INonSharedRegionModule
50 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); 51 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
52
53 public int DebugLevel { get; set; }
54
55 /// <summary>
56 /// Period to sleep per 100 prims in order to avoid CPU spikes when an avatar with many attachments logs in/changes
57 /// outfit or many avatars with a medium levels of attachments login/change outfit simultaneously.
58 /// </summary>
59 /// <remarks>
60 /// A value of 0 will apply no pause. The pause is specified in milliseconds.
61 /// </remarks>
62 public int ThrottlePer100PrimsRezzed { get; set; }
51 63
52 private Scene m_scene; 64 private Scene m_scene;
53 private IInventoryAccessModule m_invAccessModule; 65 private IInventoryAccessModule m_invAccessModule;
@@ -64,21 +76,127 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments
64 { 76 {
65 IConfig config = source.Configs["Attachments"]; 77 IConfig config = source.Configs["Attachments"];
66 if (config != null) 78 if (config != null)
79 {
67 Enabled = config.GetBoolean("Enabled", true); 80 Enabled = config.GetBoolean("Enabled", true);
81
82 ThrottlePer100PrimsRezzed = config.GetInt("ThrottlePer100PrimsRezzed", 0);
83 }
68 else 84 else
85 {
69 Enabled = true; 86 Enabled = true;
87 }
70 } 88 }
71 89
72 public void AddRegion(Scene scene) 90 public void AddRegion(Scene scene)
73 { 91 {
74 m_scene = scene; 92 m_scene = scene;
75 m_scene.RegisterModuleInterface<IAttachmentsModule>(this);
76
77 if (Enabled) 93 if (Enabled)
94 {
95 // Only register module with scene if it is enabled. All callers check for a null attachments module.
96 // Ideally, there should be a null attachments module for when this core attachments module has been
97 // disabled. Registering only when enabled allows for other attachments module implementations.
98 m_scene.RegisterModuleInterface<IAttachmentsModule>(this);
78 m_scene.EventManager.OnNewClient += SubscribeToClientEvents; 99 m_scene.EventManager.OnNewClient += SubscribeToClientEvents;
100 m_scene.EventManager.OnStartScript += (localID, itemID) => HandleScriptStateChange(localID, true);
101 m_scene.EventManager.OnStopScript += (localID, itemID) => HandleScriptStateChange(localID, false);
102
103 MainConsole.Instance.Commands.AddCommand(
104 "Debug",
105 false,
106 "debug attachments log",
107 "debug attachments log [0|1]",
108 "Turn on attachments debug logging",
109 " <= 0 - turns off debug logging\n"
110 + " >= 1 - turns on attachment message debug logging",
111 HandleDebugAttachmentsLog);
112
113 MainConsole.Instance.Commands.AddCommand(
114 "Debug",
115 false,
116 "debug attachments throttle",
117 "debug attachments throttle <ms>",
118 "Turn on attachments throttling.",
119 "This requires a millisecond value. " +
120 " == 0 - disable throttling.\n"
121 + " > 0 - sleeps for this number of milliseconds per 100 prims rezzed.",
122 HandleDebugAttachmentsThrottle);
123
124 MainConsole.Instance.Commands.AddCommand(
125 "Debug",
126 false,
127 "debug attachments status",
128 "debug attachments status",
129 "Show current attachments debug status",
130 HandleDebugAttachmentsStatus);
131 }
79 132
80 // TODO: Should probably be subscribing to CloseClient too, but this doesn't yet give us IClientAPI 133 // TODO: Should probably be subscribing to CloseClient too, but this doesn't yet give us IClientAPI
81 } 134 }
135
136 private void HandleDebugAttachmentsLog(string module, string[] args)
137 {
138 int debugLevel;
139
140 if (!(args.Length == 4 && int.TryParse(args[3], out debugLevel)))
141 {
142 MainConsole.Instance.OutputFormat("Usage: debug attachments log [0|1]");
143 }
144 else
145 {
146 DebugLevel = debugLevel;
147 MainConsole.Instance.OutputFormat(
148 "Set attachments debug level to {0} in {1}", DebugLevel, m_scene.Name);
149 }
150 }
151
152 private void HandleDebugAttachmentsThrottle(string module, string[] args)
153 {
154 int ms;
155
156 if (args.Length == 4 && int.TryParse(args[3], out ms))
157 {
158 ThrottlePer100PrimsRezzed = ms;
159 MainConsole.Instance.OutputFormat(
160 "Attachments rez throttle per 100 prims is now {0} in {1}", ThrottlePer100PrimsRezzed, m_scene.Name);
161
162 return;
163 }
164
165 MainConsole.Instance.OutputFormat("Usage: debug attachments throttle <ms>");
166 }
167
168 private void HandleDebugAttachmentsStatus(string module, string[] args)
169 {
170 MainConsole.Instance.OutputFormat("Settings for {0}", m_scene.Name);
171 MainConsole.Instance.OutputFormat("Debug logging level: {0}", DebugLevel);
172 MainConsole.Instance.OutputFormat("Throttle per 100 prims: {0}ms", ThrottlePer100PrimsRezzed);
173 }
174
175 /// <summary>
176 /// Listen for client triggered running state changes so that we can persist the script's object if necessary.
177 /// </summary>
178 /// <param name='localID'></param>
179 /// <param name='itemID'></param>
180 private void HandleScriptStateChange(uint localID, bool started)
181 {
182 SceneObjectGroup sog = m_scene.GetGroupByPrim(localID);
183 if (sog != null && sog.IsAttachment)
184 {
185 if (!started)
186 {
187 // FIXME: This is a convoluted way for working out whether the script state has changed to stop
188 // because it has been manually stopped or because the stop was called in UpdateDetachedObject() below
189 // This needs to be handled in a less tangled way.
190 ScenePresence sp = m_scene.GetScenePresence(sog.AttachedAvatar);
191 if (sp.ControllingClient.IsActive)
192 sog.HasGroupChanged = true;
193 }
194 else
195 {
196 sog.HasGroupChanged = true;
197 }
198 }
199 }
82 200
83 public void RemoveRegion(Scene scene) 201 public void RemoveRegion(Scene scene)
84 { 202 {
@@ -127,8 +245,9 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments
127 string state = sog.GetStateSnapshot(); 245 string state = sog.GetStateSnapshot();
128 ad.AttachmentObjectStates.Add(state); 246 ad.AttachmentObjectStates.Add(state);
129 sp.InTransitScriptStates.Add(state); 247 sp.InTransitScriptStates.Add(state);
130 // Let's remove the scripts of the original object here 248
131 sog.RemoveScriptInstances(true); 249 // Scripts of the originals will be removed when the Agent is successfully removed.
250 // sog.RemoveScriptInstances(true);
132 } 251 }
133 } 252 }
134 } 253 }
@@ -136,6 +255,8 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments
136 255
137 public void CopyAttachments(AgentData ad, IScenePresence sp) 256 public void CopyAttachments(AgentData ad, IScenePresence sp)
138 { 257 {
258// m_log.DebugFormat("[ATTACHMENTS MODULE]: Copying attachment data into {0} in {1}", sp.Name, m_scene.Name);
259
139 if (ad.AttachmentObjects != null && ad.AttachmentObjects.Count > 0) 260 if (ad.AttachmentObjects != null && ad.AttachmentObjects.Count > 0)
140 { 261 {
141 lock (sp.AttachmentsSyncLock) 262 lock (sp.AttachmentsSyncLock)
@@ -146,16 +267,17 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments
146 { 267 {
147 ((SceneObjectGroup)so).LocalId = 0; 268 ((SceneObjectGroup)so).LocalId = 0;
148 ((SceneObjectGroup)so).RootPart.ClearUpdateSchedule(); 269 ((SceneObjectGroup)so).RootPart.ClearUpdateSchedule();
270
271// m_log.DebugFormat(
272// "[ATTACHMENTS MODULE]: Copying script state with {0} bytes for object {1} for {2} in {3}",
273// ad.AttachmentObjectStates[i].Length, so.Name, sp.Name, m_scene.Name);
274
149 so.SetState(ad.AttachmentObjectStates[i++], m_scene); 275 so.SetState(ad.AttachmentObjectStates[i++], m_scene);
150 m_scene.IncomingCreateObject(Vector3.Zero, so); 276 m_scene.IncomingCreateObject(Vector3.Zero, so);
151 } 277 }
152 } 278 }
153 } 279 }
154 280
155 /// <summary>
156 /// RezAttachments. This should only be called upon login on the first region.
157 /// Attachment rezzings on crossings and TPs are done in a different way.
158 /// </summary>
159 public void RezAttachments(IScenePresence sp) 281 public void RezAttachments(IScenePresence sp)
160 { 282 {
161 if (!Enabled) 283 if (!Enabled)
@@ -164,15 +286,35 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments
164 if (null == sp.Appearance) 286 if (null == sp.Appearance)
165 { 287 {
166 m_log.WarnFormat("[ATTACHMENTS MODULE]: Appearance has not been initialized for agent {0}", sp.UUID); 288 m_log.WarnFormat("[ATTACHMENTS MODULE]: Appearance has not been initialized for agent {0}", sp.UUID);
289
167 return; 290 return;
168 } 291 }
169 292
170// m_log.DebugFormat("[ATTACHMENTS MODULE]: Rezzing any attachments for {0}", sp.Name); 293 if (sp.GetAttachments().Count > 0)
294 {
295 if (DebugLevel > 0)
296 m_log.DebugFormat(
297 "[ATTACHMENTS MODULE]: Not doing simulator-side attachment rez for {0} in {1} as their viewer has already rezzed attachments",
298 m_scene.Name, sp.Name);
299
300 return;
301 }
302
303 if (DebugLevel > 0)
304 m_log.DebugFormat("[ATTACHMENTS MODULE]: Rezzing any attachments for {0} from simulator-side", sp.Name);
171 305
172 List<AvatarAttachment> attachments = sp.Appearance.GetAttachments(); 306 List<AvatarAttachment> attachments = sp.Appearance.GetAttachments();
307
308 // Let's get all items at once, so they get cached
309 UUID[] items = new UUID[attachments.Count];
310 int i = 0;
311 foreach (AvatarAttachment attach in attachments)
312 items[i++] = attach.ItemID;
313 m_scene.InventoryService.GetMultipleItems(sp.UUID, items);
314
173 foreach (AvatarAttachment attach in attachments) 315 foreach (AvatarAttachment attach in attachments)
174 { 316 {
175 uint p = (uint)attach.AttachPoint; 317 uint attachmentPt = (uint)attach.AttachPoint;
176 318
177// m_log.DebugFormat( 319// m_log.DebugFormat(
178// "[ATTACHMENTS MODULE]: Doing initial rez of attachment with itemID {0}, assetID {1}, point {2} for {3} in {4}", 320// "[ATTACHMENTS MODULE]: Doing initial rez of attachment with itemID {0}, assetID {1}, point {2} for {3} in {4}",
@@ -190,16 +332,14 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments
190 { 332 {
191 // If we're an NPC then skip all the item checks and manipulations since we don't have an 333 // If we're an NPC then skip all the item checks and manipulations since we don't have an
192 // inventory right now. 334 // inventory right now.
193 if (sp.PresenceType == PresenceType.Npc) 335 RezSingleAttachmentFromInventoryInternal(
194 RezSingleAttachmentFromInventoryInternal(sp, UUID.Zero, attach.AssetID, p); 336 sp, sp.PresenceType == PresenceType.Npc ? UUID.Zero : attach.ItemID, attach.AssetID, attachmentPt, true);
195 else
196 RezSingleAttachmentFromInventory(sp, attach.ItemID, p);
197 } 337 }
198 catch (Exception e) 338 catch (Exception e)
199 { 339 {
200 UUID agentId = (sp.ControllingClient == null) ? (UUID)null : sp.ControllingClient.AgentId; 340 UUID agentId = (sp.ControllingClient == null) ? default(UUID) : sp.ControllingClient.AgentId;
201 m_log.ErrorFormat("[ATTACHMENTS MODULE]: Unable to rez attachment with itemID {0}, assetID {1}, point {2} for {3}: {4}\n{5}", 341 m_log.ErrorFormat("[ATTACHMENTS MODULE]: Unable to rez attachment with itemID {0}, assetID {1}, point {2} for {3}: {4}\n{5}",
202 attach.ItemID, attach.AssetID, p, agentId, e.Message, e.StackTrace); 342 attach.ItemID, attach.AssetID, attachmentPt, agentId, e.Message, e.StackTrace);
203 } 343 }
204 } 344 }
205 } 345 }
@@ -209,14 +349,36 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments
209 if (!Enabled) 349 if (!Enabled)
210 return; 350 return;
211 351
212// m_log.DebugFormat("[ATTACHMENTS MODULE]: Saving changed attachments for {0}", sp.Name); 352 List<SceneObjectGroup> attachments = sp.GetAttachments();
353
354 if (DebugLevel > 0)
355 m_log.DebugFormat(
356 "[ATTACHMENTS MODULE]: Saving for {0} attachments for {1} in {2}",
357 attachments.Count, sp.Name, m_scene.Name);
358
359 if (attachments.Count <= 0)
360 return;
361
362 Dictionary<SceneObjectGroup, string> scriptStates = new Dictionary<SceneObjectGroup, string>();
363
364 foreach (SceneObjectGroup so in attachments)
365 {
366 // Scripts MUST be snapshotted before the object is
367 // removed from the scene because doing otherwise will
368 // clobber the run flag
369 // This must be done outside the sp.AttachmentSyncLock so that there is no risk of a deadlock from
370 // scripts performing attachment operations at the same time. Getting object states stops the scripts.
371 scriptStates[so] = PrepareScriptInstanceForSave(so, false);
372
373// m_log.DebugFormat(
374// "[ATTACHMENTS MODULE]: For object {0} for {1} in {2} got saved state {3}",
375// so.Name, sp.Name, m_scene.Name, scriptStates[so]);
376 }
213 377
214 lock (sp.AttachmentsSyncLock) 378 lock (sp.AttachmentsSyncLock)
215 { 379 {
216 foreach (SceneObjectGroup so in sp.GetAttachments()) 380 foreach (SceneObjectGroup so in attachments)
217 { 381 UpdateDetachedObject(sp, so, scriptStates[so]);
218 UpdateDetachedObject(sp, so);
219 }
220 382
221 sp.ClearAttachments(); 383 sp.ClearAttachments();
222 } 384 }
@@ -227,9 +389,10 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments
227 if (!Enabled) 389 if (!Enabled)
228 return; 390 return;
229 391
230// m_log.DebugFormat( 392 if (DebugLevel > 0)
231// "[ATTACHMENTS MODULE]: Deleting attachments from scene {0} for {1}, silent = {2}", 393 m_log.DebugFormat(
232// m_scene.RegionInfo.RegionName, sp.Name, silent); 394 "[ATTACHMENTS MODULE]: Deleting attachments from scene {0} for {1}, silent = {2}",
395 m_scene.RegionInfo.RegionName, sp.Name, silent);
233 396
234 foreach (SceneObjectGroup sop in sp.GetAttachments()) 397 foreach (SceneObjectGroup sop in sp.GetAttachments())
235 { 398 {
@@ -238,114 +401,149 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments
238 401
239 sp.ClearAttachments(); 402 sp.ClearAttachments();
240 } 403 }
241 404
242 public bool AttachObject(IScenePresence sp, SceneObjectGroup group, uint attachmentPt, bool silent, bool temp) 405 public bool AttachObject(
406 IScenePresence sp, SceneObjectGroup group, uint attachmentPt, bool silent, bool addToInventory, bool append)
243 { 407 {
244 if (!Enabled) 408 if (!Enabled)
245 return false; 409 return false;
246 410
247 if (AttachObjectInternal(sp, group, attachmentPt, silent, temp)) 411 group.DetachFromBackup();
248 {
249 m_scene.EventManager.TriggerOnAttach(group.LocalId, group.FromItemID, sp.UUID);
250 return true;
251 }
252 412
253 return false; 413 bool success = AttachObjectInternal(sp, group, attachmentPt, silent, addToInventory, false, append);
414
415 if (!success)
416 group.AttachToBackup();
417
418 return success;
254 } 419 }
255 420
256 private bool AttachObjectInternal(IScenePresence sp, SceneObjectGroup group, uint attachmentPt, bool silent, bool temp) 421 /// <summary>
422 /// Internal method which actually does all the work for attaching an object.
423 /// </summary>
424 /// <returns>The object attached.</returns>
425 /// <param name='sp'></param>
426 /// <param name='group'>The object to attach.</param>
427 /// <param name='attachmentPt'></param>
428 /// <param name='silent'></param>
429 /// <param name='addToInventory'>If true then add object to user inventory.</param>
430 /// <param name='resumeScripts'>If true then scripts are resumed on the attached object.</param>
431 /// <param name='append'>Append to attachment point rather than replace.</param>
432 private bool AttachObjectInternal(
433 IScenePresence sp, SceneObjectGroup group, uint attachmentPt, bool silent, bool addToInventory, bool resumeScripts, bool append)
257 { 434 {
258 lock (sp.AttachmentsSyncLock) 435 if (group.GetSittingAvatarsCount() != 0)
259 { 436 {
260// m_log.DebugFormat( 437 if (DebugLevel > 0)
261// "[ATTACHMENTS MODULE]: Attaching object {0} {1} to {2} point {3} from ground (silent = {4})", 438 m_log.WarnFormat(
262// group.Name, group.LocalId, sp.Name, attachmentPt, silent); 439 "[ATTACHMENTS MODULE]: Ignoring request to attach {0} {1} to {2} on {3} since {4} avatars are still sitting on it",
440 group.Name, group.LocalId, sp.Name, attachmentPt, group.GetSittingAvatarsCount());
263 441
264 if (group.GetSittingAvatarsCount() != 0) 442 return false;
265 { 443 }
266// m_log.WarnFormat( 444
267// "[ATTACHMENTS MODULE]: Ignoring request to attach {0} {1} to {2} on {3} since {4} avatars are still sitting on it", 445 Vector3 attachPos = group.AbsolutePosition;
268// group.Name, group.LocalId, sp.Name, attachmentPt, group.GetSittingAvatarsCount()); 446 // If the attachment point isn't the same as the one previously used
269 447 // set it's offset position = 0 so that it appears on the attachment point
270 return false; 448 // and not in a weird location somewhere unknown.
271 } 449 if (attachmentPt != (uint)AttachmentPoint.Default && attachmentPt != group.AttachmentPoint)
272 450 {
273 if (sp.GetAttachments(attachmentPt).Contains(group)) 451 attachPos = Vector3.Zero;
274 { 452 }
275 // m_log.WarnFormat( 453
276 // "[ATTACHMENTS MODULE]: Ignoring request to attach {0} {1} to {2} on {3} since it's already attached", 454 // if the attachment point is the same as previous, make sure we get the saved
277 // group.Name, group.LocalId, sp.Name, AttachmentPt); 455 // position info.
278 456 if (attachmentPt != 0 && attachmentPt == group.RootPart.Shape.LastAttachPoint)
279 return false; 457 {
280 } 458 attachPos = group.RootPart.AttachedPos;
281 459 }
282 Vector3 attachPos = group.AbsolutePosition; 460
283 461 // AttachmentPt 0 means the client chose to 'wear' the attachment.
284 // TODO: this short circuits multiple attachments functionality in LL viewer 2.1+ and should 462 if (attachmentPt == (uint)AttachmentPoint.Default)
285 // be removed when that functionality is implemented in opensim 463 {
286 attachmentPt &= 0x7f; 464 // Check object for stored attachment point
287 465 attachmentPt = group.AttachmentPoint;
288 // If the attachment point isn't the same as the one previously used 466 }
289 // set it's offset position = 0 so that it appears on the attachment point 467
290 // and not in a weird location somewhere unknown. 468 // if we didn't find an attach point, look for where it was last attached
291 if (attachmentPt != 0 && attachmentPt != group.AttachmentPoint) 469 if (attachmentPt == 0)
292 { 470 {
293 attachPos = Vector3.Zero; 471 attachmentPt = (uint)group.RootPart.Shape.LastAttachPoint;
294 } 472 attachPos = group.RootPart.AttachedPos;
295 473 group.HasGroupChanged = true;
296 // AttachmentPt 0 means the client chose to 'wear' the attachment. 474 }
297 if (attachmentPt == 0) 475
476 // if we still didn't find a suitable attachment point.......
477 if (attachmentPt == 0)
478 {
479 // Stick it on left hand with Zero Offset from the attachment point.
480 attachmentPt = (uint)AttachmentPoint.LeftHand;
481 attachPos = Vector3.Zero;
482 }
483
484 group.AttachmentPoint = attachmentPt;
485 group.AbsolutePosition = attachPos;
486
487 List<SceneObjectGroup> attachments = sp.GetAttachments(attachmentPt);
488
489 if (attachments.Contains(group))
490 {
491 if (DebugLevel > 0)
492 m_log.WarnFormat(
493 "[ATTACHMENTS MODULE]: Ignoring request to attach {0} {1} to {2} on {3} since it's already attached",
494 group.Name, group.LocalId, sp.Name, attachmentPt);
495
496 return false;
497 }
498
499 // If we already have 5, remove the oldest until only 4 are left. Skip over temp ones
500 while (attachments.Count >= 5)
501 {
502 if (attachments[0].FromItemID != UUID.Zero)
503 DetachSingleAttachmentToInv(sp, attachments[0]);
504 attachments.RemoveAt(0);
505 }
506
507 // If we're not appending, remove the rest as well
508 if (attachments.Count != 0 && !append)
509 {
510 foreach (SceneObjectGroup g in attachments)
298 { 511 {
299 // Check object for stored attachment point 512 if (g.FromItemID != UUID.Zero)
300 attachmentPt = group.AttachmentPoint; 513 DetachSingleAttachmentToInv(sp, g);
301 } 514 }
515 }
516
517 lock (sp.AttachmentsSyncLock)
518 {
519 if (addToInventory && sp.PresenceType != PresenceType.Npc)
520 UpdateUserInventoryWithAttachment(sp, group, attachmentPt, append);
302 521
303 // if we still didn't find a suitable attachment point....... 522 AttachToAgent(sp, group, attachmentPt, attachPos, silent);
304 if (attachmentPt == 0) 523
524 if (resumeScripts)
305 { 525 {
306 // Stick it on left hand with Zero Offset from the attachment point. 526 // Fire after attach, so we don't get messy perms dialogs
307 attachmentPt = (uint)AttachmentPoint.LeftHand; 527 // 4 == AttachedRez
308 attachPos = Vector3.Zero; 528 group.CreateScriptInstances(0, true, m_scene.DefaultScriptEngine, 4);
529 group.ResumeScripts();
309 } 530 }
310
311 group.AttachmentPoint = attachmentPt;
312 group.AbsolutePosition = attachPos;
313 531
314 if (sp.PresenceType != PresenceType.Npc) 532 // Do this last so that event listeners have access to all the effects of the attachment
315 UpdateUserInventoryWithAttachment(sp, group, attachmentPt, temp); 533 m_scene.EventManager.TriggerOnAttach(group.LocalId, group.FromItemID, sp.UUID);
316
317 AttachToAgent(sp, group, attachmentPt, attachPos, silent);
318 } 534 }
319 535
320 return true; 536 return true;
321 } 537 }
322 538
323 private void UpdateUserInventoryWithAttachment(IScenePresence sp, SceneObjectGroup group, uint attachmentPt, bool temp) 539 private void UpdateUserInventoryWithAttachment(IScenePresence sp, SceneObjectGroup group, uint attachmentPt, bool append)
324 { 540 {
325 // Remove any previous attachments
326 List<SceneObjectGroup> attachments = sp.GetAttachments(attachmentPt);
327
328 // At the moment we can only deal with a single attachment
329 if (attachments.Count != 0)
330 {
331 if (attachments[0].FromItemID != UUID.Zero)
332 DetachSingleAttachmentToInvInternal(sp, attachments[0]);
333 // Error logging commented because UUID.Zero now means temp attachment
334// else
335// m_log.WarnFormat(
336// "[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!",
337// attachments[0].Name, attachments[0].LocalId, attachmentPt, group.Name, group.LocalId, sp.Name);
338 }
339
340 // Add the new attachment to inventory if we don't already have it. 541 // Add the new attachment to inventory if we don't already have it.
341 if (!temp) 542 UUID newAttachmentItemID = group.FromItemID;
342 { 543 if (newAttachmentItemID == UUID.Zero)
343 UUID newAttachmentItemID = group.FromItemID; 544 newAttachmentItemID = AddSceneObjectAsNewAttachmentInInv(sp, group).ID;
344 if (newAttachmentItemID == UUID.Zero)
345 newAttachmentItemID = AddSceneObjectAsNewAttachmentInInv(sp, group).ID;
346 545
347 ShowAttachInUserInventory(sp, attachmentPt, newAttachmentItemID, group); 546 ShowAttachInUserInventory(sp, attachmentPt, newAttachmentItemID, group, append);
348 }
349 } 547 }
350 548
351 public SceneObjectGroup RezSingleAttachmentFromInventory(IScenePresence sp, UUID itemID, uint AttachmentPt) 549 public SceneObjectGroup RezSingleAttachmentFromInventory(IScenePresence sp, UUID itemID, uint AttachmentPt)
@@ -353,41 +551,40 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments
353 if (!Enabled) 551 if (!Enabled)
354 return null; 552 return null;
355 553
356// m_log.DebugFormat( 554 if (DebugLevel > 0)
357// "[ATTACHMENTS MODULE]: RezSingleAttachmentFromInventory to point {0} from item {1} for {2}", 555 m_log.DebugFormat(
358// (AttachmentPoint)AttachmentPt, itemID, sp.Name); 556 "[ATTACHMENTS MODULE]: RezSingleAttachmentFromInventory to point {0} from item {1} for {2} in {3}",
359 557 (AttachmentPoint)AttachmentPt, itemID, sp.Name, m_scene.Name);
360 // TODO: this short circuits multiple attachments functionality in LL viewer 2.1+ and should
361 // be removed when that functionality is implemented in opensim
362 AttachmentPt &= 0x7f;
363 558
364 // Viewer 2/3 sometimes asks to re-wear items that are already worn (and show up in it's inventory as such). 559 // We check the attachments in the avatar appearance here rather than the objects attached to the
365 // This often happens during login - not sure the exact reason. 560 // ScenePresence itself so that we can ignore calls by viewer 2/3 to attach objects on startup. We are
366 // For now, we will ignore the request. Unfortunately, this means that we need to dig through all the 561 // already doing this in ScenePresence.MakeRootAgent(). Simulator-side attaching needs to be done
367 // ScenePresence attachments. We can't use the data in AvatarAppearance because that's present at login 562 // because pre-outfit folder viewers (most version 1 viewers) require it.
368 // before anything has actually been attached.
369 bool alreadyOn = false; 563 bool alreadyOn = false;
370 List<SceneObjectGroup> existingAttachments = sp.GetAttachments(); 564 List<AvatarAttachment> existingAttachments = sp.Appearance.GetAttachments();
371 foreach (SceneObjectGroup so in existingAttachments) 565 foreach (AvatarAttachment existingAttachment in existingAttachments)
372 { 566 {
373 if (so.FromItemID == itemID) 567 if (existingAttachment.ItemID == itemID)
374 { 568 {
375 alreadyOn = true; 569 alreadyOn = true;
376 break; 570 break;
377 } 571 }
378 } 572 }
379 573
380// if (sp.Appearance.GetAttachmentForItem(itemID) != null)
381 if (alreadyOn) 574 if (alreadyOn)
382 { 575 {
383// m_log.WarnFormat( 576 if (DebugLevel > 0)
384// "[ATTACHMENTS MODULE]: Ignoring request by {0} to wear item {1} at {2} since it is already worn", 577 m_log.DebugFormat(
385// sp.Name, itemID, AttachmentPt); 578 "[ATTACHMENTS MODULE]: Ignoring request by {0} to wear item {1} at {2} since it is already worn",
579 sp.Name, itemID, AttachmentPt);
386 580
387 return null; 581 return null;
388 } 582 }
389 583
390 return RezSingleAttachmentFromInventoryInternal(sp, itemID, UUID.Zero, AttachmentPt); 584 bool append = (AttachmentPt & 0x80) != 0;
585 AttachmentPt &= 0x7f;
586
587 return RezSingleAttachmentFromInventoryInternal(sp, itemID, UUID.Zero, AttachmentPt, append);
391 } 588 }
392 589
393 public void RezMultipleAttachmentsFromInventory(IScenePresence sp, List<KeyValuePair<UUID, uint>> rezlist) 590 public void RezMultipleAttachmentsFromInventory(IScenePresence sp, List<KeyValuePair<UUID, uint>> rezlist)
@@ -395,13 +592,14 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments
395 if (!Enabled) 592 if (!Enabled)
396 return; 593 return;
397 594
398 // m_log.DebugFormat("[ATTACHMENTS MODULE]: Rezzing multiple attachments from inventory for {0}", sp.Name); 595 if (DebugLevel > 0)
399 lock (sp.AttachmentsSyncLock) 596 m_log.DebugFormat(
597 "[ATTACHMENTS MODULE]: Rezzing {0} attachments from inventory for {1} in {2}",
598 rezlist.Count, sp.Name, m_scene.Name);
599
600 foreach (KeyValuePair<UUID, uint> rez in rezlist)
400 { 601 {
401 foreach (KeyValuePair<UUID, uint> rez in rezlist) 602 RezSingleAttachmentFromInventory(sp, rez.Key, rez.Value);
402 {
403 RezSingleAttachmentFromInventory(sp, rez.Key, rez.Value);
404 }
405 } 603 }
406 } 604 }
407 605
@@ -415,9 +613,10 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments
415 if (!Enabled) 613 if (!Enabled)
416 return; 614 return;
417 615
418// m_log.DebugFormat( 616 if (DebugLevel > 0)
419// "[ATTACHMENTS MODULE]: DetachSingleAttachmentToGround() for {0}, object {1}", 617 m_log.DebugFormat(
420// sp.UUID, soLocalId); 618 "[ATTACHMENTS MODULE]: DetachSingleAttachmentToGround() for {0}, object {1}",
619 sp.UUID, soLocalId);
421 620
422 SceneObjectGroup so = m_scene.GetGroupByPrim(soLocalId); 621 SceneObjectGroup so = m_scene.GetGroupByPrim(soLocalId);
423 622
@@ -433,9 +632,10 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments
433 if (inventoryID == UUID.Zero) 632 if (inventoryID == UUID.Zero)
434 return; 633 return;
435 634
436// m_log.DebugFormat( 635 if (DebugLevel > 0)
437// "[ATTACHMENTS MODULE]: In DetachSingleAttachmentToGround(), object is {0} {1}, associated item is {2}", 636 m_log.DebugFormat(
438// so.Name, so.LocalId, inventoryID); 637 "[ATTACHMENTS MODULE]: In DetachSingleAttachmentToGround(), object is {0} {1}, associated item is {2}",
638 so.Name, so.LocalId, inventoryID);
439 639
440 lock (sp.AttachmentsSyncLock) 640 lock (sp.AttachmentsSyncLock)
441 { 641 {
@@ -463,6 +663,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments
463 so.ClearPartAttachmentData(); 663 so.ClearPartAttachmentData();
464 rootPart.ApplyPhysics(rootPart.GetEffectiveObjectFlags(), rootPart.VolumeDetectActive); 664 rootPart.ApplyPhysics(rootPart.GetEffectiveObjectFlags(), rootPart.VolumeDetectActive);
465 so.HasGroupChanged = true; 665 so.HasGroupChanged = true;
666 so.RootPart.Shape.LastAttachPoint = (byte)so.AttachmentPoint;
466 rootPart.Rezzed = DateTime.Now; 667 rootPart.Rezzed = DateTime.Now;
467 rootPart.RemFlag(PrimFlags.TemporaryOnRez); 668 rootPart.RemFlag(PrimFlags.TemporaryOnRez);
468 so.AttachToBackup(); 669 so.AttachToBackup();
@@ -481,25 +682,38 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments
481 682
482 public void DetachSingleAttachmentToInv(IScenePresence sp, SceneObjectGroup so) 683 public void DetachSingleAttachmentToInv(IScenePresence sp, SceneObjectGroup so)
483 { 684 {
685 if (so.AttachedAvatar != sp.UUID)
686 {
687 m_log.WarnFormat(
688 "[ATTACHMENTS MODULE]: Tried to detach object {0} from {1} {2} but attached avatar id was {3} in {4}",
689 so.Name, sp.Name, sp.UUID, so.AttachedAvatar, m_scene.RegionInfo.RegionName);
690
691 return;
692 }
693
694 if (DebugLevel > 0)
695 m_log.DebugFormat(
696 "[ATTACHMENTS MODULE]: Detaching object {0} {1} (FromItemID {2}) for {3} in {4}",
697 so.Name, so.LocalId, so.FromItemID, sp.Name, m_scene.Name);
698
699 // Scripts MUST be snapshotted before the object is
700 // removed from the scene because doing otherwise will
701 // clobber the run flag
702 // This must be done outside the sp.AttachmentSyncLock so that there is no risk of a deadlock from
703 // scripts performing attachment operations at the same time. Getting object states stops the scripts.
704 string scriptedState = PrepareScriptInstanceForSave(so, true);
705
484 lock (sp.AttachmentsSyncLock) 706 lock (sp.AttachmentsSyncLock)
485 { 707 {
486 // Save avatar attachment information 708 // Save avatar attachment information
487// m_log.Debug("[ATTACHMENTS MODULE]: Detaching from UserID: " + sp.UUID + ", ItemID: " + itemID); 709// m_log.Debug("[ATTACHMENTS MODULE]: Detaching from UserID: " + sp.UUID + ", ItemID: " + itemID);
488 710
489 if (so.AttachedAvatar != sp.UUID)
490 {
491 m_log.WarnFormat(
492 "[ATTACHMENTS MODULE]: Tried to detach object {0} from {1} {2} but attached avatar id was {3} in {4}",
493 so.Name, sp.Name, sp.UUID, so.AttachedAvatar, m_scene.RegionInfo.RegionName);
494
495 return;
496 }
497
498 bool changed = sp.Appearance.DetachAttachment(so.FromItemID); 711 bool changed = sp.Appearance.DetachAttachment(so.FromItemID);
499 if (changed && m_scene.AvatarFactory != null) 712 if (changed && m_scene.AvatarFactory != null)
500 m_scene.AvatarFactory.QueueAppearanceSave(sp.UUID); 713 m_scene.AvatarFactory.QueueAppearanceSave(sp.UUID);
501 714
502 DetachSingleAttachmentToInvInternal(sp, so); 715 sp.RemoveAttachment(so);
716 UpdateDetachedObject(sp, so, scriptedState);
503 } 717 }
504 } 718 }
505 719
@@ -588,15 +802,9 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments
588 (sbyte)AssetType.Object, 802 (sbyte)AssetType.Object,
589 Utils.StringToBytes(sceneObjectXml), 803 Utils.StringToBytes(sceneObjectXml),
590 sp.UUID); 804 sp.UUID);
591 m_scene.AssetService.Store(asset);
592 805
593 item.AssetID = asset.FullID; 806 if (m_invAccessModule != null)
594 item.Description = asset.Description; 807 m_invAccessModule.UpdateInventoryItemAsset(sp.UUID, item, asset);
595 item.Name = asset.Name;
596 item.AssetType = asset.Type;
597 item.InvType = (int)InventoryType.Object;
598
599 m_scene.InventoryService.UpdateItem(item);
600 808
601 // If the name of the object has been changed whilst attached then we want to update the inventory 809 // If the name of the object has been changed whilst attached then we want to update the inventory
602 // item in the viewer. 810 // item in the viewer.
@@ -606,12 +814,12 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments
606 814
607 grp.HasGroupChanged = false; // Prevent it being saved over and over 815 grp.HasGroupChanged = false; // Prevent it being saved over and over
608 } 816 }
609// else 817 else if (DebugLevel > 0)
610// { 818 {
611// m_log.DebugFormat( 819 m_log.DebugFormat(
612// "[ATTACHMENTS MODULE]: Don't need to update asset for unchanged attachment {0}, attachpoint {1}", 820 "[ATTACHMENTS MODULE]: Don't need to update asset for unchanged attachment {0}, attachpoint {1}",
613// grp.UUID, grp.AttachmentPoint); 821 grp.UUID, grp.AttachmentPoint);
614// } 822 }
615 } 823 }
616 824
617 /// <summary> 825 /// <summary>
@@ -629,11 +837,10 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments
629 private void AttachToAgent( 837 private void AttachToAgent(
630 IScenePresence sp, SceneObjectGroup so, uint attachmentpoint, Vector3 attachOffset, bool silent) 838 IScenePresence sp, SceneObjectGroup so, uint attachmentpoint, Vector3 attachOffset, bool silent)
631 { 839 {
632// m_log.DebugFormat( 840 if (DebugLevel > 0)
633// "[ATTACHMENTS MODULE]: Adding attachment {0} to avatar {1} in pt {2} pos {3} {4}", 841 m_log.DebugFormat(
634// so.Name, sp.Name, attachmentpoint, attachOffset, so.RootPart.AttachedPos); 842 "[ATTACHMENTS MODULE]: Adding attachment {0} to avatar {1} at pt {2} pos {3} {4} in {5}",
635 843 so.Name, sp.Name, attachmentpoint, attachOffset, so.RootPart.AttachedPos, m_scene.Name);
636 so.DetachFromBackup();
637 844
638 // Remove from database and parcel prim count 845 // Remove from database and parcel prim count
639 m_scene.DeleteFromStorage(so.UUID); 846 m_scene.DeleteFromStorage(so.UUID);
@@ -656,16 +863,17 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments
656 { 863 {
657 if (so.HasPrivateAttachmentPoint) 864 if (so.HasPrivateAttachmentPoint)
658 { 865 {
659// m_log.DebugFormat( 866 if (DebugLevel > 0)
660// "[ATTACHMENTS MODULE]: Killing private HUD {0} for avatars other than {1} at attachment point {2}", 867 m_log.DebugFormat(
661// so.Name, sp.Name, so.AttachmentPoint); 868 "[ATTACHMENTS MODULE]: Killing private HUD {0} for avatars other than {1} at attachment point {2}",
869 so.Name, sp.Name, so.AttachmentPoint);
662 870
663 // As this scene object can now only be seen by the attaching avatar, tell everybody else in the 871 // As this scene object can now only be seen by the attaching avatar, tell everybody else in the
664 // scene that it's no longer in their awareness. 872 // scene that it's no longer in their awareness.
665 m_scene.ForEachClient( 873 m_scene.ForEachClient(
666 client => 874 client =>
667 { if (client.AgentId != so.AttachedAvatar) 875 { if (client.AgentId != so.AttachedAvatar)
668 client.SendKillObject(m_scene.RegionInfo.RegionHandle, new List<uint>() { so.LocalId }); 876 client.SendKillObject(new List<uint>() { so.LocalId });
669 }); 877 });
670 } 878 }
671 879
@@ -692,14 +900,15 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments
692 if (m_invAccessModule == null) 900 if (m_invAccessModule == null)
693 return null; 901 return null;
694 902
695 // m_log.DebugFormat( 903 if (DebugLevel > 0)
696 // "[ATTACHMENTS MODULE]: Called AddSceneObjectAsAttachment for object {0} {1} for {2}", 904 m_log.DebugFormat(
697 // grp.Name, grp.LocalId, remoteClient.Name); 905 "[ATTACHMENTS MODULE]: Called AddSceneObjectAsAttachment for object {0} {1} for {2}",
906 grp.Name, grp.LocalId, sp.Name);
698 907
699 InventoryItemBase newItem 908 InventoryItemBase newItem
700 = m_invAccessModule.CopyToInventory( 909 = m_invAccessModule.CopyToInventory(
701 DeRezAction.TakeCopy, 910 DeRezAction.TakeCopy,
702 m_scene.InventoryService.GetFolderForType(sp.UUID, AssetType.Object).ID, 911 m_scene.InventoryService.GetFolderForType(sp.UUID, FolderType.Object).ID,
703 new List<SceneObjectGroup> { grp }, 912 new List<SceneObjectGroup> { grp },
704 sp.ControllingClient, true)[0]; 913 sp.ControllingClient, true)[0];
705 914
@@ -709,8 +918,32 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments
709 return newItem; 918 return newItem;
710 } 919 }
711 920
712 private string GetObjectScriptStates(SceneObjectGroup grp) 921 /// <summary>
922 /// Prepares the script instance for save.
923 /// </summary>
924 /// <remarks>
925 /// This involves triggering the detach event and getting the script state (which also stops the script)
926 /// This MUST be done outside sp.AttachmentsSyncLock, since otherwise there is a chance of deadlock if a
927 /// running script is performing attachment operations.
928 /// </remarks>
929 /// <returns>
930 /// The script state ready for persistence.
931 /// </returns>
932 /// <param name='grp'>
933 /// </param>
934 /// <param name='fireDetachEvent'>
935 /// If true, then fire the script event before we save its state.
936 /// </param>
937 private string PrepareScriptInstanceForSave(SceneObjectGroup grp, bool fireDetachEvent)
713 { 938 {
939 if (fireDetachEvent)
940 {
941 m_scene.EventManager.TriggerOnAttach(grp.LocalId, grp.FromItemID, UUID.Zero);
942
943 // Allow detach event time to do some work before stopping the script
944 Thread.Sleep(2);
945 }
946
714 using (StringWriter sw = new StringWriter()) 947 using (StringWriter sw = new StringWriter())
715 { 948 {
716 using (XmlTextWriter writer = new XmlTextWriter(sw)) 949 using (XmlTextWriter writer = new XmlTextWriter(sw))
@@ -722,7 +955,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments
722 } 955 }
723 } 956 }
724 957
725 private void UpdateDetachedObject(IScenePresence sp, SceneObjectGroup so) 958 private void UpdateDetachedObject(IScenePresence sp, SceneObjectGroup so, string scriptedState)
726 { 959 {
727 // Don't save attachments for HG visitors, it 960 // Don't save attachments for HG visitors, it
728 // messes up their inventory. When a HG visitor logs 961 // messes up their inventory. When a HG visitor logs
@@ -735,11 +968,6 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments
735 && (m_scene.UserManagementModule == null 968 && (m_scene.UserManagementModule == null
736 || m_scene.UserManagementModule.IsLocalGridUser(sp.UUID)); 969 || m_scene.UserManagementModule.IsLocalGridUser(sp.UUID));
737 970
738 // Scripts MUST be snapshotted before the object is
739 // removed from the scene because doing otherwise will
740 // clobber the run flag
741 string scriptedState = GetObjectScriptStates(so);
742
743 // Remove the object from the scene so no more updates 971 // Remove the object from the scene so no more updates
744 // are sent. Doing this before the below changes will ensure 972 // are sent. Doing this before the below changes will ensure
745 // updates can't cause "HUD artefacts" 973 // updates can't cause "HUD artefacts"
@@ -763,91 +991,88 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments
763 so.RemoveScriptInstances(true); 991 so.RemoveScriptInstances(true);
764 } 992 }
765 993
766 private void DetachSingleAttachmentToInvInternal(IScenePresence sp, SceneObjectGroup so) 994 protected SceneObjectGroup RezSingleAttachmentFromInventoryInternal(
995 IScenePresence sp, UUID itemID, UUID assetID, uint attachmentPt, bool append)
767 { 996 {
768 // m_log.DebugFormat("[ATTACHMENTS MODULE]: Detaching item {0} to inventory for {1}", itemID, sp.Name); 997 if (m_invAccessModule == null)
998 return null;
769 999
770 m_scene.EventManager.TriggerOnAttach(so.LocalId, so.FromItemID, UUID.Zero); 1000 SceneObjectGroup objatt;
771 sp.RemoveAttachment(so);
772 1001
773 UpdateDetachedObject(sp, so); 1002 if (itemID != UUID.Zero)
774 } 1003 objatt = m_invAccessModule.RezObject(sp.ControllingClient,
1004 itemID, Vector3.Zero, Vector3.Zero, UUID.Zero, (byte)1, true,
1005 false, false, sp.UUID, true);
1006 else
1007 objatt = m_invAccessModule.RezObject(sp.ControllingClient,
1008 null, assetID, Vector3.Zero, Vector3.Zero, UUID.Zero, (byte)1, true,
1009 false, false, sp.UUID, true);
775 1010
776 private SceneObjectGroup RezSingleAttachmentFromInventoryInternal( 1011 if (objatt == null)
777 IScenePresence sp, UUID itemID, UUID assetID, uint attachmentPt) 1012 {
778 { 1013 m_log.WarnFormat(
779 if (m_invAccessModule == null) 1014 "[ATTACHMENTS MODULE]: Could not retrieve item {0} for attaching to avatar {1} at point {2}",
780 return null; 1015 itemID, sp.Name, attachmentPt);
781 1016
782 lock (sp.AttachmentsSyncLock) 1017 return null;
1018 }
1019 else if (itemID == UUID.Zero)
783 { 1020 {
784 SceneObjectGroup objatt; 1021 // We need to have a FromItemID for multiple attachments on a single attach point to appear. This is
1022 // true on Singularity 1.8.5 and quite possibly other viewers as well. As NPCs don't have an inventory
1023 // we will satisfy this requirement by inserting a random UUID.
1024 objatt.FromItemID = UUID.Random();
1025 }
785 1026
786 if (itemID != UUID.Zero) 1027 if (DebugLevel > 0)
787 objatt = m_invAccessModule.RezObject(sp.ControllingClient, 1028 m_log.DebugFormat(
788 itemID, Vector3.Zero, Vector3.Zero, UUID.Zero, (byte)1, true, 1029 "[ATTACHMENTS MODULE]: Rezzed single object {0} with {1} prims for attachment to {2} on point {3} in {4}",
789 false, false, sp.UUID, true); 1030 objatt.Name, objatt.PrimCount, sp.Name, attachmentPt, m_scene.Name);
790 else 1031
791 objatt = m_invAccessModule.RezObject(sp.ControllingClient, 1032 // HasGroupChanged is being set from within RezObject. Ideally it would be set by the caller.
792 null, assetID, Vector3.Zero, Vector3.Zero, UUID.Zero, (byte)1, true, 1033 objatt.HasGroupChanged = false;
793 false, false, sp.UUID, true); 1034 bool tainted = false;
1035 if (attachmentPt != 0 && attachmentPt != objatt.AttachmentPoint)
1036 tainted = true;
1037
1038 // FIXME: Detect whether it's really likely for AttachObject to throw an exception in the normal
1039 // course of events. If not, then it's probably not worth trying to recover the situation
1040 // since this is more likely to trigger further exceptions and confuse later debugging. If
1041 // exceptions can be thrown in expected error conditions (not NREs) then make this consistent
1042 // since other normal error conditions will simply return false instead.
1043 // This will throw if the attachment fails
1044 try
1045 {
1046 AttachObjectInternal(sp, objatt, attachmentPt, false, true, true, append);
1047 }
1048 catch (Exception e)
1049 {
1050 m_log.ErrorFormat(
1051 "[ATTACHMENTS MODULE]: Failed to attach {0} {1} for {2}, exception {3}{4}",
1052 objatt.Name, objatt.UUID, sp.Name, e.Message, e.StackTrace);
794 1053
795 if (objatt != null) 1054 // Make sure the object doesn't stick around and bail
796 { 1055 sp.RemoveAttachment(objatt);
797// m_log.DebugFormat( 1056 m_scene.DeleteSceneObject(objatt, false);
798// "[ATTACHMENTS MODULE]: Rezzed single object {0} for attachment to {1} on point {2} in {3}", 1057 return null;
799// objatt.Name, sp.Name, attachmentPt, m_scene.Name); 1058 }
800
801 // HasGroupChanged is being set from within RezObject. Ideally it would be set by the caller.
802 objatt.HasGroupChanged = false;
803 bool tainted = false;
804 if (attachmentPt != 0 && attachmentPt != objatt.AttachmentPoint)
805 tainted = true;
806
807 // FIXME: Detect whether it's really likely for AttachObject to throw an exception in the normal
808 // course of events. If not, then it's probably not worth trying to recover the situation
809 // since this is more likely to trigger further exceptions and confuse later debugging. If
810 // exceptions can be thrown in expected error conditions (not NREs) then make this consistent
811 // since other normal error conditions will simply return false instead.
812 // This will throw if the attachment fails
813 try
814 {
815 AttachObjectInternal(sp, objatt, attachmentPt, false, false);
816 }
817 catch (Exception e)
818 {
819 m_log.ErrorFormat(
820 "[ATTACHMENTS MODULE]: Failed to attach {0} {1} for {2}, exception {3}{4}",
821 objatt.Name, objatt.UUID, sp.Name, e.Message, e.StackTrace);
822
823 // Make sure the object doesn't stick around and bail
824 sp.RemoveAttachment(objatt);
825 m_scene.DeleteSceneObject(objatt, false);
826 return null;
827 }
828 1059
829 if (tainted) 1060 if (tainted)
830 objatt.HasGroupChanged = true; 1061 objatt.HasGroupChanged = true;
831 1062
832 // Fire after attach, so we don't get messy perms dialogs 1063 if (ThrottlePer100PrimsRezzed > 0)
833 // 4 == AttachedRez 1064 {
834 objatt.CreateScriptInstances(0, true, m_scene.DefaultScriptEngine, 4); 1065 int throttleMs = (int)Math.Round((float)objatt.PrimCount / 100 * ThrottlePer100PrimsRezzed);
835 objatt.ResumeScripts();
836 1066
837 // Do this last so that event listeners have access to all the effects of the attachment 1067 if (DebugLevel > 0)
838 m_scene.EventManager.TriggerOnAttach(objatt.LocalId, itemID, sp.UUID); 1068 m_log.DebugFormat(
1069 "[ATTACHMENTS MODULE]: Throttling by {0}ms after rez of {1} with {2} prims for attachment to {3} on point {4} in {5}",
1070 throttleMs, objatt.Name, objatt.PrimCount, sp.Name, attachmentPt, m_scene.Name);
839 1071
840 return objatt; 1072 Thread.Sleep(throttleMs);
841 }
842 else
843 {
844 m_log.WarnFormat(
845 "[ATTACHMENTS MODULE]: Could not retrieve item {0} for attaching to avatar {1} at point {2}",
846 itemID, sp.Name, attachmentPt);
847 }
848 } 1073 }
849 1074
850 return null; 1075 return objatt;
851 } 1076 }
852 1077
853 /// <summary> 1078 /// <summary>
@@ -857,7 +1082,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments
857 /// <param name="AttachmentPt"></param> 1082 /// <param name="AttachmentPt"></param>
858 /// <param name="itemID"></param> 1083 /// <param name="itemID"></param>
859 /// <param name="att"></param> 1084 /// <param name="att"></param>
860 private void ShowAttachInUserInventory(IScenePresence sp, uint AttachmentPt, UUID itemID, SceneObjectGroup att) 1085 private void ShowAttachInUserInventory(IScenePresence sp, uint AttachmentPt, UUID itemID, SceneObjectGroup att, bool append)
861 { 1086 {
862// m_log.DebugFormat( 1087// m_log.DebugFormat(
863// "[USER INVENTORY]: Updating attachment {0} for {1} at {2} using item ID {3}", 1088// "[USER INVENTORY]: Updating attachment {0} for {1} at {2} using item ID {3}",
@@ -880,12 +1105,14 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments
880 if (item == null) 1105 if (item == null)
881 return; 1106 return;
882 1107
883 bool changed = sp.Appearance.SetAttachment((int)AttachmentPt, itemID, item.AssetID); 1108 int attFlag = append ? 0x80 : 0;
1109 bool changed = sp.Appearance.SetAttachment((int)AttachmentPt | attFlag, itemID, item.AssetID);
884 if (changed && m_scene.AvatarFactory != null) 1110 if (changed && m_scene.AvatarFactory != null)
885 { 1111 {
886// m_log.DebugFormat( 1112 if (DebugLevel > 0)
887// "[ATTACHMENTS MODULE]: Queueing appearance save for {0}, attachment {1} point {2} in ShowAttachInUserInventory()", 1113 m_log.DebugFormat(
888// sp.Name, att.Name, AttachmentPt); 1114 "[ATTACHMENTS MODULE]: Queueing appearance save for {0}, attachment {1} point {2} in ShowAttachInUserInventory()",
1115 sp.Name, att.Name, AttachmentPt);
889 1116
890 m_scene.AvatarFactory.QueueAppearanceSave(sp.UUID); 1117 m_scene.AvatarFactory.QueueAppearanceSave(sp.UUID);
891 } 1118 }
@@ -900,9 +1127,10 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments
900 if (!Enabled) 1127 if (!Enabled)
901 return null; 1128 return null;
902 1129
903 // m_log.DebugFormat( 1130 if (DebugLevel > 0)
904 // "[ATTACHMENTS MODULE]: Rezzing attachment to point {0} from item {1} for {2}", 1131 m_log.DebugFormat(
905 // (AttachmentPoint)AttachmentPt, itemID, remoteClient.Name); 1132 "[ATTACHMENTS MODULE]: Rezzing attachment to point {0} from item {1} for {2}",
1133 (AttachmentPoint)AttachmentPt, itemID, remoteClient.Name);
906 1134
907 ScenePresence sp = m_scene.GetScenePresence(remoteClient.AgentId); 1135 ScenePresence sp = m_scene.GetScenePresence(remoteClient.AgentId);
908 1136
@@ -933,9 +1161,10 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments
933 1161
934 private void Client_OnObjectAttach(IClientAPI remoteClient, uint objectLocalID, uint AttachmentPt, bool silent) 1162 private void Client_OnObjectAttach(IClientAPI remoteClient, uint objectLocalID, uint AttachmentPt, bool silent)
935 { 1163 {
936// m_log.DebugFormat( 1164 if (DebugLevel > 0)
937// "[ATTACHMENTS MODULE]: Attaching object local id {0} to {1} point {2} from ground (silent = {3})", 1165 m_log.DebugFormat(
938// objectLocalID, remoteClient.Name, AttachmentPt, silent); 1166 "[ATTACHMENTS MODULE]: Attaching object local id {0} to {1} point {2} from ground (silent = {3})",
1167 objectLocalID, remoteClient.Name, AttachmentPt, silent);
939 1168
940 if (!Enabled) 1169 if (!Enabled)
941 return; 1170 return;
@@ -964,12 +1193,20 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments
964 return; 1193 return;
965 } 1194 }
966 1195
967 // TODO: this short circuits multiple attachments functionality in LL viewer 2.1+ and should 1196 bool append = (AttachmentPt & 0x80) != 0;
968 // be removed when that functionality is implemented in opensim
969 AttachmentPt &= 0x7f; 1197 AttachmentPt &= 0x7f;
970 1198
971 // Calls attach with a Zero position 1199 // Calls attach with a Zero position
972 AttachObject(sp, part.ParentGroup, AttachmentPt, false, false); 1200 if (AttachObject(sp, part.ParentGroup, AttachmentPt, false, true, append))
1201 {
1202 if (DebugLevel > 0)
1203 m_log.Debug(
1204 "[ATTACHMENTS MODULE]: Saving avatar attachment. AgentID: " + remoteClient.AgentId
1205 + ", AttachmentPoint: " + AttachmentPt);
1206
1207 // Save avatar attachment information
1208 m_scene.EventManager.TriggerOnAttach(objectLocalID, part.ParentGroup.FromItemID, remoteClient.AgentId);
1209 }
973 } 1210 }
974 catch (Exception e) 1211 catch (Exception e)
975 { 1212 {
@@ -997,17 +1234,14 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments
997 ScenePresence sp = m_scene.GetScenePresence(remoteClient.AgentId); 1234 ScenePresence sp = m_scene.GetScenePresence(remoteClient.AgentId);
998 if (sp != null) 1235 if (sp != null)
999 { 1236 {
1000 lock (sp.AttachmentsSyncLock) 1237 List<SceneObjectGroup> attachments = sp.GetAttachments();
1238
1239 foreach (SceneObjectGroup group in attachments)
1001 { 1240 {
1002 List<SceneObjectGroup> attachments = sp.GetAttachments(); 1241 if (group.FromItemID == itemID && group.FromItemID != UUID.Zero)
1003
1004 foreach (SceneObjectGroup group in attachments)
1005 { 1242 {
1006 if (group.FromItemID == itemID && group.FromItemID != UUID.Zero) 1243 DetachSingleAttachmentToInv(sp, group);
1007 { 1244 return;
1008 DetachSingleAttachmentToInv(sp, group);
1009 return;
1010 }
1011 } 1245 }
1012 } 1246 }
1013 } 1247 }
diff --git a/OpenSim/Region/CoreModules/Avatar/Attachments/Tests/AttachmentsModuleTests.cs b/OpenSim/Region/CoreModules/Avatar/Attachments/Tests/AttachmentsModuleTests.cs
index 0ee01c7..0ac3add 100644
--- a/OpenSim/Region/CoreModules/Avatar/Attachments/Tests/AttachmentsModuleTests.cs
+++ b/OpenSim/Region/CoreModules/Avatar/Attachments/Tests/AttachmentsModuleTests.cs
@@ -37,7 +37,8 @@ using Nini.Config;
37using NUnit.Framework; 37using NUnit.Framework;
38using OpenMetaverse; 38using OpenMetaverse;
39using OpenSim.Framework; 39using OpenSim.Framework;
40using OpenSim.Framework.Communications; 40using OpenSim.Framework.Servers;
41using OpenSim.Framework.Servers.HttpServer;
41using OpenSim.Region.CoreModules.Avatar.Attachments; 42using OpenSim.Region.CoreModules.Avatar.Attachments;
42using OpenSim.Region.CoreModules.Framework; 43using OpenSim.Region.CoreModules.Framework;
43using OpenSim.Region.CoreModules.Framework.EntityTransfer; 44using OpenSim.Region.CoreModules.Framework.EntityTransfer;
@@ -51,7 +52,6 @@ using OpenSim.Region.ScriptEngine.Interfaces;
51using OpenSim.Region.ScriptEngine.XEngine; 52using OpenSim.Region.ScriptEngine.XEngine;
52using OpenSim.Services.Interfaces; 53using OpenSim.Services.Interfaces;
53using OpenSim.Tests.Common; 54using OpenSim.Tests.Common;
54using OpenSim.Tests.Common.Mock;
55 55
56namespace OpenSim.Region.CoreModules.Avatar.Attachments.Tests 56namespace OpenSim.Region.CoreModules.Avatar.Attachments.Tests
57{ 57{
@@ -130,7 +130,9 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments.Tests
130 config.AddConfig("Modules"); 130 config.AddConfig("Modules");
131 config.Configs["Modules"].Set("InventoryAccessModule", "BasicInventoryAccessModule"); 131 config.Configs["Modules"].Set("InventoryAccessModule", "BasicInventoryAccessModule");
132 132
133 modules.Add(new AttachmentsModule()); 133 AttachmentsModule attMod = new AttachmentsModule();
134 attMod.DebugLevel = 1;
135 modules.Add(attMod);
134 modules.Add(new BasicInventoryAccessModule()); 136 modules.Add(new BasicInventoryAccessModule());
135 } 137 }
136 138
@@ -195,9 +197,10 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments.Tests
195 string attName = "att"; 197 string attName = "att";
196 198
197 SceneObjectGroup so = SceneHelpers.AddSceneObject(scene, attName, sp.UUID); 199 SceneObjectGroup so = SceneHelpers.AddSceneObject(scene, attName, sp.UUID);
200 Assert.That(so.Backup, Is.True);
198 201
199 m_numberOfAttachEventsFired = 0; 202 m_numberOfAttachEventsFired = 0;
200 scene.AttachmentsModule.AttachObject(sp, so, (uint)AttachmentPoint.Chest, false, false); 203 scene.AttachmentsModule.AttachObject(sp, so, (uint)AttachmentPoint.Chest, false, true, false);
201 204
202 // Check status on scene presence 205 // Check status on scene presence
203 Assert.That(sp.HasAttachments(), Is.True); 206 Assert.That(sp.HasAttachments(), Is.True);
@@ -209,6 +212,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments.Tests
209 Assert.That(attSo.IsAttachment); 212 Assert.That(attSo.IsAttachment);
210 Assert.That(attSo.UsesPhysics, Is.False); 213 Assert.That(attSo.UsesPhysics, Is.False);
211 Assert.That(attSo.IsTemporary, Is.False); 214 Assert.That(attSo.IsTemporary, Is.False);
215 Assert.That(attSo.Backup, Is.False);
212 216
213 // Check item status 217 // Check item status
214 Assert.That( 218 Assert.That(
@@ -219,7 +223,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments.Tests
219 Assert.That(attachmentItem, Is.Not.Null); 223 Assert.That(attachmentItem, Is.Not.Null);
220 Assert.That(attachmentItem.Name, Is.EqualTo(attName)); 224 Assert.That(attachmentItem.Name, Is.EqualTo(attName));
221 225
222 InventoryFolderBase targetFolder = scene.InventoryService.GetFolderForType(sp.UUID, AssetType.Object); 226 InventoryFolderBase targetFolder = scene.InventoryService.GetFolderForType(sp.UUID, FolderType.Object);
223 Assert.That(attachmentItem.Folder, Is.EqualTo(targetFolder.ID)); 227 Assert.That(attachmentItem.Folder, Is.EqualTo(targetFolder.ID));
224 228
225 Assert.That(scene.GetSceneObjectGroups().Count, Is.EqualTo(1)); 229 Assert.That(scene.GetSceneObjectGroups().Count, Is.EqualTo(1));
@@ -228,6 +232,120 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments.Tests
228 Assert.That(m_numberOfAttachEventsFired, Is.EqualTo(1)); 232 Assert.That(m_numberOfAttachEventsFired, Is.EqualTo(1));
229 } 233 }
230 234
235 [Test]
236 public void TestWearAttachmentFromGround()
237 {
238 TestHelpers.InMethod();
239// TestHelpers.EnableLogging();
240
241 Scene scene = CreateTestScene();
242 UserAccount ua1 = UserAccountHelpers.CreateUserWithInventory(scene, 0x1);
243 ScenePresence sp = SceneHelpers.AddScenePresence(scene, ua1);
244
245 SceneObjectGroup so2 = SceneHelpers.AddSceneObject(scene, "att2", sp.UUID);
246
247 {
248 SceneObjectGroup so = SceneHelpers.AddSceneObject(scene, "att1", sp.UUID);
249
250 m_numberOfAttachEventsFired = 0;
251 scene.AttachmentsModule.AttachObject(sp, so, (uint)AttachmentPoint.Default, false, true, false);
252
253 // Check status on scene presence
254 Assert.That(sp.HasAttachments(), Is.True);
255 List<SceneObjectGroup> attachments = sp.GetAttachments();
256 Assert.That(attachments.Count, Is.EqualTo(1));
257 SceneObjectGroup attSo = attachments[0];
258 Assert.That(attSo.Name, Is.EqualTo(so.Name));
259 Assert.That(attSo.AttachmentPoint, Is.EqualTo((byte)AttachmentPoint.LeftHand));
260 Assert.That(attSo.IsAttachment);
261 Assert.That(attSo.UsesPhysics, Is.False);
262 Assert.That(attSo.IsTemporary, Is.False);
263
264 // Check item status
265 Assert.That(
266 sp.Appearance.GetAttachpoint(attSo.FromItemID),
267 Is.EqualTo((int)AttachmentPoint.LeftHand));
268
269 InventoryItemBase attachmentItem = scene.InventoryService.GetItem(new InventoryItemBase(attSo.FromItemID));
270 Assert.That(attachmentItem, Is.Not.Null);
271 Assert.That(attachmentItem.Name, Is.EqualTo(so.Name));
272
273 InventoryFolderBase targetFolder = scene.InventoryService.GetFolderForType(sp.UUID, FolderType.Object);
274 Assert.That(attachmentItem.Folder, Is.EqualTo(targetFolder.ID));
275
276 Assert.That(scene.GetSceneObjectGroups().Count, Is.EqualTo(2));
277
278 // Check events
279 Assert.That(m_numberOfAttachEventsFired, Is.EqualTo(1));
280 }
281
282 // Test wearing a different attachment from the ground.
283 {
284 scene.AttachmentsModule.AttachObject(sp, so2, (uint)AttachmentPoint.Default, false, true, false);
285
286 // Check status on scene presence
287 Assert.That(sp.HasAttachments(), Is.True);
288 List<SceneObjectGroup> attachments = sp.GetAttachments();
289 Assert.That(attachments.Count, Is.EqualTo(1));
290 SceneObjectGroup attSo = attachments[0];
291 Assert.That(attSo.Name, Is.EqualTo(so2.Name));
292 Assert.That(attSo.AttachmentPoint, Is.EqualTo((byte)AttachmentPoint.LeftHand));
293 Assert.That(attSo.IsAttachment);
294 Assert.That(attSo.UsesPhysics, Is.False);
295 Assert.That(attSo.IsTemporary, Is.False);
296
297 // Check item status
298 Assert.That(
299 sp.Appearance.GetAttachpoint(attSo.FromItemID),
300 Is.EqualTo((int)AttachmentPoint.LeftHand));
301
302 InventoryItemBase attachmentItem = scene.InventoryService.GetItem(new InventoryItemBase(attSo.FromItemID));
303 Assert.That(attachmentItem, Is.Not.Null);
304 Assert.That(attachmentItem.Name, Is.EqualTo(so2.Name));
305
306 InventoryFolderBase targetFolder = scene.InventoryService.GetFolderForType(sp.UUID, FolderType.Object);
307 Assert.That(attachmentItem.Folder, Is.EqualTo(targetFolder.ID));
308
309 Assert.That(scene.GetSceneObjectGroups().Count, Is.EqualTo(1));
310
311 // Check events
312 Assert.That(m_numberOfAttachEventsFired, Is.EqualTo(3));
313 }
314
315 // Test rewearing an already worn attachment from ground. Nothing should happen.
316 {
317 scene.AttachmentsModule.AttachObject(sp, so2, (uint)AttachmentPoint.Default, false, true, false);
318
319 // Check status on scene presence
320 Assert.That(sp.HasAttachments(), Is.True);
321 List<SceneObjectGroup> attachments = sp.GetAttachments();
322 Assert.That(attachments.Count, Is.EqualTo(1));
323 SceneObjectGroup attSo = attachments[0];
324 Assert.That(attSo.Name, Is.EqualTo(so2.Name));
325 Assert.That(attSo.AttachmentPoint, Is.EqualTo((byte)AttachmentPoint.LeftHand));
326 Assert.That(attSo.IsAttachment);
327 Assert.That(attSo.UsesPhysics, Is.False);
328 Assert.That(attSo.IsTemporary, Is.False);
329
330 // Check item status
331 Assert.That(
332 sp.Appearance.GetAttachpoint(attSo.FromItemID),
333 Is.EqualTo((int)AttachmentPoint.LeftHand));
334
335 InventoryItemBase attachmentItem = scene.InventoryService.GetItem(new InventoryItemBase(attSo.FromItemID));
336 Assert.That(attachmentItem, Is.Not.Null);
337 Assert.That(attachmentItem.Name, Is.EqualTo(so2.Name));
338
339 InventoryFolderBase targetFolder = scene.InventoryService.GetFolderForType(sp.UUID, FolderType.Object);
340 Assert.That(attachmentItem.Folder, Is.EqualTo(targetFolder.ID));
341
342 Assert.That(scene.GetSceneObjectGroups().Count, Is.EqualTo(1));
343
344 // Check events
345 Assert.That(m_numberOfAttachEventsFired, Is.EqualTo(3));
346 }
347 }
348
231 /// <summary> 349 /// <summary>
232 /// Test that we do not attempt to attach an in-world object that someone else is sitting on. 350 /// Test that we do not attempt to attach an in-world object that someone else is sitting on.
233 /// </summary> 351 /// </summary>
@@ -254,7 +372,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments.Tests
254 sp2.AbsolutePosition = new Vector3(0, 0, 0); 372 sp2.AbsolutePosition = new Vector3(0, 0, 0);
255 sp2.HandleAgentRequestSit(sp2.ControllingClient, sp2.UUID, so.UUID, Vector3.Zero); 373 sp2.HandleAgentRequestSit(sp2.ControllingClient, sp2.UUID, so.UUID, Vector3.Zero);
256 374
257 scene.AttachmentsModule.AttachObject(sp, so, (uint)AttachmentPoint.Chest, false, false); 375 scene.AttachmentsModule.AttachObject(sp, so, (uint)AttachmentPoint.Chest, false, true, false);
258 376
259 Assert.That(sp.HasAttachments(), Is.False); 377 Assert.That(sp.HasAttachments(), Is.False);
260 Assert.That(scene.GetSceneObjectGroups().Count, Is.EqualTo(1)); 378 Assert.That(scene.GetSceneObjectGroups().Count, Is.EqualTo(1));
@@ -267,7 +385,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments.Tests
267 public void TestRezAttachmentFromInventory() 385 public void TestRezAttachmentFromInventory()
268 { 386 {
269 TestHelpers.InMethod(); 387 TestHelpers.InMethod();
270// log4net.Config.XmlConfigurator.Configure(); 388// TestHelpers.EnableLogging();
271 389
272 Scene scene = CreateTestScene(); 390 Scene scene = CreateTestScene();
273 UserAccount ua1 = UserAccountHelpers.CreateUserWithInventory(scene, 0x1); 391 UserAccount ua1 = UserAccountHelpers.CreateUserWithInventory(scene, 0x1);
@@ -275,29 +393,141 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments.Tests
275 393
276 InventoryItemBase attItem = CreateAttachmentItem(scene, ua1.PrincipalID, "att", 0x10, 0x20); 394 InventoryItemBase attItem = CreateAttachmentItem(scene, ua1.PrincipalID, "att", 0x10, 0x20);
277 395
278 m_numberOfAttachEventsFired = 0; 396 {
279 scene.AttachmentsModule.RezSingleAttachmentFromInventory( 397 scene.AttachmentsModule.RezSingleAttachmentFromInventory(
280 sp, attItem.ID, (uint)AttachmentPoint.Chest); 398 sp, attItem.ID, (uint)AttachmentPoint.Chest);
281 399
282 // Check scene presence status 400 // Check scene presence status
283 Assert.That(sp.HasAttachments(), Is.True); 401 Assert.That(sp.HasAttachments(), Is.True);
284 List<SceneObjectGroup> attachments = sp.GetAttachments(); 402 List<SceneObjectGroup> attachments = sp.GetAttachments();
285 Assert.That(attachments.Count, Is.EqualTo(1)); 403 Assert.That(attachments.Count, Is.EqualTo(1));
286 SceneObjectGroup attSo = attachments[0]; 404 SceneObjectGroup attSo = attachments[0];
287 Assert.That(attSo.Name, Is.EqualTo(attItem.Name)); 405 Assert.That(attSo.Name, Is.EqualTo(attItem.Name));
288 Assert.That(attSo.AttachmentPoint, Is.EqualTo((byte)AttachmentPoint.Chest)); 406 Assert.That(attSo.AttachmentPoint, Is.EqualTo((byte)AttachmentPoint.Chest));
289 Assert.That(attSo.IsAttachment); 407 Assert.That(attSo.IsAttachment);
290 Assert.That(attSo.UsesPhysics, Is.False); 408 Assert.That(attSo.UsesPhysics, Is.False);
291 Assert.That(attSo.IsTemporary, Is.False); 409 Assert.That(attSo.IsTemporary, Is.False);
410 Assert.IsFalse(attSo.Backup);
411
412 // Check appearance status
413 Assert.That(sp.Appearance.GetAttachments().Count, Is.EqualTo(1));
414 Assert.That(sp.Appearance.GetAttachpoint(attItem.ID), Is.EqualTo((int)AttachmentPoint.Chest));
415 Assert.That(scene.GetSceneObjectGroups().Count, Is.EqualTo(1));
416
417 // Check events
418 Assert.That(m_numberOfAttachEventsFired, Is.EqualTo(1));
419 }
420
421 // Test attaching an already attached attachment
422 {
423 scene.AttachmentsModule.RezSingleAttachmentFromInventory(
424 sp, attItem.ID, (uint)AttachmentPoint.Chest);
292 425
293 // Check appearance status 426 // Check scene presence status
294 Assert.That(sp.Appearance.GetAttachments().Count, Is.EqualTo(1)); 427 Assert.That(sp.HasAttachments(), Is.True);
295 Assert.That(sp.Appearance.GetAttachpoint(attItem.ID), Is.EqualTo((int)AttachmentPoint.Chest)); 428 List<SceneObjectGroup> attachments = sp.GetAttachments();
429 Assert.That(attachments.Count, Is.EqualTo(1));
430 SceneObjectGroup attSo = attachments[0];
431 Assert.That(attSo.Name, Is.EqualTo(attItem.Name));
432 Assert.That(attSo.AttachmentPoint, Is.EqualTo((byte)AttachmentPoint.Chest));
433 Assert.That(attSo.IsAttachment);
434 Assert.That(attSo.UsesPhysics, Is.False);
435 Assert.That(attSo.IsTemporary, Is.False);
436
437 // Check appearance status
438 Assert.That(sp.Appearance.GetAttachments().Count, Is.EqualTo(1));
439 Assert.That(sp.Appearance.GetAttachpoint(attItem.ID), Is.EqualTo((int)AttachmentPoint.Chest));
440 Assert.That(scene.GetSceneObjectGroups().Count, Is.EqualTo(1));
441
442 // Check events
443 Assert.That(m_numberOfAttachEventsFired, Is.EqualTo(1));
444 }
445 }
296 446
297 Assert.That(scene.GetSceneObjectGroups().Count, Is.EqualTo(1)); 447 /// <summary>
448 /// Test wearing an attachment from inventory, as opposed to explicit choosing the rez point
449 /// </summary>
450 [Test]
451 public void TestWearAttachmentFromInventory()
452 {
453 TestHelpers.InMethod();
454// TestHelpers.EnableLogging();
298 455
299 // Check events 456 Scene scene = CreateTestScene();
300 Assert.That(m_numberOfAttachEventsFired, Is.EqualTo(1)); 457 UserAccount ua1 = UserAccountHelpers.CreateUserWithInventory(scene, 0x1);
458 ScenePresence sp = SceneHelpers.AddScenePresence(scene, ua1.PrincipalID);
459
460 InventoryItemBase attItem1 = CreateAttachmentItem(scene, ua1.PrincipalID, "att1", 0x10, 0x20);
461 InventoryItemBase attItem2 = CreateAttachmentItem(scene, ua1.PrincipalID, "att2", 0x11, 0x21);
462
463 {
464 m_numberOfAttachEventsFired = 0;
465 scene.AttachmentsModule.RezSingleAttachmentFromInventory(sp, attItem1.ID, (uint)AttachmentPoint.Default);
466
467 // default attachment point is currently the left hand.
468 Assert.That(sp.HasAttachments(), Is.True);
469 List<SceneObjectGroup> attachments = sp.GetAttachments();
470 Assert.That(attachments.Count, Is.EqualTo(1));
471 SceneObjectGroup attSo = attachments[0];
472 Assert.That(attSo.Name, Is.EqualTo(attItem1.Name));
473 Assert.That(attSo.AttachmentPoint, Is.EqualTo((byte)AttachmentPoint.LeftHand));
474 Assert.That(attSo.IsAttachment);
475
476 // Check appearance status
477 Assert.That(sp.Appearance.GetAttachments().Count, Is.EqualTo(1));
478 Assert.That(sp.Appearance.GetAttachpoint(attItem1.ID), Is.EqualTo((int)AttachmentPoint.LeftHand));
479 Assert.That(scene.GetSceneObjectGroups().Count, Is.EqualTo(1));
480
481 // Check events
482 Assert.That(m_numberOfAttachEventsFired, Is.EqualTo(1));
483 }
484
485 // Test wearing a second attachment at the same position
486 // Until multiple attachments at one point is implemented, this will remove the first attachment
487 // This test relies on both attachments having the same default attachment point (in this case LeftHand
488 // since none other has been set).
489 {
490 scene.AttachmentsModule.RezSingleAttachmentFromInventory(sp, attItem2.ID, (uint)AttachmentPoint.Default);
491
492 // default attachment point is currently the left hand.
493 Assert.That(sp.HasAttachments(), Is.True);
494 List<SceneObjectGroup> attachments = sp.GetAttachments();
495 Assert.That(attachments.Count, Is.EqualTo(1));
496 SceneObjectGroup attSo = attachments[0];
497 Assert.That(attSo.Name, Is.EqualTo(attItem2.Name));
498 Assert.That(attSo.AttachmentPoint, Is.EqualTo((byte)AttachmentPoint.LeftHand));
499 Assert.That(attSo.IsAttachment);
500
501 // Check appearance status
502 Assert.That(sp.Appearance.GetAttachments().Count, Is.EqualTo(1));
503 Assert.That(sp.Appearance.GetAttachpoint(attItem2.ID), Is.EqualTo((int)AttachmentPoint.LeftHand));
504 Assert.That(scene.GetSceneObjectGroups().Count, Is.EqualTo(1));
505
506 // Check events
507 Assert.That(m_numberOfAttachEventsFired, Is.EqualTo(3));
508 }
509
510 // Test wearing an already attached attachment
511 {
512 scene.AttachmentsModule.RezSingleAttachmentFromInventory(sp, attItem2.ID, (uint)AttachmentPoint.Default);
513
514 // default attachment point is currently the left hand.
515 Assert.That(sp.HasAttachments(), Is.True);
516 List<SceneObjectGroup> attachments = sp.GetAttachments();
517 Assert.That(attachments.Count, Is.EqualTo(1));
518 SceneObjectGroup attSo = attachments[0];
519 Assert.That(attSo.Name, Is.EqualTo(attItem2.Name));
520 Assert.That(attSo.AttachmentPoint, Is.EqualTo((byte)AttachmentPoint.LeftHand));
521 Assert.That(attSo.IsAttachment);
522
523 // Check appearance status
524 Assert.That(sp.Appearance.GetAttachments().Count, Is.EqualTo(1));
525 Assert.That(sp.Appearance.GetAttachpoint(attItem2.ID), Is.EqualTo((int)AttachmentPoint.LeftHand));
526 Assert.That(scene.GetSceneObjectGroups().Count, Is.EqualTo(1));
527
528 // Check events
529 Assert.That(m_numberOfAttachEventsFired, Is.EqualTo(3));
530 }
301 } 531 }
302 532
303 /// <summary> 533 /// <summary>
@@ -315,7 +545,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments.Tests
315 SceneObjectGroup so = SceneHelpers.CreateSceneObject(1, sp.UUID, "att-name", 0x10); 545 SceneObjectGroup so = SceneHelpers.CreateSceneObject(1, sp.UUID, "att-name", 0x10);
316 TaskInventoryItem scriptItem 546 TaskInventoryItem scriptItem
317 = TaskInventoryHelpers.AddScript( 547 = TaskInventoryHelpers.AddScript(
318 scene, 548 scene.AssetService,
319 so.RootPart, 549 so.RootPart,
320 "scriptItem", 550 "scriptItem",
321 "default { attach(key id) { if (id != NULL_KEY) { llSay(0, \"Hello World\"); } } }"); 551 "default { attach(key id) { if (id != NULL_KEY) { llSay(0, \"Hello World\"); } } }");
@@ -372,7 +602,9 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments.Tests
372 Assert.That(scene.InventoryService.GetItem(new InventoryItemBase(attItem.ID)), Is.Null); 602 Assert.That(scene.InventoryService.GetItem(new InventoryItemBase(attItem.ID)), Is.Null);
373 603
374 // Check object in scene 604 // Check object in scene
375 Assert.That(scene.GetSceneObjectGroup("att"), Is.Not.Null); 605 SceneObjectGroup soInScene = scene.GetSceneObjectGroup("att");
606 Assert.That(soInScene, Is.Not.Null);
607 Assert.IsTrue(soInScene.Backup);
376 608
377 // Check events 609 // Check events
378 Assert.That(m_numberOfAttachEventsFired, Is.EqualTo(1)); 610 Assert.That(m_numberOfAttachEventsFired, Is.EqualTo(1));
@@ -426,7 +658,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments.Tests
426 SceneObjectGroup so = SceneHelpers.CreateSceneObject(1, sp.UUID, "att-name", 0x10); 658 SceneObjectGroup so = SceneHelpers.CreateSceneObject(1, sp.UUID, "att-name", 0x10);
427 TaskInventoryItem scriptTaskItem 659 TaskInventoryItem scriptTaskItem
428 = TaskInventoryHelpers.AddScript( 660 = TaskInventoryHelpers.AddScript(
429 scene, 661 scene.AssetService,
430 so.RootPart, 662 so.RootPart,
431 "scriptItem", 663 "scriptItem",
432 "default { attach(key id) { if (id != NULL_KEY) { llSay(0, \"Hello World\"); } } }"); 664 "default { attach(key id) { if (id != NULL_KEY) { llSay(0, \"Hello World\"); } } }");
@@ -490,7 +722,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments.Tests
490 SceneObjectGroup rezzedAtt = presence.GetAttachments()[0]; 722 SceneObjectGroup rezzedAtt = presence.GetAttachments()[0];
491 723
492 m_numberOfAttachEventsFired = 0; 724 m_numberOfAttachEventsFired = 0;
493 scene.IncomingCloseAgent(presence.UUID, false); 725 scene.CloseAgent(presence.UUID, false);
494 726
495 // Check that we can't retrieve this attachment from the scene. 727 // Check that we can't retrieve this attachment from the scene.
496 Assert.That(scene.GetSceneObjectGroup(rezzedAtt.UUID), Is.Null); 728 Assert.That(scene.GetSceneObjectGroup(rezzedAtt.UUID), Is.Null);
@@ -503,7 +735,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments.Tests
503 public void TestRezAttachmentsOnAvatarEntrance() 735 public void TestRezAttachmentsOnAvatarEntrance()
504 { 736 {
505 TestHelpers.InMethod(); 737 TestHelpers.InMethod();
506// log4net.Config.XmlConfigurator.Configure(); 738// TestHelpers.EnableLogging();
507 739
508 Scene scene = CreateTestScene(); 740 Scene scene = CreateTestScene();
509 UserAccount ua1 = UserAccountHelpers.CreateUserWithInventory(scene, 0x1); 741 UserAccount ua1 = UserAccountHelpers.CreateUserWithInventory(scene, 0x1);
@@ -526,6 +758,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments.Tests
526 Assert.That(attSo.IsAttachment); 758 Assert.That(attSo.IsAttachment);
527 Assert.That(attSo.UsesPhysics, Is.False); 759 Assert.That(attSo.UsesPhysics, Is.False);
528 Assert.That(attSo.IsTemporary, Is.False); 760 Assert.That(attSo.IsTemporary, Is.False);
761 Assert.IsFalse(attSo.Backup);
529 762
530 // Check appearance status 763 // Check appearance status
531 List<AvatarAttachment> retreivedAttachments = presence.Appearance.GetAttachments(); 764 List<AvatarAttachment> retreivedAttachments = presence.Appearance.GetAttachments();
@@ -569,12 +802,17 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments.Tests
569 Assert.That(m_numberOfAttachEventsFired, Is.EqualTo(0)); 802 Assert.That(m_numberOfAttachEventsFired, Is.EqualTo(0));
570 } 803 }
571 804
805/*
572 [Test] 806 [Test]
573 public void TestSameSimulatorNeighbouringRegionsTeleport() 807 public void TestSameSimulatorNeighbouringRegionsTeleportV1()
574 { 808 {
575 TestHelpers.InMethod(); 809 TestHelpers.InMethod();
576// TestHelpers.EnableLogging(); 810// TestHelpers.EnableLogging();
577 811
812 BaseHttpServer httpServer = new BaseHttpServer(99999);
813 MainServer.AddHttpServer(httpServer);
814 MainServer.Instance = httpServer;
815
578 AttachmentsModule attModA = new AttachmentsModule(); 816 AttachmentsModule attModA = new AttachmentsModule();
579 AttachmentsModule attModB = new AttachmentsModule(); 817 AttachmentsModule attModB = new AttachmentsModule();
580 EntityTransferModule etmA = new EntityTransferModule(); 818 EntityTransferModule etmA = new EntityTransferModule();
@@ -603,8 +841,17 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments.Tests
603 SceneHelpers.SetupSceneModules( 841 SceneHelpers.SetupSceneModules(
604 sceneB, config, new CapabilitiesModule(), etmB, attModB, new BasicInventoryAccessModule()); 842 sceneB, config, new CapabilitiesModule(), etmB, attModB, new BasicInventoryAccessModule());
605 843
844 // FIXME: Hack - this is here temporarily to revert back to older entity transfer behaviour
845 lscm.ServiceVersion = 0.1f;
846
606 UserAccount ua1 = UserAccountHelpers.CreateUserWithInventory(sceneA, 0x1); 847 UserAccount ua1 = UserAccountHelpers.CreateUserWithInventory(sceneA, 0x1);
607 ScenePresence beforeTeleportSp = SceneHelpers.AddScenePresence(sceneA, ua1.PrincipalID, sh.SceneManager); 848
849 AgentCircuitData acd = SceneHelpers.GenerateAgentData(ua1.PrincipalID);
850 TestClient tc = new TestClient(acd, sceneA);
851 List<TestClient> destinationTestClients = new List<TestClient>();
852 EntityTransferHelpers.SetupInformClientOfNeighbourTriggersNeighbourClientCreate(tc, destinationTestClients);
853
854 ScenePresence beforeTeleportSp = SceneHelpers.AddScenePresence(sceneA, tc, acd);
608 beforeTeleportSp.AbsolutePosition = new Vector3(30, 31, 32); 855 beforeTeleportSp.AbsolutePosition = new Vector3(30, 31, 32);
609 856
610 InventoryItemBase attItem = CreateAttachmentItem(sceneA, ua1.PrincipalID, "att", 0x10, 0x20); 857 InventoryItemBase attItem = CreateAttachmentItem(sceneA, ua1.PrincipalID, "att", 0x10, 0x20);
@@ -623,7 +870,119 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments.Tests
623 teleportLookAt, 870 teleportLookAt,
624 (uint)TeleportFlags.ViaLocation); 871 (uint)TeleportFlags.ViaLocation);
625 872
626 ((TestClient)beforeTeleportSp.ControllingClient).CompleteTeleportClientSide(); 873 destinationTestClients[0].CompleteMovement();
874
875 // Check attachments have made it into sceneB
876 ScenePresence afterTeleportSceneBSp = sceneB.GetScenePresence(ua1.PrincipalID);
877
878 // This is appearance data, as opposed to actually rezzed attachments
879 List<AvatarAttachment> sceneBAttachments = afterTeleportSceneBSp.Appearance.GetAttachments();
880 Assert.That(sceneBAttachments.Count, Is.EqualTo(1));
881 Assert.That(sceneBAttachments[0].AttachPoint, Is.EqualTo((int)AttachmentPoint.Chest));
882 Assert.That(sceneBAttachments[0].ItemID, Is.EqualTo(attItem.ID));
883 Assert.That(sceneBAttachments[0].AssetID, Is.EqualTo(attItem.AssetID));
884 Assert.That(afterTeleportSceneBSp.Appearance.GetAttachpoint(attItem.ID), Is.EqualTo((int)AttachmentPoint.Chest));
885
886 // This is the actual attachment
887 List<SceneObjectGroup> actualSceneBAttachments = afterTeleportSceneBSp.GetAttachments();
888 Assert.That(actualSceneBAttachments.Count, Is.EqualTo(1));
889 SceneObjectGroup actualSceneBAtt = actualSceneBAttachments[0];
890 Assert.That(actualSceneBAtt.Name, Is.EqualTo(attItem.Name));
891 Assert.That(actualSceneBAtt.AttachmentPoint, Is.EqualTo((uint)AttachmentPoint.Chest));
892 Assert.IsFalse(actualSceneBAtt.Backup);
893
894 Assert.That(sceneB.GetSceneObjectGroups().Count, Is.EqualTo(1));
895
896 // Check attachments have been removed from sceneA
897 ScenePresence afterTeleportSceneASp = sceneA.GetScenePresence(ua1.PrincipalID);
898
899 // Since this is appearance data, it is still present on the child avatar!
900 List<AvatarAttachment> sceneAAttachments = afterTeleportSceneASp.Appearance.GetAttachments();
901 Assert.That(sceneAAttachments.Count, Is.EqualTo(1));
902 Assert.That(afterTeleportSceneASp.Appearance.GetAttachpoint(attItem.ID), Is.EqualTo((int)AttachmentPoint.Chest));
903
904 // This is the actual attachment, which should no longer exist
905 List<SceneObjectGroup> actualSceneAAttachments = afterTeleportSceneASp.GetAttachments();
906 Assert.That(actualSceneAAttachments.Count, Is.EqualTo(0));
907
908 Assert.That(sceneA.GetSceneObjectGroups().Count, Is.EqualTo(0));
909
910 // Check events
911 Assert.That(m_numberOfAttachEventsFired, Is.EqualTo(0));
912 }
913*/
914
915 [Test]
916 public void TestSameSimulatorNeighbouringRegionsTeleportV2()
917 {
918 TestHelpers.InMethod();
919// TestHelpers.EnableLogging();
920
921 BaseHttpServer httpServer = new BaseHttpServer(99999);
922 MainServer.AddHttpServer(httpServer);
923 MainServer.Instance = httpServer;
924
925 AttachmentsModule attModA = new AttachmentsModule();
926 AttachmentsModule attModB = new AttachmentsModule();
927 EntityTransferModule etmA = new EntityTransferModule();
928 EntityTransferModule etmB = new EntityTransferModule();
929 LocalSimulationConnectorModule lscm = new LocalSimulationConnectorModule();
930
931 IConfigSource config = new IniConfigSource();
932 IConfig modulesConfig = config.AddConfig("Modules");
933 modulesConfig.Set("EntityTransferModule", etmA.Name);
934 modulesConfig.Set("SimulationServices", lscm.Name);
935
936 modulesConfig.Set("InventoryAccessModule", "BasicInventoryAccessModule");
937
938 SceneHelpers sh = new SceneHelpers();
939 TestScene sceneA = sh.SetupScene("sceneA", TestHelpers.ParseTail(0x100), 1000, 1000);
940 TestScene sceneB = sh.SetupScene("sceneB", TestHelpers.ParseTail(0x200), 1001, 1000);
941
942 SceneHelpers.SetupSceneModules(new Scene[] { sceneA, sceneB }, config, lscm);
943 SceneHelpers.SetupSceneModules(
944 sceneA, config, new CapabilitiesModule(), etmA, attModA, new BasicInventoryAccessModule());
945 SceneHelpers.SetupSceneModules(
946 sceneB, config, new CapabilitiesModule(), etmB, attModB, new BasicInventoryAccessModule());
947
948 UserAccount ua1 = UserAccountHelpers.CreateUserWithInventory(sceneA, 0x1);
949
950 AgentCircuitData acd = SceneHelpers.GenerateAgentData(ua1.PrincipalID);
951 TestClient tc = new TestClient(acd, sceneA);
952 List<TestClient> destinationTestClients = new List<TestClient>();
953 EntityTransferHelpers.SetupInformClientOfNeighbourTriggersNeighbourClientCreate(tc, destinationTestClients);
954
955 ScenePresence beforeTeleportSp = SceneHelpers.AddScenePresence(sceneA, tc, acd);
956 beforeTeleportSp.AbsolutePosition = new Vector3(30, 31, 32);
957
958 Assert.That(destinationTestClients.Count, Is.EqualTo(1));
959 Assert.That(destinationTestClients[0], Is.Not.Null);
960
961 InventoryItemBase attItem = CreateAttachmentItem(sceneA, ua1.PrincipalID, "att", 0x10, 0x20);
962
963 sceneA.AttachmentsModule.RezSingleAttachmentFromInventory(
964 beforeTeleportSp, attItem.ID, (uint)AttachmentPoint.Chest);
965
966 Vector3 teleportPosition = new Vector3(10, 11, 12);
967 Vector3 teleportLookAt = new Vector3(20, 21, 22);
968
969 // Here, we need to make clientA's receipt of SendRegionTeleport trigger clientB's CompleteMovement(). This
970 // is to operate the teleport V2 mechanism where the EntityTransferModule will first request the client to
971 // CompleteMovement to the region and then call UpdateAgent to the destination region to confirm the receipt
972 // Both these operations will occur on different threads and will wait for each other.
973 // We have to do this via ThreadPool directly since FireAndForget has been switched to sync for the V1
974 // test protocol, where we are trying to avoid unpredictable async operations in regression tests.
975 tc.OnTestClientSendRegionTeleport
976 += (regionHandle, simAccess, regionExternalEndPoint, locationID, flags, capsURL)
977 => ThreadPool.UnsafeQueueUserWorkItem(o => destinationTestClients[0].CompleteMovement(), null);
978
979 m_numberOfAttachEventsFired = 0;
980 sceneA.RequestTeleportLocation(
981 beforeTeleportSp.ControllingClient,
982 sceneB.RegionInfo.RegionHandle,
983 teleportPosition,
984 teleportLookAt,
985 (uint)TeleportFlags.ViaLocation);
627 986
628 // Check attachments have made it into sceneB 987 // Check attachments have made it into sceneB
629 ScenePresence afterTeleportSceneBSp = sceneB.GetScenePresence(ua1.PrincipalID); 988 ScenePresence afterTeleportSceneBSp = sceneB.GetScenePresence(ua1.PrincipalID);
@@ -642,6 +1001,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments.Tests
642 SceneObjectGroup actualSceneBAtt = actualSceneBAttachments[0]; 1001 SceneObjectGroup actualSceneBAtt = actualSceneBAttachments[0];
643 Assert.That(actualSceneBAtt.Name, Is.EqualTo(attItem.Name)); 1002 Assert.That(actualSceneBAtt.Name, Is.EqualTo(attItem.Name));
644 Assert.That(actualSceneBAtt.AttachmentPoint, Is.EqualTo((uint)AttachmentPoint.Chest)); 1003 Assert.That(actualSceneBAtt.AttachmentPoint, Is.EqualTo((uint)AttachmentPoint.Chest));
1004 Assert.IsFalse(actualSceneBAtt.Backup);
645 1005
646 Assert.That(sceneB.GetSceneObjectGroups().Count, Is.EqualTo(1)); 1006 Assert.That(sceneB.GetSceneObjectGroups().Count, Is.EqualTo(1));
647 1007
@@ -663,4 +1023,4 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments.Tests
663 Assert.That(m_numberOfAttachEventsFired, Is.EqualTo(0)); 1023 Assert.That(m_numberOfAttachEventsFired, Is.EqualTo(0));
664 } 1024 }
665 } 1025 }
666} \ No newline at end of file 1026}