diff options
Diffstat (limited to 'OpenSim/Region/CoreModules/Avatar/Attachments')
-rw-r--r-- | OpenSim/Region/CoreModules/Avatar/Attachments/AttachmentsModule.cs | 792 | ||||
-rw-r--r-- | OpenSim/Region/CoreModules/Avatar/Attachments/Tests/AttachmentsModuleTests.cs | 430 |
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; | |||
29 | using System.Collections.Generic; | 29 | using System.Collections.Generic; |
30 | using System.Reflection; | 30 | using System.Reflection; |
31 | using System.IO; | 31 | using System.IO; |
32 | using System.Threading; | ||
32 | using System.Xml; | 33 | using System.Xml; |
33 | using log4net; | 34 | using log4net; |
34 | using Mono.Addins; | 35 | using 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; | |||
37 | using NUnit.Framework; | 37 | using NUnit.Framework; |
38 | using OpenMetaverse; | 38 | using OpenMetaverse; |
39 | using OpenSim.Framework; | 39 | using OpenSim.Framework; |
40 | using OpenSim.Framework.Communications; | 40 | using OpenSim.Framework.Servers; |
41 | using OpenSim.Framework.Servers.HttpServer; | ||
41 | using OpenSim.Region.CoreModules.Avatar.Attachments; | 42 | using OpenSim.Region.CoreModules.Avatar.Attachments; |
42 | using OpenSim.Region.CoreModules.Framework; | 43 | using OpenSim.Region.CoreModules.Framework; |
43 | using OpenSim.Region.CoreModules.Framework.EntityTransfer; | 44 | using OpenSim.Region.CoreModules.Framework.EntityTransfer; |
@@ -51,7 +52,6 @@ using OpenSim.Region.ScriptEngine.Interfaces; | |||
51 | using OpenSim.Region.ScriptEngine.XEngine; | 52 | using OpenSim.Region.ScriptEngine.XEngine; |
52 | using OpenSim.Services.Interfaces; | 53 | using OpenSim.Services.Interfaces; |
53 | using OpenSim.Tests.Common; | 54 | using OpenSim.Tests.Common; |
54 | using OpenSim.Tests.Common.Mock; | ||
55 | 55 | ||
56 | namespace OpenSim.Region.CoreModules.Avatar.Attachments.Tests | 56 | namespace 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 | } |