diff options
Diffstat (limited to 'OpenSim/Region/CoreModules/Avatar/Attachments/AttachmentsModule.cs')
-rw-r--r-- | OpenSim/Region/CoreModules/Avatar/Attachments/AttachmentsModule.cs | 792 |
1 files changed, 513 insertions, 279 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 | } |