diff options
Diffstat (limited to 'OpenSim/Region/CoreModules/Avatar')
34 files changed, 5038 insertions, 1184 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 | } |
diff --git a/OpenSim/Region/CoreModules/Avatar/AvatarFactory/AvatarFactoryModule.cs b/OpenSim/Region/CoreModules/Avatar/AvatarFactory/AvatarFactoryModule.cs index 0a69979..cfb082b 100644 --- a/OpenSim/Region/CoreModules/Avatar/AvatarFactory/AvatarFactoryModule.cs +++ b/OpenSim/Region/CoreModules/Avatar/AvatarFactory/AvatarFactoryModule.cs | |||
@@ -40,6 +40,7 @@ using OpenSim.Region.Framework.Scenes; | |||
40 | using OpenSim.Services.Interfaces; | 40 | using OpenSim.Services.Interfaces; |
41 | 41 | ||
42 | using Mono.Addins; | 42 | using Mono.Addins; |
43 | using PermissionMask = OpenSim.Framework.PermissionMask; | ||
43 | 44 | ||
44 | namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory | 45 | namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory |
45 | { | 46 | { |
@@ -54,6 +55,7 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory | |||
54 | 55 | ||
55 | private int m_savetime = 5; // seconds to wait before saving changed appearance | 56 | private int m_savetime = 5; // seconds to wait before saving changed appearance |
56 | private int m_sendtime = 2; // seconds to wait before sending changed appearance | 57 | private int m_sendtime = 2; // seconds to wait before sending changed appearance |
58 | private bool m_reusetextures = false; | ||
57 | 59 | ||
58 | private int m_checkTime = 500; // milliseconds to wait between checks for appearance updates | 60 | private int m_checkTime = 500; // milliseconds to wait between checks for appearance updates |
59 | private System.Timers.Timer m_updateTimer = new System.Timers.Timer(); | 61 | private System.Timers.Timer m_updateTimer = new System.Timers.Timer(); |
@@ -72,6 +74,8 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory | |||
72 | { | 74 | { |
73 | m_savetime = Convert.ToInt32(appearanceConfig.GetString("DelayBeforeAppearanceSave",Convert.ToString(m_savetime))); | 75 | m_savetime = Convert.ToInt32(appearanceConfig.GetString("DelayBeforeAppearanceSave",Convert.ToString(m_savetime))); |
74 | m_sendtime = Convert.ToInt32(appearanceConfig.GetString("DelayBeforeAppearanceSend",Convert.ToString(m_sendtime))); | 76 | m_sendtime = Convert.ToInt32(appearanceConfig.GetString("DelayBeforeAppearanceSend",Convert.ToString(m_sendtime))); |
77 | m_reusetextures = appearanceConfig.GetBoolean("ReuseTextures",m_reusetextures); | ||
78 | |||
75 | // m_log.InfoFormat("[AVFACTORY] configured for {0} save and {1} send",m_savetime,m_sendtime); | 79 | // m_log.InfoFormat("[AVFACTORY] configured for {0} save and {1} send",m_savetime,m_sendtime); |
76 | } | 80 | } |
77 | 81 | ||
@@ -130,6 +134,7 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory | |||
130 | client.OnRequestWearables += Client_OnRequestWearables; | 134 | client.OnRequestWearables += Client_OnRequestWearables; |
131 | client.OnSetAppearance += Client_OnSetAppearance; | 135 | client.OnSetAppearance += Client_OnSetAppearance; |
132 | client.OnAvatarNowWearing += Client_OnAvatarNowWearing; | 136 | client.OnAvatarNowWearing += Client_OnAvatarNowWearing; |
137 | client.OnCachedTextureRequest += Client_OnCachedTextureRequest; | ||
133 | } | 138 | } |
134 | 139 | ||
135 | #endregion | 140 | #endregion |
@@ -140,9 +145,24 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory | |||
140 | /// <param name="sp"></param> | 145 | /// <param name="sp"></param> |
141 | /// <param name="texture"></param> | 146 | /// <param name="texture"></param> |
142 | /// <param name="visualParam"></param> | 147 | /// <param name="visualParam"></param> |
143 | public void SetAppearance(IScenePresence sp, AvatarAppearance appearance) | 148 | public void SetAppearance(IScenePresence sp, AvatarAppearance appearance, WearableCacheItem[] cacheItems) |
144 | { | 149 | { |
145 | SetAppearance(sp, appearance.Texture, appearance.VisualParams); | 150 | SetAppearance(sp, appearance.Texture, appearance.VisualParams, cacheItems); |
151 | } | ||
152 | |||
153 | |||
154 | public void SetAppearance(IScenePresence sp, Primitive.TextureEntry textureEntry, byte[] visualParams, Vector3 avSize, WearableCacheItem[] cacheItems) | ||
155 | { | ||
156 | float oldoff = sp.Appearance.AvatarFeetOffset; | ||
157 | Vector3 oldbox = sp.Appearance.AvatarBoxSize; | ||
158 | |||
159 | SetAppearance(sp, textureEntry, visualParams, cacheItems); | ||
160 | sp.Appearance.SetSize(avSize); | ||
161 | |||
162 | float off = sp.Appearance.AvatarFeetOffset; | ||
163 | Vector3 box = sp.Appearance.AvatarBoxSize; | ||
164 | if (oldoff != off || oldbox != box) | ||
165 | ((ScenePresence)sp).SetSize(box, off); | ||
146 | } | 166 | } |
147 | 167 | ||
148 | /// <summary> | 168 | /// <summary> |
@@ -151,7 +171,7 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory | |||
151 | /// <param name="sp"></param> | 171 | /// <param name="sp"></param> |
152 | /// <param name="texture"></param> | 172 | /// <param name="texture"></param> |
153 | /// <param name="visualParam"></param> | 173 | /// <param name="visualParam"></param> |
154 | public void SetAppearance(IScenePresence sp, Primitive.TextureEntry textureEntry, byte[] visualParams) | 174 | public void SetAppearance(IScenePresence sp, Primitive.TextureEntry textureEntry, byte[] visualParams, WearableCacheItem[] cacheItems) |
155 | { | 175 | { |
156 | // m_log.DebugFormat( | 176 | // m_log.DebugFormat( |
157 | // "[AVFACTORY]: start SetAppearance for {0}, te {1}, visualParams {2}", | 177 | // "[AVFACTORY]: start SetAppearance for {0}, te {1}, visualParams {2}", |
@@ -174,18 +194,27 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory | |||
174 | // m_log.DebugFormat( | 194 | // m_log.DebugFormat( |
175 | // "[AVFACTORY]: Setting visual params for {0} to {1}", | 195 | // "[AVFACTORY]: Setting visual params for {0} to {1}", |
176 | // client.Name, string.Join(", ", visualParamsStrings)); | 196 | // client.Name, string.Join(", ", visualParamsStrings)); |
177 | 197 | /* | |
178 | float oldHeight = sp.Appearance.AvatarHeight; | 198 | float oldHeight = sp.Appearance.AvatarHeight; |
179 | changed = sp.Appearance.SetVisualParams(visualParams); | 199 | changed = sp.Appearance.SetVisualParams(visualParams); |
180 | 200 | ||
181 | if (sp.Appearance.AvatarHeight != oldHeight && sp.Appearance.AvatarHeight > 0) | 201 | if (sp.Appearance.AvatarHeight != oldHeight && sp.Appearance.AvatarHeight > 0) |
182 | ((ScenePresence)sp).SetHeight(sp.Appearance.AvatarHeight); | 202 | ((ScenePresence)sp).SetHeight(sp.Appearance.AvatarHeight); |
183 | } | 203 | */ |
204 | // float oldoff = sp.Appearance.AvatarFeetOffset; | ||
205 | // Vector3 oldbox = sp.Appearance.AvatarBoxSize; | ||
206 | changed = sp.Appearance.SetVisualParams(visualParams); | ||
207 | // float off = sp.Appearance.AvatarFeetOffset; | ||
208 | // Vector3 box = sp.Appearance.AvatarBoxSize; | ||
209 | // if(oldoff != off || oldbox != box) | ||
210 | // ((ScenePresence)sp).SetSize(box,off); | ||
184 | 211 | ||
212 | } | ||
213 | |||
185 | // Process the baked texture array | 214 | // Process the baked texture array |
186 | if (textureEntry != null) | 215 | if (textureEntry != null) |
187 | { | 216 | { |
188 | // m_log.DebugFormat("[AVFACTORY]: Received texture update for {0} {1}", sp.Name, sp.UUID); | 217 | m_log.DebugFormat("[AVFACTORY]: Received texture update for {0} {1}", sp.Name, sp.UUID); |
189 | 218 | ||
190 | // WriteBakedTexturesReport(sp, m_log.DebugFormat); | 219 | // WriteBakedTexturesReport(sp, m_log.DebugFormat); |
191 | 220 | ||
@@ -222,7 +251,7 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory | |||
222 | private void SendAppearance(ScenePresence sp) | 251 | private void SendAppearance(ScenePresence sp) |
223 | { | 252 | { |
224 | // Send the appearance to everyone in the scene | 253 | // Send the appearance to everyone in the scene |
225 | sp.SendAppearanceToAllOtherAgents(); | 254 | sp.SendAppearanceToAllOtherClients(); |
226 | 255 | ||
227 | // Send animations back to the avatar as well | 256 | // Send animations back to the avatar as well |
228 | sp.Animator.SendAnimPack(); | 257 | sp.Animator.SendAnimPack(); |
@@ -254,6 +283,17 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory | |||
254 | return GetBakedTextureFaces(sp); | 283 | return GetBakedTextureFaces(sp); |
255 | } | 284 | } |
256 | 285 | ||
286 | public WearableCacheItem[] GetCachedItems(UUID agentId) | ||
287 | { | ||
288 | ScenePresence sp = m_scene.GetScenePresence(agentId); | ||
289 | WearableCacheItem[] items = sp.Appearance.WearableCacheItems; | ||
290 | //foreach (WearableCacheItem item in items) | ||
291 | //{ | ||
292 | |||
293 | //} | ||
294 | return items; | ||
295 | } | ||
296 | |||
257 | public bool SaveBakedTextures(UUID agentId) | 297 | public bool SaveBakedTextures(UUID agentId) |
258 | { | 298 | { |
259 | ScenePresence sp = m_scene.GetScenePresence(agentId); | 299 | ScenePresence sp = m_scene.GetScenePresence(agentId); |
@@ -287,6 +327,9 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory | |||
287 | 327 | ||
288 | if (asset != null) | 328 | if (asset != null) |
289 | { | 329 | { |
330 | // Replace an HG ID with the simple asset ID so that we can persist textures for foreign HG avatars | ||
331 | asset.ID = asset.FullID.ToString(); | ||
332 | |||
290 | asset.Temporary = false; | 333 | asset.Temporary = false; |
291 | asset.Local = false; | 334 | asset.Local = false; |
292 | m_scene.AssetService.Store(asset); | 335 | m_scene.AssetService.Store(asset); |
@@ -323,7 +366,7 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory | |||
323 | 366 | ||
324 | public void QueueAppearanceSave(UUID agentid) | 367 | public void QueueAppearanceSave(UUID agentid) |
325 | { | 368 | { |
326 | // m_log.WarnFormat("[AVFACTORY]: Queue appearance save for {0}", agentid); | 369 | // m_log.DebugFormat("[AVFACTORY]: Queueing appearance save for {0}", agentid); |
327 | 370 | ||
328 | // 10000 ticks per millisecond, 1000 milliseconds per second | 371 | // 10000 ticks per millisecond, 1000 milliseconds per second |
329 | long timestamp = DateTime.Now.Ticks + Convert.ToInt64(m_savetime * 1000 * 10000); | 372 | long timestamp = DateTime.Now.Ticks + Convert.ToInt64(m_savetime * 1000 * 10000); |
@@ -337,6 +380,53 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory | |||
337 | public bool ValidateBakedTextureCache(IScenePresence sp) | 380 | public bool ValidateBakedTextureCache(IScenePresence sp) |
338 | { | 381 | { |
339 | bool defonly = true; // are we only using default textures | 382 | bool defonly = true; // are we only using default textures |
383 | IImprovedAssetCache cache = m_scene.RequestModuleInterface<IImprovedAssetCache>(); | ||
384 | IBakedTextureModule bakedModule = m_scene.RequestModuleInterface<IBakedTextureModule>(); | ||
385 | WearableCacheItem[] wearableCache = null; | ||
386 | |||
387 | // Cache wearable data for teleport. | ||
388 | // Only makes sense if there's a bake module and a cache module | ||
389 | if (bakedModule != null && cache != null) | ||
390 | { | ||
391 | try | ||
392 | { | ||
393 | wearableCache = bakedModule.Get(sp.UUID); | ||
394 | } | ||
395 | catch (Exception) | ||
396 | { | ||
397 | |||
398 | } | ||
399 | if (wearableCache != null) | ||
400 | { | ||
401 | for (int i = 0; i < wearableCache.Length; i++) | ||
402 | { | ||
403 | cache.Cache(wearableCache[i].TextureAsset); | ||
404 | } | ||
405 | } | ||
406 | } | ||
407 | /* | ||
408 | IBakedTextureModule bakedModule = m_scene.RequestModuleInterface<IBakedTextureModule>(); | ||
409 | if (invService.GetRootFolder(userID) != null) | ||
410 | { | ||
411 | WearableCacheItem[] wearableCache = null; | ||
412 | if (bakedModule != null) | ||
413 | { | ||
414 | try | ||
415 | { | ||
416 | wearableCache = bakedModule.Get(userID); | ||
417 | appearance.WearableCacheItems = wearableCache; | ||
418 | appearance.WearableCacheItemsDirty = false; | ||
419 | foreach (WearableCacheItem item in wearableCache) | ||
420 | { | ||
421 | appearance.Texture.FaceTextures[item.TextureIndex].TextureID = item.TextureID; | ||
422 | } | ||
423 | } | ||
424 | catch (Exception) | ||
425 | { | ||
426 | |||
427 | } | ||
428 | } | ||
429 | */ | ||
340 | 430 | ||
341 | // Process the texture entry | 431 | // Process the texture entry |
342 | for (int i = 0; i < AvatarAppearance.BAKE_INDICES.Length; i++) | 432 | for (int i = 0; i < AvatarAppearance.BAKE_INDICES.Length; i++) |
@@ -344,10 +434,32 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory | |||
344 | int idx = AvatarAppearance.BAKE_INDICES[i]; | 434 | int idx = AvatarAppearance.BAKE_INDICES[i]; |
345 | Primitive.TextureEntryFace face = sp.Appearance.Texture.FaceTextures[idx]; | 435 | Primitive.TextureEntryFace face = sp.Appearance.Texture.FaceTextures[idx]; |
346 | 436 | ||
347 | // if there is no texture entry, skip it | 437 | // No face, so lets check our baked service cache, teleport or login. |
348 | if (face == null) | 438 | if (face == null) |
349 | continue; | 439 | { |
350 | 440 | if (wearableCache != null) | |
441 | { | ||
442 | // If we find the an appearance item, set it as the textureentry and the face | ||
443 | WearableCacheItem searchitem = WearableCacheItem.SearchTextureIndex((uint) idx, wearableCache); | ||
444 | if (searchitem != null) | ||
445 | { | ||
446 | sp.Appearance.Texture.FaceTextures[idx] = sp.Appearance.Texture.CreateFace((uint) idx); | ||
447 | sp.Appearance.Texture.FaceTextures[idx].TextureID = searchitem.TextureID; | ||
448 | face = sp.Appearance.Texture.FaceTextures[idx]; | ||
449 | } | ||
450 | else | ||
451 | { | ||
452 | // if there is no texture entry and no baked cache, skip it | ||
453 | continue; | ||
454 | } | ||
455 | } | ||
456 | else | ||
457 | { | ||
458 | //No texture entry face and no cache. Skip this face. | ||
459 | continue; | ||
460 | } | ||
461 | } | ||
462 | |||
351 | // m_log.DebugFormat( | 463 | // m_log.DebugFormat( |
352 | // "[AVFACTORY]: Looking for texture {0}, id {1} for {2} {3}", | 464 | // "[AVFACTORY]: Looking for texture {0}, id {1} for {2} {3}", |
353 | // face.TextureID, idx, client.Name, client.AgentId); | 465 | // face.TextureID, idx, client.Name, client.AgentId); |
@@ -374,6 +486,7 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory | |||
374 | public int RequestRebake(IScenePresence sp, bool missingTexturesOnly) | 486 | public int RequestRebake(IScenePresence sp, bool missingTexturesOnly) |
375 | { | 487 | { |
376 | int texturesRebaked = 0; | 488 | int texturesRebaked = 0; |
489 | // IImprovedAssetCache cache = m_scene.RequestModuleInterface<IImprovedAssetCache>(); | ||
377 | 490 | ||
378 | for (int i = 0; i < AvatarAppearance.BAKE_INDICES.Length; i++) | 491 | for (int i = 0; i < AvatarAppearance.BAKE_INDICES.Length; i++) |
379 | { | 492 | { |
@@ -480,7 +593,7 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory | |||
480 | 593 | ||
481 | if (sendTime < now) | 594 | if (sendTime < now) |
482 | { | 595 | { |
483 | Util.FireAndForget(o => SendAppearance(avatarID)); | 596 | Util.FireAndForget(o => SendAppearance(avatarID), null, "AvatarFactoryModule.SendAppearance"); |
484 | m_sendqueue.Remove(avatarID); | 597 | m_sendqueue.Remove(avatarID); |
485 | } | 598 | } |
486 | } | 599 | } |
@@ -498,7 +611,7 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory | |||
498 | 611 | ||
499 | if (sendTime < now) | 612 | if (sendTime < now) |
500 | { | 613 | { |
501 | Util.FireAndForget(o => SaveAppearance(avatarID)); | 614 | Util.FireAndForget(o => SaveAppearance(avatarID), null, "AvatarFactoryModule.SaveAppearance"); |
502 | m_savequeue.Remove(avatarID); | 615 | m_savequeue.Remove(avatarID); |
503 | } | 616 | } |
504 | } | 617 | } |
@@ -526,7 +639,7 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory | |||
526 | return; | 639 | return; |
527 | } | 640 | } |
528 | 641 | ||
529 | // m_log.WarnFormat("[AVFACTORY] avatar {0} save appearance",agentid); | 642 | // m_log.DebugFormat("[AVFACTORY]: Saving appearance for avatar {0}", agentid); |
530 | 643 | ||
531 | // This could take awhile since it needs to pull inventory | 644 | // This could take awhile since it needs to pull inventory |
532 | // We need to do it at the point of save so that there is a sufficient delay for any upload of new body part/shape | 645 | // We need to do it at the point of save so that there is a sufficient delay for any upload of new body part/shape |
@@ -535,6 +648,14 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory | |||
535 | // multiple save requests. | 648 | // multiple save requests. |
536 | SetAppearanceAssets(sp.UUID, sp.Appearance); | 649 | SetAppearanceAssets(sp.UUID, sp.Appearance); |
537 | 650 | ||
651 | // List<AvatarAttachment> attachments = sp.Appearance.GetAttachments(); | ||
652 | // foreach (AvatarAttachment att in attachments) | ||
653 | // { | ||
654 | // m_log.DebugFormat( | ||
655 | // "[AVFACTORY]: For {0} saving attachment {1} at point {2}", | ||
656 | // sp.Name, att.ItemID, att.AttachPoint); | ||
657 | // } | ||
658 | |||
538 | m_scene.AvatarService.SetAppearance(agentid, sp.Appearance); | 659 | m_scene.AvatarService.SetAppearance(agentid, sp.Appearance); |
539 | 660 | ||
540 | // Trigger this here because it's the final step in the set/queue/save process for appearance setting. | 661 | // Trigger this here because it's the final step in the set/queue/save process for appearance setting. |
@@ -542,6 +663,12 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory | |||
542 | m_scene.EventManager.TriggerAvatarAppearanceChanged(sp); | 663 | m_scene.EventManager.TriggerAvatarAppearanceChanged(sp); |
543 | } | 664 | } |
544 | 665 | ||
666 | /// <summary> | ||
667 | /// For a given set of appearance items, check whether the items are valid and add their asset IDs to | ||
668 | /// appearance data. | ||
669 | /// </summary> | ||
670 | /// <param name='userID'></param> | ||
671 | /// <param name='appearance'></param> | ||
545 | private void SetAppearanceAssets(UUID userID, AvatarAppearance appearance) | 672 | private void SetAppearanceAssets(UUID userID, AvatarAppearance appearance) |
546 | { | 673 | { |
547 | IInventoryService invService = m_scene.InventoryService; | 674 | IInventoryService invService = m_scene.InventoryService; |
@@ -553,7 +680,13 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory | |||
553 | for (int j = 0; j < appearance.Wearables[i].Count; j++) | 680 | for (int j = 0; j < appearance.Wearables[i].Count; j++) |
554 | { | 681 | { |
555 | if (appearance.Wearables[i][j].ItemID == UUID.Zero) | 682 | if (appearance.Wearables[i][j].ItemID == UUID.Zero) |
683 | { | ||
684 | m_log.WarnFormat( | ||
685 | "[AVFACTORY]: Wearable item {0}:{1} for user {2} unexpectedly UUID.Zero. Ignoring.", | ||
686 | i, j, userID); | ||
687 | |||
556 | continue; | 688 | continue; |
689 | } | ||
557 | 690 | ||
558 | // Ignore ruth's assets | 691 | // Ignore ruth's assets |
559 | if (appearance.Wearables[i][j].ItemID == AvatarWearable.DefaultWearables[i][0].ItemID) | 692 | if (appearance.Wearables[i][j].ItemID == AvatarWearable.DefaultWearables[i][0].ItemID) |
@@ -568,7 +701,7 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory | |||
568 | } | 701 | } |
569 | else | 702 | else |
570 | { | 703 | { |
571 | m_log.ErrorFormat( | 704 | m_log.WarnFormat( |
572 | "[AVFACTORY]: Can't find inventory item {0} for {1}, setting to default", | 705 | "[AVFACTORY]: Can't find inventory item {0} for {1}, setting to default", |
573 | appearance.Wearables[i][j].ItemID, (WearableType)i); | 706 | appearance.Wearables[i][j].ItemID, (WearableType)i); |
574 | 707 | ||
@@ -581,8 +714,311 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory | |||
581 | { | 714 | { |
582 | m_log.WarnFormat("[AVFACTORY]: user {0} has no inventory, appearance isn't going to work", userID); | 715 | m_log.WarnFormat("[AVFACTORY]: user {0} has no inventory, appearance isn't going to work", userID); |
583 | } | 716 | } |
717 | |||
718 | // IInventoryService invService = m_scene.InventoryService; | ||
719 | // bool resetwearable = false; | ||
720 | // if (invService.GetRootFolder(userID) != null) | ||
721 | // { | ||
722 | // for (int i = 0; i < AvatarWearable.MAX_WEARABLES; i++) | ||
723 | // { | ||
724 | // for (int j = 0; j < appearance.Wearables[i].Count; j++) | ||
725 | // { | ||
726 | // // Check if the default wearables are not set | ||
727 | // if (appearance.Wearables[i][j].ItemID == UUID.Zero) | ||
728 | // { | ||
729 | // switch ((WearableType) i) | ||
730 | // { | ||
731 | // case WearableType.Eyes: | ||
732 | // case WearableType.Hair: | ||
733 | // case WearableType.Shape: | ||
734 | // case WearableType.Skin: | ||
735 | // //case WearableType.Underpants: | ||
736 | // TryAndRepairBrokenWearable((WearableType)i, invService, userID, appearance); | ||
737 | // resetwearable = true; | ||
738 | // m_log.Warn("[AVFACTORY]: UUID.Zero Wearables, passing fake values."); | ||
739 | // resetwearable = true; | ||
740 | // break; | ||
741 | // | ||
742 | // } | ||
743 | // continue; | ||
744 | // } | ||
745 | // | ||
746 | // // Ignore ruth's assets except for the body parts! missing body parts fail avatar appearance on V1 | ||
747 | // if (appearance.Wearables[i][j].ItemID == AvatarWearable.DefaultWearables[i][0].ItemID) | ||
748 | // { | ||
749 | // switch ((WearableType)i) | ||
750 | // { | ||
751 | // case WearableType.Eyes: | ||
752 | // case WearableType.Hair: | ||
753 | // case WearableType.Shape: | ||
754 | // case WearableType.Skin: | ||
755 | // //case WearableType.Underpants: | ||
756 | // TryAndRepairBrokenWearable((WearableType)i, invService, userID, appearance); | ||
757 | // | ||
758 | // m_log.WarnFormat("[AVFACTORY]: {0} Default Wearables, passing existing values.", (WearableType)i); | ||
759 | // resetwearable = true; | ||
760 | // break; | ||
761 | // | ||
762 | // } | ||
763 | // continue; | ||
764 | // } | ||
765 | // | ||
766 | // InventoryItemBase baseItem = new InventoryItemBase(appearance.Wearables[i][j].ItemID, userID); | ||
767 | // baseItem = invService.GetItem(baseItem); | ||
768 | // | ||
769 | // if (baseItem != null) | ||
770 | // { | ||
771 | // appearance.Wearables[i].Add(appearance.Wearables[i][j].ItemID, baseItem.AssetID); | ||
772 | // int unmodifiedWearableIndexForClosure = i; | ||
773 | // m_scene.AssetService.Get(baseItem.AssetID.ToString(), this, | ||
774 | // delegate(string x, object y, AssetBase z) | ||
775 | // { | ||
776 | // if (z == null) | ||
777 | // { | ||
778 | // TryAndRepairBrokenWearable( | ||
779 | // (WearableType)unmodifiedWearableIndexForClosure, invService, | ||
780 | // userID, appearance); | ||
781 | // } | ||
782 | // }); | ||
783 | // } | ||
784 | // else | ||
785 | // { | ||
786 | // m_log.ErrorFormat( | ||
787 | // "[AVFACTORY]: Can't find inventory item {0} for {1}, setting to default", | ||
788 | // appearance.Wearables[i][j].ItemID, (WearableType)i); | ||
789 | // | ||
790 | // TryAndRepairBrokenWearable((WearableType)i, invService, userID, appearance); | ||
791 | // resetwearable = true; | ||
792 | // | ||
793 | // } | ||
794 | // } | ||
795 | // } | ||
796 | // | ||
797 | // // I don't know why we have to test for this again... but the above switches do not capture these scenarios for some reason.... | ||
798 | // if (appearance.Wearables[(int) WearableType.Eyes] == null) | ||
799 | // { | ||
800 | // m_log.WarnFormat("[AVFACTORY]: {0} Eyes are Null, passing existing values.", (WearableType.Eyes)); | ||
801 | // | ||
802 | // TryAndRepairBrokenWearable(WearableType.Eyes, invService, userID, appearance); | ||
803 | // resetwearable = true; | ||
804 | // } | ||
805 | // else | ||
806 | // { | ||
807 | // if (appearance.Wearables[(int) WearableType.Eyes][0].ItemID == UUID.Zero) | ||
808 | // { | ||
809 | // m_log.WarnFormat("[AVFACTORY]: Eyes are UUID.Zero are broken, {0} {1}", | ||
810 | // appearance.Wearables[(int) WearableType.Eyes][0].ItemID, | ||
811 | // appearance.Wearables[(int) WearableType.Eyes][0].AssetID); | ||
812 | // TryAndRepairBrokenWearable(WearableType.Eyes, invService, userID, appearance); | ||
813 | // resetwearable = true; | ||
814 | // | ||
815 | // } | ||
816 | // | ||
817 | // } | ||
818 | // // I don't know why we have to test for this again... but the above switches do not capture these scenarios for some reason.... | ||
819 | // if (appearance.Wearables[(int)WearableType.Shape] == null) | ||
820 | // { | ||
821 | // m_log.WarnFormat("[AVFACTORY]: {0} shape is Null, passing existing values.", (WearableType.Shape)); | ||
822 | // | ||
823 | // TryAndRepairBrokenWearable(WearableType.Shape, invService, userID, appearance); | ||
824 | // resetwearable = true; | ||
825 | // } | ||
826 | // else | ||
827 | // { | ||
828 | // if (appearance.Wearables[(int)WearableType.Shape][0].ItemID == UUID.Zero) | ||
829 | // { | ||
830 | // m_log.WarnFormat("[AVFACTORY]: Shape is UUID.Zero and broken, {0} {1}", | ||
831 | // appearance.Wearables[(int)WearableType.Shape][0].ItemID, | ||
832 | // appearance.Wearables[(int)WearableType.Shape][0].AssetID); | ||
833 | // TryAndRepairBrokenWearable(WearableType.Shape, invService, userID, appearance); | ||
834 | // resetwearable = true; | ||
835 | // | ||
836 | // } | ||
837 | // | ||
838 | // } | ||
839 | // // I don't know why we have to test for this again... but the above switches do not capture these scenarios for some reason.... | ||
840 | // if (appearance.Wearables[(int)WearableType.Hair] == null) | ||
841 | // { | ||
842 | // m_log.WarnFormat("[AVFACTORY]: {0} Hair is Null, passing existing values.", (WearableType.Hair)); | ||
843 | // | ||
844 | // TryAndRepairBrokenWearable(WearableType.Hair, invService, userID, appearance); | ||
845 | // resetwearable = true; | ||
846 | // } | ||
847 | // else | ||
848 | // { | ||
849 | // if (appearance.Wearables[(int)WearableType.Hair][0].ItemID == UUID.Zero) | ||
850 | // { | ||
851 | // m_log.WarnFormat("[AVFACTORY]: Hair is UUID.Zero and broken, {0} {1}", | ||
852 | // appearance.Wearables[(int)WearableType.Hair][0].ItemID, | ||
853 | // appearance.Wearables[(int)WearableType.Hair][0].AssetID); | ||
854 | // TryAndRepairBrokenWearable(WearableType.Hair, invService, userID, appearance); | ||
855 | // resetwearable = true; | ||
856 | // | ||
857 | // } | ||
858 | // | ||
859 | // } | ||
860 | // // I don't know why we have to test for this again... but the above switches do not capture these scenarios for some reason.... | ||
861 | // if (appearance.Wearables[(int)WearableType.Skin] == null) | ||
862 | // { | ||
863 | // m_log.WarnFormat("[AVFACTORY]: {0} Skin is Null, passing existing values.", (WearableType.Skin)); | ||
864 | // | ||
865 | // TryAndRepairBrokenWearable(WearableType.Skin, invService, userID, appearance); | ||
866 | // resetwearable = true; | ||
867 | // } | ||
868 | // else | ||
869 | // { | ||
870 | // if (appearance.Wearables[(int)WearableType.Skin][0].ItemID == UUID.Zero) | ||
871 | // { | ||
872 | // m_log.WarnFormat("[AVFACTORY]: Skin is UUID.Zero and broken, {0} {1}", | ||
873 | // appearance.Wearables[(int)WearableType.Skin][0].ItemID, | ||
874 | // appearance.Wearables[(int)WearableType.Skin][0].AssetID); | ||
875 | // TryAndRepairBrokenWearable(WearableType.Skin, invService, userID, appearance); | ||
876 | // resetwearable = true; | ||
877 | // | ||
878 | // } | ||
879 | // | ||
880 | // } | ||
881 | // if (resetwearable) | ||
882 | // { | ||
883 | // ScenePresence presence = null; | ||
884 | // if (m_scene.TryGetScenePresence(userID, out presence)) | ||
885 | // { | ||
886 | // presence.ControllingClient.SendWearables(presence.Appearance.Wearables, | ||
887 | // presence.Appearance.Serial++); | ||
888 | // } | ||
889 | // } | ||
890 | // | ||
891 | // } | ||
892 | // else | ||
893 | // { | ||
894 | // m_log.WarnFormat("[AVFACTORY]: user {0} has no inventory, appearance isn't going to work", userID); | ||
895 | // } | ||
896 | } | ||
897 | |||
898 | private void TryAndRepairBrokenWearable(WearableType type, IInventoryService invService, UUID userID,AvatarAppearance appearance) | ||
899 | { | ||
900 | UUID defaultwearable = GetDefaultItem(type); | ||
901 | if (defaultwearable != UUID.Zero) | ||
902 | { | ||
903 | UUID newInvItem = UUID.Random(); | ||
904 | InventoryItemBase itembase = new InventoryItemBase(newInvItem, userID) | ||
905 | { | ||
906 | AssetID = | ||
907 | defaultwearable, | ||
908 | AssetType | ||
909 | = | ||
910 | (int) | ||
911 | FolderType | ||
912 | .BodyPart, | ||
913 | CreatorId | ||
914 | = | ||
915 | userID | ||
916 | .ToString | ||
917 | (), | ||
918 | //InvType = (int)InventoryType.Wearable, | ||
919 | |||
920 | Description | ||
921 | = | ||
922 | "Failed Wearable Replacement", | ||
923 | Folder = | ||
924 | invService | ||
925 | .GetFolderForType | ||
926 | (userID, | ||
927 | FolderType | ||
928 | .BodyPart) | ||
929 | .ID, | ||
930 | Flags = (uint) type, | ||
931 | Name = Enum.GetName(typeof (WearableType), type), | ||
932 | BasePermissions = (uint) PermissionMask.Copy, | ||
933 | CurrentPermissions = (uint) PermissionMask.Copy, | ||
934 | EveryOnePermissions = (uint) PermissionMask.Copy, | ||
935 | GroupPermissions = (uint) PermissionMask.Copy, | ||
936 | NextPermissions = (uint) PermissionMask.Copy | ||
937 | }; | ||
938 | invService.AddItem(itembase); | ||
939 | UUID LinkInvItem = UUID.Random(); | ||
940 | itembase = new InventoryItemBase(LinkInvItem, userID) | ||
941 | { | ||
942 | AssetID = | ||
943 | newInvItem, | ||
944 | AssetType | ||
945 | = | ||
946 | (int) | ||
947 | AssetType | ||
948 | .Link, | ||
949 | CreatorId | ||
950 | = | ||
951 | userID | ||
952 | .ToString | ||
953 | (), | ||
954 | InvType = (int) InventoryType.Wearable, | ||
955 | |||
956 | Description | ||
957 | = | ||
958 | "Failed Wearable Replacement", | ||
959 | Folder = | ||
960 | invService | ||
961 | .GetFolderForType | ||
962 | (userID, | ||
963 | FolderType | ||
964 | .CurrentOutfit) | ||
965 | .ID, | ||
966 | Flags = (uint) type, | ||
967 | Name = Enum.GetName(typeof (WearableType), type), | ||
968 | BasePermissions = (uint) PermissionMask.Copy, | ||
969 | CurrentPermissions = (uint) PermissionMask.Copy, | ||
970 | EveryOnePermissions = (uint) PermissionMask.Copy, | ||
971 | GroupPermissions = (uint) PermissionMask.Copy, | ||
972 | NextPermissions = (uint) PermissionMask.Copy | ||
973 | }; | ||
974 | invService.AddItem(itembase); | ||
975 | appearance.Wearables[(int)type] = new AvatarWearable(newInvItem, GetDefaultItem(type)); | ||
976 | ScenePresence presence = null; | ||
977 | if (m_scene.TryGetScenePresence(userID, out presence)) | ||
978 | { | ||
979 | m_scene.SendInventoryUpdate(presence.ControllingClient, | ||
980 | invService.GetFolderForType(userID, | ||
981 | FolderType | ||
982 | .CurrentOutfit), | ||
983 | false, true); | ||
984 | } | ||
985 | } | ||
584 | } | 986 | } |
585 | 987 | ||
988 | private UUID GetDefaultItem(WearableType wearable) | ||
989 | { | ||
990 | // These are ruth | ||
991 | UUID ret = UUID.Zero; | ||
992 | switch (wearable) | ||
993 | { | ||
994 | case WearableType.Eyes: | ||
995 | ret = new UUID("4bb6fa4d-1cd2-498a-a84c-95c1a0e745a7"); | ||
996 | break; | ||
997 | case WearableType.Hair: | ||
998 | ret = new UUID("d342e6c0-b9d2-11dc-95ff-0800200c9a66"); | ||
999 | break; | ||
1000 | case WearableType.Pants: | ||
1001 | ret = new UUID("00000000-38f9-1111-024e-222222111120"); | ||
1002 | break; | ||
1003 | case WearableType.Shape: | ||
1004 | ret = new UUID("66c41e39-38f9-f75a-024e-585989bfab73"); | ||
1005 | break; | ||
1006 | case WearableType.Shirt: | ||
1007 | ret = new UUID("00000000-38f9-1111-024e-222222111110"); | ||
1008 | break; | ||
1009 | case WearableType.Skin: | ||
1010 | ret = new UUID("77c41e39-38f9-f75a-024e-585989bbabbb"); | ||
1011 | break; | ||
1012 | case WearableType.Undershirt: | ||
1013 | ret = new UUID("16499ebb-3208-ec27-2def-481881728f47"); | ||
1014 | break; | ||
1015 | case WearableType.Underpants: | ||
1016 | ret = new UUID("4ac2e9c7-3671-d229-316a-67717730841d"); | ||
1017 | break; | ||
1018 | } | ||
1019 | |||
1020 | return ret; | ||
1021 | } | ||
586 | #endregion | 1022 | #endregion |
587 | 1023 | ||
588 | #region Client Event Handlers | 1024 | #region Client Event Handlers |
@@ -592,12 +1028,17 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory | |||
592 | /// <param name="client"></param> | 1028 | /// <param name="client"></param> |
593 | private void Client_OnRequestWearables(IClientAPI client) | 1029 | private void Client_OnRequestWearables(IClientAPI client) |
594 | { | 1030 | { |
595 | // m_log.DebugFormat("[AVFACTORY]: Client_OnRequestWearables called for {0} ({1})", client.Name, client.AgentId); | 1031 | Util.FireAndForget(delegate(object x) |
596 | ScenePresence sp = m_scene.GetScenePresence(client.AgentId); | 1032 | { |
597 | if (sp != null) | 1033 | Thread.Sleep(4000); |
598 | client.SendWearables(sp.Appearance.Wearables, sp.Appearance.Serial++); | 1034 | |
599 | else | 1035 | // m_log.DebugFormat("[AVFACTORY]: Client_OnRequestWearables called for {0} ({1})", client.Name, client.AgentId); |
600 | m_log.WarnFormat("[AVFACTORY]: Client_OnRequestWearables unable to find presence for {0}", client.AgentId); | 1036 | ScenePresence sp = m_scene.GetScenePresence(client.AgentId); |
1037 | if (sp != null) | ||
1038 | client.SendWearables(sp.Appearance.Wearables, sp.Appearance.Serial++); | ||
1039 | else | ||
1040 | m_log.WarnFormat("[AVFACTORY]: Client_OnRequestWearables unable to find presence for {0}", client.AgentId); | ||
1041 | }, null, "AvatarFactoryModule.OnClientRequestWearables"); | ||
601 | } | 1042 | } |
602 | 1043 | ||
603 | /// <summary> | 1044 | /// <summary> |
@@ -606,12 +1047,12 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory | |||
606 | /// <param name="client"></param> | 1047 | /// <param name="client"></param> |
607 | /// <param name="texture"></param> | 1048 | /// <param name="texture"></param> |
608 | /// <param name="visualParam"></param> | 1049 | /// <param name="visualParam"></param> |
609 | private void Client_OnSetAppearance(IClientAPI client, Primitive.TextureEntry textureEntry, byte[] visualParams) | 1050 | private void Client_OnSetAppearance(IClientAPI client, Primitive.TextureEntry textureEntry, byte[] visualParams, Vector3 avSize, WearableCacheItem[] cacheItems) |
610 | { | 1051 | { |
611 | // m_log.WarnFormat("[AVFACTORY]: Client_OnSetAppearance called for {0} ({1})", client.Name, client.AgentId); | 1052 | // m_log.WarnFormat("[AVFACTORY]: Client_OnSetAppearance called for {0} ({1})", client.Name, client.AgentId); |
612 | ScenePresence sp = m_scene.GetScenePresence(client.AgentId); | 1053 | ScenePresence sp = m_scene.GetScenePresence(client.AgentId); |
613 | if (sp != null) | 1054 | if (sp != null) |
614 | SetAppearance(sp, textureEntry, visualParams); | 1055 | SetAppearance(sp, textureEntry, visualParams,avSize, cacheItems); |
615 | else | 1056 | else |
616 | m_log.WarnFormat("[AVFACTORY]: Client_OnSetAppearance unable to find presence for {0}", client.AgentId); | 1057 | m_log.WarnFormat("[AVFACTORY]: Client_OnSetAppearance unable to find presence for {0}", client.AgentId); |
617 | } | 1058 | } |
@@ -659,6 +1100,61 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory | |||
659 | QueueAppearanceSave(client.AgentId); | 1100 | QueueAppearanceSave(client.AgentId); |
660 | } | 1101 | } |
661 | } | 1102 | } |
1103 | |||
1104 | /// <summary> | ||
1105 | /// Respond to the cached textures request from the client | ||
1106 | /// </summary> | ||
1107 | /// <param name="client"></param> | ||
1108 | /// <param name="serial"></param> | ||
1109 | /// <param name="cachedTextureRequest"></param> | ||
1110 | private void Client_OnCachedTextureRequest(IClientAPI client, int serial, List<CachedTextureRequestArg> cachedTextureRequest) | ||
1111 | { | ||
1112 | // m_log.WarnFormat("[AVFACTORY]: Client_OnCachedTextureRequest called for {0} ({1})", client.Name, client.AgentId); | ||
1113 | ScenePresence sp = m_scene.GetScenePresence(client.AgentId); | ||
1114 | |||
1115 | List<CachedTextureResponseArg> cachedTextureResponse = new List<CachedTextureResponseArg>(); | ||
1116 | foreach (CachedTextureRequestArg request in cachedTextureRequest) | ||
1117 | { | ||
1118 | UUID texture = UUID.Zero; | ||
1119 | int index = request.BakedTextureIndex; | ||
1120 | |||
1121 | if (m_reusetextures) | ||
1122 | { | ||
1123 | // this is the most insanely dumb way to do this... however it seems to | ||
1124 | // actually work. if the appearance has been reset because wearables have | ||
1125 | // changed then the texture entries are zero'd out until the bakes are | ||
1126 | // uploaded. on login, if the textures exist in the cache (eg if you logged | ||
1127 | // into the simulator recently, then the appearance will pull those and send | ||
1128 | // them back in the packet and you won't have to rebake. if the textures aren't | ||
1129 | // in the cache then the intial makeroot() call in scenepresence will zero | ||
1130 | // them out. | ||
1131 | // | ||
1132 | // a better solution (though how much better is an open question) is to | ||
1133 | // store the hashes in the appearance and compare them. Thats's coming. | ||
1134 | |||
1135 | Primitive.TextureEntryFace face = sp.Appearance.Texture.FaceTextures[index]; | ||
1136 | if (face != null) | ||
1137 | texture = face.TextureID; | ||
1138 | |||
1139 | // m_log.WarnFormat("[AVFACTORY]: reuse texture {0} for index {1}",texture,index); | ||
1140 | } | ||
1141 | |||
1142 | CachedTextureResponseArg response = new CachedTextureResponseArg(); | ||
1143 | response.BakedTextureIndex = index; | ||
1144 | response.BakedTextureID = texture; | ||
1145 | response.HostName = null; | ||
1146 | |||
1147 | cachedTextureResponse.Add(response); | ||
1148 | } | ||
1149 | |||
1150 | // m_log.WarnFormat("[AVFACTORY]: serial is {0}",serial); | ||
1151 | // The serial number appears to be used to match requests and responses | ||
1152 | // in the texture transaction. We just send back the serial number | ||
1153 | // that was provided in the request. The viewer bumps this for us. | ||
1154 | client.SendCachedTextureResponse(sp, serial, cachedTextureResponse); | ||
1155 | } | ||
1156 | |||
1157 | |||
662 | #endregion | 1158 | #endregion |
663 | 1159 | ||
664 | public void WriteBakedTexturesReport(IScenePresence sp, ReportOutputAction outputAction) | 1160 | public void WriteBakedTexturesReport(IScenePresence sp, ReportOutputAction outputAction) |
@@ -690,7 +1186,7 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory | |||
690 | } | 1186 | } |
691 | 1187 | ||
692 | bool bakedTextureValid = m_scene.AvatarFactory.ValidateBakedTextureCache(sp); | 1188 | bool bakedTextureValid = m_scene.AvatarFactory.ValidateBakedTextureCache(sp); |
693 | outputAction("{0} baked appearance texture is {1}", sp.Name, bakedTextureValid ? "OK" : "corrupt"); | 1189 | outputAction("{0} baked appearance texture is {1}", sp.Name, bakedTextureValid ? "OK" : "incomplete"); |
694 | } | 1190 | } |
695 | } | 1191 | } |
696 | } | 1192 | } |
diff --git a/OpenSim/Region/CoreModules/Avatar/AvatarFactory/Tests/AvatarFactoryModuleTests.cs b/OpenSim/Region/CoreModules/Avatar/AvatarFactory/Tests/AvatarFactoryModuleTests.cs index 1830d41..9513408 100644 --- a/OpenSim/Region/CoreModules/Avatar/AvatarFactory/Tests/AvatarFactoryModuleTests.cs +++ b/OpenSim/Region/CoreModules/Avatar/AvatarFactory/Tests/AvatarFactoryModuleTests.cs | |||
@@ -34,7 +34,6 @@ using OpenSim.Framework; | |||
34 | using OpenSim.Region.CoreModules.Asset; | 34 | using OpenSim.Region.CoreModules.Asset; |
35 | using OpenSim.Region.Framework.Scenes; | 35 | using OpenSim.Region.Framework.Scenes; |
36 | using OpenSim.Tests.Common; | 36 | using OpenSim.Tests.Common; |
37 | using OpenSim.Tests.Common.Mock; | ||
38 | 37 | ||
39 | namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory | 38 | namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory |
40 | { | 39 | { |
@@ -48,23 +47,103 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory | |||
48 | public void TestSetAppearance() | 47 | public void TestSetAppearance() |
49 | { | 48 | { |
50 | TestHelpers.InMethod(); | 49 | TestHelpers.InMethod(); |
51 | // log4net.Config.XmlConfigurator.Configure(); | 50 | // TestHelpers.EnableLogging(); |
52 | 51 | ||
53 | UUID userId = TestHelpers.ParseTail(0x1); | 52 | UUID userId = TestHelpers.ParseTail(0x1); |
53 | UUID bakedTextureID = TestHelpers.ParseTail(0x2); | ||
54 | 54 | ||
55 | // We need an asset cache because otherwise the LocalAssetServiceConnector will short-circuit directly | ||
56 | // to the AssetService, which will then store temporary and local assets permanently | ||
57 | CoreAssetCache assetCache = new CoreAssetCache(); | ||
58 | |||
55 | AvatarFactoryModule afm = new AvatarFactoryModule(); | 59 | AvatarFactoryModule afm = new AvatarFactoryModule(); |
56 | TestScene scene = new SceneHelpers().SetupScene(); | 60 | TestScene scene = new SceneHelpers(assetCache).SetupScene(); |
57 | SceneHelpers.SetupSceneModules(scene, afm); | 61 | SceneHelpers.SetupSceneModules(scene, afm); |
58 | ScenePresence sp = SceneHelpers.AddScenePresence(scene, userId); | 62 | ScenePresence sp = SceneHelpers.AddScenePresence(scene, userId); |
59 | 63 | ||
64 | // TODO: Use the actual BunchOfCaps functionality once we slot in the CapabilitiesModules | ||
65 | AssetBase bakedTextureAsset; | ||
66 | bakedTextureAsset | ||
67 | = new AssetBase( | ||
68 | bakedTextureID, "Test Baked Texture", (sbyte)AssetType.Texture, userId.ToString()); | ||
69 | bakedTextureAsset.Data = new byte[] { 2 }; // Not necessary to have a genuine JPEG2000 asset here yet | ||
70 | bakedTextureAsset.Temporary = true; | ||
71 | bakedTextureAsset.Local = true; | ||
72 | scene.AssetService.Store(bakedTextureAsset); | ||
73 | |||
60 | byte[] visualParams = new byte[AvatarAppearance.VISUALPARAM_COUNT]; | 74 | byte[] visualParams = new byte[AvatarAppearance.VISUALPARAM_COUNT]; |
61 | for (byte i = 0; i < visualParams.Length; i++) | 75 | for (byte i = 0; i < visualParams.Length; i++) |
62 | visualParams[i] = i; | 76 | visualParams[i] = i; |
63 | 77 | ||
64 | afm.SetAppearance(sp, new Primitive.TextureEntry(TestHelpers.ParseTail(0x10)), visualParams); | 78 | Primitive.TextureEntry bakedTextureEntry = new Primitive.TextureEntry(TestHelpers.ParseTail(0x10)); |
79 | uint eyesFaceIndex = (uint)AppearanceManager.BakeTypeToAgentTextureIndex(BakeType.Eyes); | ||
80 | Primitive.TextureEntryFace eyesFace = bakedTextureEntry.CreateFace(eyesFaceIndex); | ||
81 | |||
82 | int rebakeRequestsReceived = 0; | ||
83 | ((TestClient)sp.ControllingClient).OnReceivedSendRebakeAvatarTextures += id => rebakeRequestsReceived++; | ||
84 | |||
85 | // This is the alpha texture | ||
86 | eyesFace.TextureID = bakedTextureID; | ||
87 | afm.SetAppearance(sp, bakedTextureEntry, visualParams, null); | ||
88 | |||
89 | Assert.That(rebakeRequestsReceived, Is.EqualTo(0)); | ||
90 | |||
91 | AssetBase eyesBake = scene.AssetService.Get(bakedTextureID.ToString()); | ||
92 | Assert.That(eyesBake, Is.Not.Null); | ||
93 | Assert.That(eyesBake.Temporary, Is.True); | ||
94 | Assert.That(eyesBake.Local, Is.True); | ||
95 | } | ||
96 | |||
97 | /// <summary> | ||
98 | /// Test appearance setting where the baked texture UUID are library alpha textures. | ||
99 | /// </summary> | ||
100 | /// <remarks> | ||
101 | /// For a mesh avatar, it appears these 'baked textures' are used. So these should not trigger a request to | ||
102 | /// rebake. | ||
103 | /// </remarks> | ||
104 | [Test] | ||
105 | public void TestSetAppearanceAlphaBakedTextures() | ||
106 | { | ||
107 | TestHelpers.InMethod(); | ||
108 | // TestHelpers.EnableLogging(); | ||
109 | |||
110 | UUID userId = TestHelpers.ParseTail(0x1); | ||
111 | UUID alphaTextureID = new UUID("3a367d1c-bef1-6d43-7595-e88c1e3aadb3"); | ||
112 | |||
113 | // We need an asset cache because otherwise the LocalAssetServiceConnector will short-circuit directly | ||
114 | // to the AssetService, which will then store temporary and local assets permanently | ||
115 | CoreAssetCache assetCache = new CoreAssetCache(); | ||
116 | |||
117 | AvatarFactoryModule afm = new AvatarFactoryModule(); | ||
118 | TestScene scene = new SceneHelpers(assetCache).SetupScene(); | ||
119 | SceneHelpers.SetupSceneModules(scene, afm); | ||
120 | ScenePresence sp = SceneHelpers.AddScenePresence(scene, userId); | ||
121 | |||
122 | AssetBase libraryAsset; | ||
123 | libraryAsset | ||
124 | = new AssetBase( | ||
125 | alphaTextureID, "Default Alpha Layer Texture", (sbyte)AssetType.Texture, userId.ToString()); | ||
126 | libraryAsset.Data = new byte[] { 2 }; // Not necessary to have a genuine JPEG2000 asset here yet | ||
127 | libraryAsset.Temporary = false; | ||
128 | libraryAsset.Local = false; | ||
129 | scene.AssetService.Store(libraryAsset); | ||
130 | |||
131 | byte[] visualParams = new byte[AvatarAppearance.VISUALPARAM_COUNT]; | ||
132 | for (byte i = 0; i < visualParams.Length; i++) | ||
133 | visualParams[i] = i; | ||
134 | |||
135 | Primitive.TextureEntry bakedTextureEntry = new Primitive.TextureEntry(TestHelpers.ParseTail(0x10)); | ||
136 | uint eyesFaceIndex = (uint)AppearanceManager.BakeTypeToAgentTextureIndex(BakeType.Eyes); | ||
137 | Primitive.TextureEntryFace eyesFace = bakedTextureEntry.CreateFace(eyesFaceIndex); | ||
138 | |||
139 | int rebakeRequestsReceived = 0; | ||
140 | ((TestClient)sp.ControllingClient).OnReceivedSendRebakeAvatarTextures += id => rebakeRequestsReceived++; | ||
65 | 141 | ||
66 | // TODO: Check baked texture | 142 | // This is the alpha texture |
67 | Assert.AreEqual(visualParams, sp.Appearance.VisualParams); | 143 | eyesFace.TextureID = alphaTextureID; |
144 | afm.SetAppearance(sp, bakedTextureEntry, visualParams, null); | ||
145 | |||
146 | Assert.That(rebakeRequestsReceived, Is.EqualTo(0)); | ||
68 | } | 147 | } |
69 | 148 | ||
70 | [Test] | 149 | [Test] |
@@ -102,7 +181,7 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory | |||
102 | Primitive.TextureEntryFace eyesFace = bakedTextureEntry.CreateFace(eyesFaceIndex); | 181 | Primitive.TextureEntryFace eyesFace = bakedTextureEntry.CreateFace(eyesFaceIndex); |
103 | eyesFace.TextureID = eyesTextureId; | 182 | eyesFace.TextureID = eyesTextureId; |
104 | 183 | ||
105 | afm.SetAppearance(sp, bakedTextureEntry, visualParams); | 184 | afm.SetAppearance(sp, bakedTextureEntry, visualParams, null); |
106 | afm.SaveBakedTextures(userId); | 185 | afm.SaveBakedTextures(userId); |
107 | // Dictionary<BakeType, Primitive.TextureEntryFace> bakedTextures = afm.GetBakedTextureFaces(userId); | 186 | // Dictionary<BakeType, Primitive.TextureEntryFace> bakedTextures = afm.GetBakedTextureFaces(userId); |
108 | 187 | ||
diff --git a/OpenSim/Region/CoreModules/Avatar/BakedTextures/XBakesModule.cs b/OpenSim/Region/CoreModules/Avatar/BakedTextures/XBakesModule.cs new file mode 100644 index 0000000..414f06a --- /dev/null +++ b/OpenSim/Region/CoreModules/Avatar/BakedTextures/XBakesModule.cs | |||
@@ -0,0 +1,200 @@ | |||
1 | /* | ||
2 | * Copyright (c) Contributors, http://opensimulator.org/ | ||
3 | * See CONTRIBUTORS.TXT for a full list of copyright holders. | ||
4 | * | ||
5 | * Redistribution and use in source and binary forms, with or without | ||
6 | * modification, are permitted provided that the following conditions are met: | ||
7 | * * Redistributions of source code must retain the above copyright | ||
8 | * notice, this list of conditions and the following disclaimer. | ||
9 | * * Redistributions in binary form must reproduce the above copyright | ||
10 | * notice, this list of conditions and the following disclaimer in the | ||
11 | * documentation and/or other materials provided with the distribution. | ||
12 | * * Neither the name of the OpenSimulator Project nor the | ||
13 | * names of its contributors may be used to endorse or promote products | ||
14 | * derived from this software without specific prior written permission. | ||
15 | * | ||
16 | * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY | ||
17 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||
18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
19 | * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY | ||
20 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||
21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||
22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | ||
23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
26 | */ | ||
27 | |||
28 | using OpenMetaverse; | ||
29 | using Nini.Config; | ||
30 | using System; | ||
31 | using System.IO; | ||
32 | using System.Text; | ||
33 | using System.Xml; | ||
34 | using System.Xml.Serialization; | ||
35 | using System.Collections; | ||
36 | using System.Collections.Generic; | ||
37 | using System.Reflection; | ||
38 | using log4net; | ||
39 | using OpenSim.Framework; | ||
40 | using OpenSim.Framework.ServiceAuth; | ||
41 | using OpenSim.Region.Framework.Interfaces; | ||
42 | using OpenSim.Region.Framework.Scenes; | ||
43 | using OpenSim.Services.Interfaces; | ||
44 | using Mono.Addins; | ||
45 | |||
46 | namespace OpenSim.Region.CoreModules.Avatar.BakedTextures | ||
47 | { | ||
48 | [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "XBakes.Module")] | ||
49 | public class XBakesModule : INonSharedRegionModule, IBakedTextureModule | ||
50 | { | ||
51 | protected Scene m_Scene; | ||
52 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | ||
53 | private UTF8Encoding enc = new UTF8Encoding(); | ||
54 | private string m_URL = String.Empty; | ||
55 | private static XmlSerializer m_serializer = new XmlSerializer(typeof(AssetBase)); | ||
56 | |||
57 | private static IServiceAuth m_Auth; | ||
58 | |||
59 | public void Initialise(IConfigSource configSource) | ||
60 | { | ||
61 | IConfig config = configSource.Configs["XBakes"]; | ||
62 | if (config == null) | ||
63 | return; | ||
64 | |||
65 | m_URL = config.GetString("URL", String.Empty); | ||
66 | m_Auth = ServiceAuth.Create(configSource, "XBakes"); | ||
67 | } | ||
68 | |||
69 | public void AddRegion(Scene scene) | ||
70 | { | ||
71 | // m_log.InfoFormat("[XBakes]: Enabled for region {0}", scene.RegionInfo.RegionName); | ||
72 | m_Scene = scene; | ||
73 | |||
74 | scene.RegisterModuleInterface<IBakedTextureModule>(this); | ||
75 | } | ||
76 | |||
77 | public void RegionLoaded(Scene scene) | ||
78 | { | ||
79 | } | ||
80 | |||
81 | public void RemoveRegion(Scene scene) | ||
82 | { | ||
83 | } | ||
84 | |||
85 | public void Close() | ||
86 | { | ||
87 | } | ||
88 | |||
89 | public string Name | ||
90 | { | ||
91 | get { return "XBakes.Module"; } | ||
92 | } | ||
93 | |||
94 | public Type ReplaceableInterface | ||
95 | { | ||
96 | get { return null; } | ||
97 | } | ||
98 | |||
99 | public WearableCacheItem[] Get(UUID id) | ||
100 | { | ||
101 | if (m_URL == String.Empty) | ||
102 | return null; | ||
103 | |||
104 | int size = 0; | ||
105 | |||
106 | using (RestClient rc = new RestClient(m_URL)) | ||
107 | { | ||
108 | List<WearableCacheItem> ret = new List<WearableCacheItem>(); | ||
109 | rc.AddResourcePath("bakes"); | ||
110 | rc.AddResourcePath(id.ToString()); | ||
111 | |||
112 | rc.RequestMethod = "GET"; | ||
113 | |||
114 | try | ||
115 | { | ||
116 | Stream s = rc.Request(m_Auth); | ||
117 | |||
118 | using (XmlTextReader sr = new XmlTextReader(s)) | ||
119 | { | ||
120 | sr.ReadStartElement("BakedAppearance"); | ||
121 | while (sr.LocalName == "BakedTexture") | ||
122 | { | ||
123 | string sTextureIndex = sr.GetAttribute("TextureIndex"); | ||
124 | int lTextureIndex = Convert.ToInt32(sTextureIndex); | ||
125 | string sCacheId = sr.GetAttribute("CacheId"); | ||
126 | UUID lCacheId = UUID.Zero; | ||
127 | if (!(UUID.TryParse(sCacheId, out lCacheId))) | ||
128 | { | ||
129 | // ?? Nothing here | ||
130 | } | ||
131 | |||
132 | ++size; | ||
133 | |||
134 | sr.ReadStartElement("BakedTexture"); | ||
135 | AssetBase a = (AssetBase)m_serializer.Deserialize(sr); | ||
136 | ret.Add(new WearableCacheItem() { CacheId = lCacheId, TextureIndex = (uint)lTextureIndex, TextureAsset = a, TextureID = a.FullID }); | ||
137 | |||
138 | sr.ReadEndElement(); | ||
139 | } | ||
140 | |||
141 | m_log.DebugFormat("[XBakes]: read {0} textures for user {1}", ret.Count, id); | ||
142 | } | ||
143 | |||
144 | return ret.ToArray(); | ||
145 | } | ||
146 | catch (XmlException) | ||
147 | { | ||
148 | return null; | ||
149 | } | ||
150 | } | ||
151 | } | ||
152 | |||
153 | public void Store(UUID agentId, WearableCacheItem[] data) | ||
154 | { | ||
155 | if (m_URL == String.Empty) | ||
156 | return; | ||
157 | |||
158 | MemoryStream reqStream; | ||
159 | |||
160 | using (MemoryStream bakeStream = new MemoryStream()) | ||
161 | using (XmlTextWriter bakeWriter = new XmlTextWriter(bakeStream, null)) | ||
162 | { | ||
163 | bakeWriter.WriteStartElement(String.Empty, "BakedAppearance", String.Empty); | ||
164 | |||
165 | for (int i = 0; i < data.Length; i++) | ||
166 | { | ||
167 | if (data[i] != null) | ||
168 | { | ||
169 | bakeWriter.WriteStartElement(String.Empty, "BakedTexture", String.Empty); | ||
170 | bakeWriter.WriteAttributeString(String.Empty, "TextureIndex", String.Empty, data[i].TextureIndex.ToString()); | ||
171 | bakeWriter.WriteAttributeString(String.Empty, "CacheId", String.Empty, data[i].CacheId.ToString()); | ||
172 | if (data[i].TextureAsset != null) | ||
173 | m_serializer.Serialize(bakeWriter, data[i].TextureAsset); | ||
174 | |||
175 | bakeWriter.WriteEndElement(); | ||
176 | } | ||
177 | } | ||
178 | |||
179 | bakeWriter.WriteEndElement(); | ||
180 | bakeWriter.Flush(); | ||
181 | |||
182 | reqStream = new MemoryStream(bakeStream.ToArray()); | ||
183 | } | ||
184 | |||
185 | RestClient rc = new RestClient(m_URL); | ||
186 | rc.AddResourcePath("bakes"); | ||
187 | rc.AddResourcePath(agentId.ToString()); | ||
188 | |||
189 | rc.RequestMethod = "POST"; | ||
190 | |||
191 | Util.FireAndForget( | ||
192 | delegate | ||
193 | { | ||
194 | rc.Request(reqStream, m_Auth); | ||
195 | m_log.DebugFormat("[XBakes]: stored {0} textures for user {1}", data.Length, agentId); | ||
196 | }, null, "XBakesModule.Store" | ||
197 | ); | ||
198 | } | ||
199 | } | ||
200 | } \ No newline at end of file | ||
diff --git a/OpenSim/Region/CoreModules/Avatar/Chat/ChatModule.cs b/OpenSim/Region/CoreModules/Avatar/Chat/ChatModule.cs index 6d62ff0..f0b1e67 100644 --- a/OpenSim/Region/CoreModules/Avatar/Chat/ChatModule.cs +++ b/OpenSim/Region/CoreModules/Avatar/Chat/ChatModule.cs | |||
@@ -32,6 +32,7 @@ using log4net; | |||
32 | using Nini.Config; | 32 | using Nini.Config; |
33 | using Mono.Addins; | 33 | using Mono.Addins; |
34 | using OpenMetaverse; | 34 | using OpenMetaverse; |
35 | using OpenMetaverse.StructuredData; | ||
35 | using OpenSim.Framework; | 36 | using OpenSim.Framework; |
36 | using OpenSim.Region.Framework.Interfaces; | 37 | using OpenSim.Region.Framework.Interfaces; |
37 | using OpenSim.Region.Framework.Scenes; | 38 | using OpenSim.Region.Framework.Scenes; |
@@ -50,7 +51,6 @@ namespace OpenSim.Region.CoreModules.Avatar.Chat | |||
50 | private int m_saydistance = 20; | 51 | private int m_saydistance = 20; |
51 | private int m_shoutdistance = 100; | 52 | private int m_shoutdistance = 100; |
52 | private int m_whisperdistance = 10; | 53 | private int m_whisperdistance = 10; |
53 | private List<Scene> m_scenes = new List<Scene>(); | ||
54 | 54 | ||
55 | internal object m_syncy = new object(); | 55 | internal object m_syncy = new object(); |
56 | 56 | ||
@@ -61,18 +61,14 @@ namespace OpenSim.Region.CoreModules.Avatar.Chat | |||
61 | { | 61 | { |
62 | m_config = config.Configs["Chat"]; | 62 | m_config = config.Configs["Chat"]; |
63 | 63 | ||
64 | if (null == m_config) | 64 | if (m_config != null) |
65 | { | 65 | { |
66 | m_log.Info("[CHAT]: no config found, plugin disabled"); | 66 | if (!m_config.GetBoolean("enabled", true)) |
67 | m_enabled = false; | 67 | { |
68 | return; | 68 | m_log.Info("[CHAT]: plugin disabled by configuration"); |
69 | } | 69 | m_enabled = false; |
70 | 70 | return; | |
71 | if (!m_config.GetBoolean("enabled", true)) | 71 | } |
72 | { | ||
73 | m_log.Info("[CHAT]: plugin disabled by configuration"); | ||
74 | m_enabled = false; | ||
75 | return; | ||
76 | } | 72 | } |
77 | 73 | ||
78 | m_whisperdistance = config.Configs["Chat"].GetInt("whisper_distance", m_whisperdistance); | 74 | m_whisperdistance = config.Configs["Chat"].GetInt("whisper_distance", m_whisperdistance); |
@@ -82,18 +78,12 @@ namespace OpenSim.Region.CoreModules.Avatar.Chat | |||
82 | 78 | ||
83 | public virtual void AddRegion(Scene scene) | 79 | public virtual void AddRegion(Scene scene) |
84 | { | 80 | { |
85 | if (!m_enabled) return; | 81 | if (!m_enabled) |
82 | return; | ||
86 | 83 | ||
87 | lock (m_syncy) | 84 | scene.EventManager.OnNewClient += OnNewClient; |
88 | { | 85 | scene.EventManager.OnChatFromWorld += OnChatFromWorld; |
89 | if (!m_scenes.Contains(scene)) | 86 | scene.EventManager.OnChatBroadcast += OnChatBroadcast; |
90 | { | ||
91 | m_scenes.Add(scene); | ||
92 | scene.EventManager.OnNewClient += OnNewClient; | ||
93 | scene.EventManager.OnChatFromWorld += OnChatFromWorld; | ||
94 | scene.EventManager.OnChatBroadcast += OnChatBroadcast; | ||
95 | } | ||
96 | } | ||
97 | 87 | ||
98 | m_log.InfoFormat("[CHAT]: Initialized for {0} w:{1} s:{2} S:{3}", scene.RegionInfo.RegionName, | 88 | m_log.InfoFormat("[CHAT]: Initialized for {0} w:{1} s:{2} S:{3}", scene.RegionInfo.RegionName, |
99 | m_whisperdistance, m_saydistance, m_shoutdistance); | 89 | m_whisperdistance, m_saydistance, m_shoutdistance); |
@@ -101,22 +91,24 @@ namespace OpenSim.Region.CoreModules.Avatar.Chat | |||
101 | 91 | ||
102 | public virtual void RegionLoaded(Scene scene) | 92 | public virtual void RegionLoaded(Scene scene) |
103 | { | 93 | { |
94 | if (!m_enabled) | ||
95 | return; | ||
96 | |||
97 | ISimulatorFeaturesModule featuresModule = scene.RequestModuleInterface<ISimulatorFeaturesModule>(); | ||
98 | |||
99 | if (featuresModule != null) | ||
100 | featuresModule.OnSimulatorFeaturesRequest += OnSimulatorFeaturesRequest; | ||
101 | |||
104 | } | 102 | } |
105 | 103 | ||
106 | public virtual void RemoveRegion(Scene scene) | 104 | public virtual void RemoveRegion(Scene scene) |
107 | { | 105 | { |
108 | if (!m_enabled) return; | 106 | if (!m_enabled) |
107 | return; | ||
109 | 108 | ||
110 | lock (m_syncy) | 109 | scene.EventManager.OnNewClient -= OnNewClient; |
111 | { | 110 | scene.EventManager.OnChatFromWorld -= OnChatFromWorld; |
112 | if (m_scenes.Contains(scene)) | 111 | scene.EventManager.OnChatBroadcast -= OnChatBroadcast; |
113 | { | ||
114 | scene.EventManager.OnNewClient -= OnNewClient; | ||
115 | scene.EventManager.OnChatFromWorld -= OnChatFromWorld; | ||
116 | scene.EventManager.OnChatBroadcast -= OnChatBroadcast; | ||
117 | m_scenes.Remove(scene); | ||
118 | } | ||
119 | } | ||
120 | } | 112 | } |
121 | 113 | ||
122 | public virtual void Close() | 114 | public virtual void Close() |
@@ -191,23 +183,16 @@ namespace OpenSim.Region.CoreModules.Avatar.Chat | |||
191 | UUID ownerID = UUID.Zero; | 183 | UUID ownerID = UUID.Zero; |
192 | UUID targetID = c.TargetUUID; | 184 | UUID targetID = c.TargetUUID; |
193 | string message = c.Message; | 185 | string message = c.Message; |
194 | IScene scene = c.Scene; | 186 | Scene scene = (Scene)c.Scene; |
195 | Vector3 fromPos = c.Position; | 187 | Vector3 fromPos = c.Position; |
196 | Vector3 regionPos = new Vector3(scene.RegionInfo.RegionLocX * Constants.RegionSize, | 188 | Vector3 regionPos = new Vector3(scene.RegionInfo.WorldLocX, scene.RegionInfo.WorldLocY, 0); |
197 | scene.RegionInfo.RegionLocY * Constants.RegionSize, 0); | ||
198 | 189 | ||
199 | if (c.Channel == DEBUG_CHANNEL) c.Type = ChatTypeEnum.DebugChannel; | 190 | if (c.Channel == DEBUG_CHANNEL) c.Type = ChatTypeEnum.DebugChannel; |
200 | 191 | ||
201 | switch (sourceType) | 192 | switch (sourceType) |
202 | { | 193 | { |
203 | case ChatSourceType.Agent: | 194 | case ChatSourceType.Agent: |
204 | if (!(scene is Scene)) | 195 | ScenePresence avatar = scene.GetScenePresence(c.Sender.AgentId); |
205 | { | ||
206 | m_log.WarnFormat("[CHAT]: scene {0} is not a Scene object, cannot obtain scene presence for {1}", | ||
207 | scene.RegionInfo.RegionName, c.Sender.AgentId); | ||
208 | return; | ||
209 | } | ||
210 | ScenePresence avatar = (scene as Scene).GetScenePresence(c.Sender.AgentId); | ||
211 | fromPos = avatar.AbsolutePosition; | 196 | fromPos = avatar.AbsolutePosition; |
212 | fromName = avatar.Name; | 197 | fromName = avatar.Name; |
213 | fromID = c.Sender.AgentId; | 198 | fromID = c.Sender.AgentId; |
@@ -234,36 +219,33 @@ namespace OpenSim.Region.CoreModules.Avatar.Chat | |||
234 | 219 | ||
235 | HashSet<UUID> receiverIDs = new HashSet<UUID>(); | 220 | HashSet<UUID> receiverIDs = new HashSet<UUID>(); |
236 | 221 | ||
237 | foreach (Scene s in m_scenes) | 222 | if (targetID == UUID.Zero) |
238 | { | 223 | { |
239 | if (targetID == UUID.Zero) | 224 | // This should use ForEachClient, but clients don't have a position. |
240 | { | 225 | // If camera is moved into client, then camera position can be used |
241 | // This should use ForEachClient, but clients don't have a position. | 226 | scene.ForEachScenePresence( |
242 | // If camera is moved into client, then camera position can be used | 227 | delegate(ScenePresence presence) |
243 | s.ForEachRootScenePresence( | ||
244 | delegate(ScenePresence presence) | ||
245 | { | ||
246 | if (TrySendChatMessage( | ||
247 | presence, fromPos, regionPos, fromID, ownerID, fromName, c.Type, message, sourceType, false)) | ||
248 | receiverIDs.Add(presence.UUID); | ||
249 | } | ||
250 | ); | ||
251 | } | ||
252 | else | ||
253 | { | ||
254 | // This is a send to a specific client eg from llRegionSayTo | ||
255 | // no need to check distance etc, jand send is as say | ||
256 | ScenePresence presence = s.GetScenePresence(targetID); | ||
257 | if (presence != null && !presence.IsChildAgent) | ||
258 | { | 228 | { |
259 | if (TrySendChatMessage( | 229 | if (TrySendChatMessage( |
260 | presence, fromPos, regionPos, fromID, ownerID, fromName, ChatTypeEnum.Say, message, sourceType, true)) | 230 | presence, fromPos, regionPos, fromID, ownerID, fromName, c.Type, message, sourceType, false)) |
261 | receiverIDs.Add(presence.UUID); | 231 | receiverIDs.Add(presence.UUID); |
262 | } | 232 | } |
233 | ); | ||
234 | } | ||
235 | else | ||
236 | { | ||
237 | // This is a send to a specific client eg from llRegionSayTo | ||
238 | // no need to check distance etc, jand send is as say | ||
239 | ScenePresence presence = scene.GetScenePresence(targetID); | ||
240 | if (presence != null && !presence.IsChildAgent) | ||
241 | { | ||
242 | if (TrySendChatMessage( | ||
243 | presence, fromPos, regionPos, fromID, ownerID, fromName, ChatTypeEnum.Say, message, sourceType, true)) | ||
244 | receiverIDs.Add(presence.UUID); | ||
263 | } | 245 | } |
264 | } | 246 | } |
265 | 247 | ||
266 | (scene as Scene).EventManager.TriggerOnChatToClients( | 248 | scene.EventManager.TriggerOnChatToClients( |
267 | fromID, receiverIDs, message, c.Type, fromPos, fromName, sourceType, ChatAudibleLevel.Fully); | 249 | fromID, receiverIDs, message, c.Type, fromPos, fromName, sourceType, ChatAudibleLevel.Fully); |
268 | } | 250 | } |
269 | 251 | ||
@@ -288,17 +270,20 @@ namespace OpenSim.Region.CoreModules.Avatar.Chat | |||
288 | string fromName = c.From; | 270 | string fromName = c.From; |
289 | 271 | ||
290 | UUID fromID = UUID.Zero; | 272 | UUID fromID = UUID.Zero; |
273 | UUID ownerID = UUID.Zero; | ||
291 | ChatSourceType sourceType = ChatSourceType.Object; | 274 | ChatSourceType sourceType = ChatSourceType.Object; |
292 | if (null != c.Sender) | 275 | if (null != c.Sender) |
293 | { | 276 | { |
294 | ScenePresence avatar = (c.Scene as Scene).GetScenePresence(c.Sender.AgentId); | 277 | ScenePresence avatar = (c.Scene as Scene).GetScenePresence(c.Sender.AgentId); |
295 | fromID = c.Sender.AgentId; | 278 | fromID = c.Sender.AgentId; |
296 | fromName = avatar.Name; | 279 | fromName = avatar.Name; |
280 | ownerID = c.Sender.AgentId; | ||
297 | sourceType = ChatSourceType.Agent; | 281 | sourceType = ChatSourceType.Agent; |
298 | } | 282 | } |
299 | else if (c.SenderUUID != UUID.Zero) | 283 | else if (c.SenderUUID != UUID.Zero) |
300 | { | 284 | { |
301 | fromID = c.SenderUUID; | 285 | fromID = c.SenderUUID; |
286 | ownerID = ((SceneObjectPart)c.SenderObject).OwnerID; | ||
302 | } | 287 | } |
303 | 288 | ||
304 | // m_log.DebugFormat("[CHAT] Broadcast: fromID {0} fromName {1}, cType {2}, sType {3}", fromID, fromName, cType, sourceType); | 289 | // m_log.DebugFormat("[CHAT] Broadcast: fromID {0} fromName {1}, cType {2}, sType {3}", fromID, fromName, cType, sourceType); |
@@ -316,7 +301,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Chat | |||
316 | return; | 301 | return; |
317 | 302 | ||
318 | client.SendChatMessage( | 303 | client.SendChatMessage( |
319 | c.Message, (byte)cType, CenterOfRegion, fromName, fromID, fromID, | 304 | c.Message, (byte)cType, CenterOfRegion, fromName, fromID, ownerID, |
320 | (byte)sourceType, (byte)ChatAudibleLevel.Fully); | 305 | (byte)sourceType, (byte)ChatAudibleLevel.Fully); |
321 | 306 | ||
322 | receiverIDs.Add(client.AgentId); | 307 | receiverIDs.Add(client.AgentId); |
@@ -348,18 +333,17 @@ namespace OpenSim.Region.CoreModules.Avatar.Chat | |||
348 | UUID fromAgentID, UUID ownerID, string fromName, ChatTypeEnum type, | 333 | UUID fromAgentID, UUID ownerID, string fromName, ChatTypeEnum type, |
349 | string message, ChatSourceType src, bool ignoreDistance) | 334 | string message, ChatSourceType src, bool ignoreDistance) |
350 | { | 335 | { |
351 | // don't send stuff to child agents | 336 | if (presence.LifecycleState != ScenePresenceState.Running) |
352 | if (presence.IsChildAgent) return false; | 337 | return false; |
353 | |||
354 | Vector3 fromRegionPos = fromPos + regionPos; | ||
355 | Vector3 toRegionPos = presence.AbsolutePosition + | ||
356 | new Vector3(presence.Scene.RegionInfo.RegionLocX * Constants.RegionSize, | ||
357 | presence.Scene.RegionInfo.RegionLocY * Constants.RegionSize, 0); | ||
358 | |||
359 | int dis = (int)Util.GetDistanceTo(toRegionPos, fromRegionPos); | ||
360 | 338 | ||
361 | if (!ignoreDistance) | 339 | if (!ignoreDistance) |
362 | { | 340 | { |
341 | Vector3 fromRegionPos = fromPos + regionPos; | ||
342 | Vector3 toRegionPos = presence.AbsolutePosition + | ||
343 | new Vector3(presence.Scene.RegionInfo.WorldLocX, presence.Scene.RegionInfo.WorldLocY, 0); | ||
344 | |||
345 | int dis = (int)Util.GetDistanceTo(toRegionPos, fromRegionPos); | ||
346 | |||
363 | if (type == ChatTypeEnum.Whisper && dis > m_whisperdistance || | 347 | if (type == ChatTypeEnum.Whisper && dis > m_whisperdistance || |
364 | type == ChatTypeEnum.Say && dis > m_saydistance || | 348 | type == ChatTypeEnum.Say && dis > m_saydistance || |
365 | type == ChatTypeEnum.Shout && dis > m_shoutdistance) | 349 | type == ChatTypeEnum.Shout && dis > m_shoutdistance) |
@@ -375,5 +359,33 @@ namespace OpenSim.Region.CoreModules.Avatar.Chat | |||
375 | 359 | ||
376 | return true; | 360 | return true; |
377 | } | 361 | } |
362 | |||
363 | #region SimulatorFeaturesRequest | ||
364 | |||
365 | static OSDInteger m_SayRange, m_WhisperRange, m_ShoutRange; | ||
366 | |||
367 | private void OnSimulatorFeaturesRequest(UUID agentID, ref OSDMap features) | ||
368 | { | ||
369 | OSD extras = new OSDMap(); | ||
370 | if (features.ContainsKey("OpenSimExtras")) | ||
371 | extras = features["OpenSimExtras"]; | ||
372 | else | ||
373 | features["OpenSimExtras"] = extras; | ||
374 | |||
375 | if (m_SayRange == null) | ||
376 | { | ||
377 | // Do this only once | ||
378 | m_SayRange = new OSDInteger(m_saydistance); | ||
379 | m_WhisperRange = new OSDInteger(m_whisperdistance); | ||
380 | m_ShoutRange = new OSDInteger(m_shoutdistance); | ||
381 | } | ||
382 | |||
383 | ((OSDMap)extras)["say-range"] = m_SayRange; | ||
384 | ((OSDMap)extras)["whisper-range"] = m_WhisperRange; | ||
385 | ((OSDMap)extras)["shout-range"] = m_ShoutRange; | ||
386 | |||
387 | } | ||
388 | |||
389 | #endregion | ||
378 | } | 390 | } |
379 | } \ No newline at end of file | 391 | } |
diff --git a/OpenSim/Region/CoreModules/Avatar/Chat/Tests/ChatModuleTests.cs b/OpenSim/Region/CoreModules/Avatar/Chat/Tests/ChatModuleTests.cs new file mode 100644 index 0000000..3018d94 --- /dev/null +++ b/OpenSim/Region/CoreModules/Avatar/Chat/Tests/ChatModuleTests.cs | |||
@@ -0,0 +1,285 @@ | |||
1 | /* | ||
2 | * Copyright (c) Contributors, http://opensimulator.org/ | ||
3 | * See CONTRIBUTORS.TXT for a full list of copyright holders. | ||
4 | * | ||
5 | * Redistribution and use in source and binary forms, with or without | ||
6 | * modification, are permitted provided that the following conditions are met: | ||
7 | * * Redistributions of source code must retain the above copyright | ||
8 | * notice, this list of conditions and the following disclaimer. | ||
9 | * * Redistributions in binary form must reproduce the above copyright | ||
10 | * notice, this list of conditions and the following disclaimer in the | ||
11 | * documentation and/or other materials provided with the distribution. | ||
12 | * * Neither the name of the OpenSimulator Project nor the | ||
13 | * names of its contributors may be used to endorse or promote products | ||
14 | * derived from this software without specific prior written permission. | ||
15 | * | ||
16 | * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY | ||
17 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||
18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
19 | * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY | ||
20 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||
21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||
22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | ||
23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
26 | */ | ||
27 | |||
28 | using System; | ||
29 | using System.Collections.Generic; | ||
30 | using log4net.Config; | ||
31 | using Nini.Config; | ||
32 | using NUnit.Framework; | ||
33 | using OpenMetaverse; | ||
34 | using OpenSim.Framework; | ||
35 | using OpenSim.Framework.Servers; | ||
36 | using OpenSim.Framework.Servers.HttpServer; | ||
37 | using OpenSim.Region.CoreModules.Avatar.Chat; | ||
38 | using OpenSim.Region.CoreModules.Framework; | ||
39 | using OpenSim.Region.CoreModules.Framework.EntityTransfer; | ||
40 | using OpenSim.Region.CoreModules.ServiceConnectorsOut.Simulation; | ||
41 | using OpenSim.Region.Framework.Scenes; | ||
42 | using OpenSim.Services.Interfaces; | ||
43 | using OpenSim.Tests.Common; | ||
44 | |||
45 | namespace OpenSim.Region.CoreModules.Avatar.Chat.Tests | ||
46 | { | ||
47 | [TestFixture] | ||
48 | public class ChatModuleTests : OpenSimTestCase | ||
49 | { | ||
50 | [TestFixtureSetUp] | ||
51 | public void FixtureInit() | ||
52 | { | ||
53 | // Don't allow tests to be bamboozled by asynchronous events. Execute everything on the same thread. | ||
54 | // We must do this here so that child agent positions are updated in a predictable manner. | ||
55 | Util.FireAndForgetMethod = FireAndForgetMethod.RegressionTest; | ||
56 | } | ||
57 | |||
58 | [TestFixtureTearDown] | ||
59 | public void TearDown() | ||
60 | { | ||
61 | // We must set this back afterwards, otherwise later tests will fail since they're expecting multiple | ||
62 | // threads. Possibly, later tests should be rewritten so none of them require async stuff (which regression | ||
63 | // tests really shouldn't). | ||
64 | Util.FireAndForgetMethod = Util.DefaultFireAndForgetMethod; | ||
65 | } | ||
66 | |||
67 | private void SetupNeighbourRegions(TestScene sceneA, TestScene sceneB) | ||
68 | { | ||
69 | // XXX: HTTP server is not (and should not be) necessary for this test, though it's absence makes the | ||
70 | // CapabilitiesModule complain when it can't set up HTTP endpoints. | ||
71 | // BaseHttpServer httpServer = new BaseHttpServer(99999); | ||
72 | // MainServer.AddHttpServer(httpServer); | ||
73 | // MainServer.Instance = httpServer; | ||
74 | |||
75 | // We need entity transfer modules so that when sp2 logs into the east region, the region calls | ||
76 | // EntityTransferModuleto set up a child agent on the west region. | ||
77 | // XXX: However, this is not an entity transfer so is misleading. | ||
78 | EntityTransferModule etmA = new EntityTransferModule(); | ||
79 | EntityTransferModule etmB = new EntityTransferModule(); | ||
80 | LocalSimulationConnectorModule lscm = new LocalSimulationConnectorModule(); | ||
81 | |||
82 | IConfigSource config = new IniConfigSource(); | ||
83 | config.AddConfig("Chat"); | ||
84 | IConfig modulesConfig = config.AddConfig("Modules"); | ||
85 | modulesConfig.Set("EntityTransferModule", etmA.Name); | ||
86 | modulesConfig.Set("SimulationServices", lscm.Name); | ||
87 | |||
88 | SceneHelpers.SetupSceneModules(new Scene[] { sceneA, sceneB }, config, lscm); | ||
89 | SceneHelpers.SetupSceneModules(sceneA, config, new CapabilitiesModule(), etmA, new ChatModule()); | ||
90 | SceneHelpers.SetupSceneModules(sceneB, config, new CapabilitiesModule(), etmB, new ChatModule()); | ||
91 | } | ||
92 | |||
93 | /// <summary> | ||
94 | /// Tests chat between neighbour regions on the east-west axis | ||
95 | /// </summary> | ||
96 | /// <remarks> | ||
97 | /// Really, this is a combination of a child agent position update test and a chat range test. These need | ||
98 | /// to be separated later on. | ||
99 | /// </remarks> | ||
100 | [Test] | ||
101 | public void TestInterRegionChatDistanceEastWest() | ||
102 | { | ||
103 | TestHelpers.InMethod(); | ||
104 | // TestHelpers.EnableLogging(); | ||
105 | |||
106 | UUID sp1Uuid = TestHelpers.ParseTail(0x11); | ||
107 | UUID sp2Uuid = TestHelpers.ParseTail(0x12); | ||
108 | |||
109 | Vector3 sp1Position = new Vector3(6, 128, 20); | ||
110 | Vector3 sp2Position = new Vector3(250, 128, 20); | ||
111 | |||
112 | SceneHelpers sh = new SceneHelpers(); | ||
113 | TestScene sceneWest = sh.SetupScene("sceneWest", TestHelpers.ParseTail(0x1), 1000, 1000); | ||
114 | TestScene sceneEast = sh.SetupScene("sceneEast", TestHelpers.ParseTail(0x2), 1001, 1000); | ||
115 | |||
116 | SetupNeighbourRegions(sceneWest, sceneEast); | ||
117 | |||
118 | ScenePresence sp1 = SceneHelpers.AddScenePresence(sceneEast, sp1Uuid); | ||
119 | TestClient sp1Client = (TestClient)sp1.ControllingClient; | ||
120 | |||
121 | // If we don't set agents to flying, test will go wrong as they instantly fall to z = 0. | ||
122 | // TODO: May need to create special complete no-op test physics module rather than basic physics, since | ||
123 | // physics is irrelevant to this test. | ||
124 | sp1.Flying = true; | ||
125 | |||
126 | // When sp1 logs in to sceneEast, it sets up a child agent in sceneWest and informs the sp2 client to | ||
127 | // make the connection. For this test, will simplify this chain by making the connection directly. | ||
128 | ScenePresence sp1Child = SceneHelpers.AddChildScenePresence(sceneWest, sp1Uuid); | ||
129 | TestClient sp1ChildClient = (TestClient)sp1Child.ControllingClient; | ||
130 | |||
131 | sp1.AbsolutePosition = sp1Position; | ||
132 | |||
133 | ScenePresence sp2 = SceneHelpers.AddScenePresence(sceneWest, sp2Uuid); | ||
134 | TestClient sp2Client = (TestClient)sp2.ControllingClient; | ||
135 | sp2.Flying = true; | ||
136 | |||
137 | ScenePresence sp2Child = SceneHelpers.AddChildScenePresence(sceneEast, sp2Uuid); | ||
138 | TestClient sp2ChildClient = (TestClient)sp2Child.ControllingClient; | ||
139 | |||
140 | sp2.AbsolutePosition = sp2Position; | ||
141 | |||
142 | // We must update the scenes in order to make the root new root agents trigger position updates in their | ||
143 | // children. | ||
144 | sceneWest.Update(1); | ||
145 | sceneEast.Update(1); | ||
146 | |||
147 | // Check child positions are correct. | ||
148 | Assert.AreEqual( | ||
149 | new Vector3(sp1Position.X + sceneEast.RegionInfo.RegionSizeX, sp1Position.Y, sp1Position.Z), | ||
150 | sp1ChildClient.SceneAgent.AbsolutePosition); | ||
151 | |||
152 | Assert.AreEqual( | ||
153 | new Vector3(sp2Position.X - sceneWest.RegionInfo.RegionSizeX, sp2Position.Y, sp2Position.Z), | ||
154 | sp2ChildClient.SceneAgent.AbsolutePosition); | ||
155 | |||
156 | string receivedSp1ChatMessage = ""; | ||
157 | string receivedSp2ChatMessage = ""; | ||
158 | |||
159 | sp1ChildClient.OnReceivedChatMessage | ||
160 | += (message, type, fromPos, fromName, fromAgentID, ownerID, source, audible) => receivedSp1ChatMessage = message; | ||
161 | sp2ChildClient.OnReceivedChatMessage | ||
162 | += (message, type, fromPos, fromName, fromAgentID, ownerID, source, audible) => receivedSp2ChatMessage = message; | ||
163 | |||
164 | TestUserInRange(sp1Client, "ello darling", ref receivedSp2ChatMessage); | ||
165 | TestUserInRange(sp2Client, "fantastic cats", ref receivedSp1ChatMessage); | ||
166 | |||
167 | sp1Position = new Vector3(30, 128, 20); | ||
168 | sp1.AbsolutePosition = sp1Position; | ||
169 | sceneEast.Update(1); | ||
170 | |||
171 | // Check child position is correct. | ||
172 | Assert.AreEqual( | ||
173 | new Vector3(sp1Position.X + sceneEast.RegionInfo.RegionSizeX, sp1Position.Y, sp1Position.Z), | ||
174 | sp1ChildClient.SceneAgent.AbsolutePosition); | ||
175 | |||
176 | TestUserOutOfRange(sp1Client, "beef", ref receivedSp2ChatMessage); | ||
177 | TestUserOutOfRange(sp2Client, "lentils", ref receivedSp1ChatMessage); | ||
178 | } | ||
179 | |||
180 | /// <summary> | ||
181 | /// Tests chat between neighbour regions on the north-south axis | ||
182 | /// </summary> | ||
183 | /// <remarks> | ||
184 | /// Really, this is a combination of a child agent position update test and a chat range test. These need | ||
185 | /// to be separated later on. | ||
186 | /// </remarks> | ||
187 | [Test] | ||
188 | public void TestInterRegionChatDistanceNorthSouth() | ||
189 | { | ||
190 | TestHelpers.InMethod(); | ||
191 | // TestHelpers.EnableLogging(); | ||
192 | |||
193 | UUID sp1Uuid = TestHelpers.ParseTail(0x11); | ||
194 | UUID sp2Uuid = TestHelpers.ParseTail(0x12); | ||
195 | |||
196 | Vector3 sp1Position = new Vector3(128, 250, 20); | ||
197 | Vector3 sp2Position = new Vector3(128, 6, 20); | ||
198 | |||
199 | SceneHelpers sh = new SceneHelpers(); | ||
200 | TestScene sceneNorth = sh.SetupScene("sceneNorth", TestHelpers.ParseTail(0x1), 1000, 1000); | ||
201 | TestScene sceneSouth = sh.SetupScene("sceneSouth", TestHelpers.ParseTail(0x2), 1000, 1001); | ||
202 | |||
203 | SetupNeighbourRegions(sceneNorth, sceneSouth); | ||
204 | |||
205 | ScenePresence sp1 = SceneHelpers.AddScenePresence(sceneNorth, sp1Uuid); | ||
206 | TestClient sp1Client = (TestClient)sp1.ControllingClient; | ||
207 | |||
208 | // If we don't set agents to flying, test will go wrong as they instantly fall to z = 0. | ||
209 | // TODO: May need to create special complete no-op test physics module rather than basic physics, since | ||
210 | // physics is irrelevant to this test. | ||
211 | sp1.Flying = true; | ||
212 | |||
213 | // When sp1 logs in to sceneEast, it sets up a child agent in sceneNorth and informs the sp2 client to | ||
214 | // make the connection. For this test, will simplify this chain by making the connection directly. | ||
215 | ScenePresence sp1Child = SceneHelpers.AddChildScenePresence(sceneSouth, sp1Uuid); | ||
216 | TestClient sp1ChildClient = (TestClient)sp1Child.ControllingClient; | ||
217 | |||
218 | sp1.AbsolutePosition = sp1Position; | ||
219 | |||
220 | ScenePresence sp2 = SceneHelpers.AddScenePresence(sceneSouth, sp2Uuid); | ||
221 | TestClient sp2Client = (TestClient)sp2.ControllingClient; | ||
222 | sp2.Flying = true; | ||
223 | |||
224 | ScenePresence sp2Child = SceneHelpers.AddChildScenePresence(sceneNorth, sp2Uuid); | ||
225 | TestClient sp2ChildClient = (TestClient)sp2Child.ControllingClient; | ||
226 | |||
227 | sp2.AbsolutePosition = sp2Position; | ||
228 | |||
229 | // We must update the scenes in order to make the root new root agents trigger position updates in their | ||
230 | // children. | ||
231 | sceneNorth.Update(1); | ||
232 | sceneSouth.Update(1); | ||
233 | |||
234 | // Check child positions are correct. | ||
235 | Assert.AreEqual( | ||
236 | new Vector3(sp1Position.X, sp1Position.Y - sceneNorth.RegionInfo.RegionSizeY, sp1Position.Z), | ||
237 | sp1ChildClient.SceneAgent.AbsolutePosition); | ||
238 | |||
239 | Assert.AreEqual( | ||
240 | new Vector3(sp2Position.X, sp2Position.Y + sceneSouth.RegionInfo.RegionSizeY, sp2Position.Z), | ||
241 | sp2ChildClient.SceneAgent.AbsolutePosition); | ||
242 | |||
243 | string receivedSp1ChatMessage = ""; | ||
244 | string receivedSp2ChatMessage = ""; | ||
245 | |||
246 | sp1ChildClient.OnReceivedChatMessage | ||
247 | += (message, type, fromPos, fromName, fromAgentID, ownerID, source, audible) => receivedSp1ChatMessage = message; | ||
248 | sp2ChildClient.OnReceivedChatMessage | ||
249 | += (message, type, fromPos, fromName, fromAgentID, ownerID, source, audible) => receivedSp2ChatMessage = message; | ||
250 | |||
251 | TestUserInRange(sp1Client, "ello darling", ref receivedSp2ChatMessage); | ||
252 | TestUserInRange(sp2Client, "fantastic cats", ref receivedSp1ChatMessage); | ||
253 | |||
254 | sp1Position = new Vector3(30, 128, 20); | ||
255 | sp1.AbsolutePosition = sp1Position; | ||
256 | sceneNorth.Update(1); | ||
257 | |||
258 | // Check child position is correct. | ||
259 | Assert.AreEqual( | ||
260 | new Vector3(sp1Position.X, sp1Position.Y - sceneNorth.RegionInfo.RegionSizeY, sp1Position.Z), | ||
261 | sp1ChildClient.SceneAgent.AbsolutePosition); | ||
262 | |||
263 | TestUserOutOfRange(sp1Client, "beef", ref receivedSp2ChatMessage); | ||
264 | TestUserOutOfRange(sp2Client, "lentils", ref receivedSp1ChatMessage); | ||
265 | } | ||
266 | |||
267 | private void TestUserInRange(TestClient speakClient, string testMessage, ref string receivedMessage) | ||
268 | { | ||
269 | receivedMessage = ""; | ||
270 | |||
271 | speakClient.Chat(0, ChatTypeEnum.Say, testMessage); | ||
272 | |||
273 | Assert.AreEqual(testMessage, receivedMessage); | ||
274 | } | ||
275 | |||
276 | private void TestUserOutOfRange(TestClient speakClient, string testMessage, ref string receivedMessage) | ||
277 | { | ||
278 | receivedMessage = ""; | ||
279 | |||
280 | speakClient.Chat(0, ChatTypeEnum.Say, testMessage); | ||
281 | |||
282 | Assert.AreNotEqual(testMessage, receivedMessage); | ||
283 | } | ||
284 | } | ||
285 | } \ No newline at end of file | ||
diff --git a/OpenSim/Region/CoreModules/Avatar/Combat/CombatModule.cs b/OpenSim/Region/CoreModules/Avatar/Combat/CombatModule.cs index 343cdb5..fc23b72 100644 --- a/OpenSim/Region/CoreModules/Avatar/Combat/CombatModule.cs +++ b/OpenSim/Region/CoreModules/Avatar/Combat/CombatModule.cs | |||
@@ -31,6 +31,7 @@ using Nini.Config; | |||
31 | using OpenSim.Framework; | 31 | using OpenSim.Framework; |
32 | using OpenSim.Region.Framework.Interfaces; | 32 | using OpenSim.Region.Framework.Interfaces; |
33 | using OpenSim.Region.Framework.Scenes; | 33 | using OpenSim.Region.Framework.Scenes; |
34 | using OpenSim.Services.Interfaces; | ||
34 | using OpenMetaverse; | 35 | using OpenMetaverse; |
35 | 36 | ||
36 | using Mono.Addins; | 37 | using Mono.Addins; |
@@ -182,6 +183,10 @@ namespace OpenSim.Region.CoreModules.Avatar.Combat.CombatModule | |||
182 | try | 183 | try |
183 | { | 184 | { |
184 | ILandObject obj = avatar.Scene.LandChannel.GetLandObject(avatar.AbsolutePosition.X, avatar.AbsolutePosition.Y); | 185 | ILandObject obj = avatar.Scene.LandChannel.GetLandObject(avatar.AbsolutePosition.X, avatar.AbsolutePosition.Y); |
186 | |||
187 | if (obj == null) | ||
188 | return; | ||
189 | |||
185 | if ((obj.LandData.Flags & (uint)ParcelFlags.AllowDamage) != 0 | 190 | if ((obj.LandData.Flags & (uint)ParcelFlags.AllowDamage) != 0 |
186 | || avatar.Scene.RegionInfo.RegionSettings.AllowDamage) | 191 | || avatar.Scene.RegionInfo.RegionSettings.AllowDamage) |
187 | { | 192 | { |
diff --git a/OpenSim/Region/CoreModules/Avatar/Dialog/DialogModule.cs b/OpenSim/Region/CoreModules/Avatar/Dialog/DialogModule.cs index d26907b..a896897 100644 --- a/OpenSim/Region/CoreModules/Avatar/Dialog/DialogModule.cs +++ b/OpenSim/Region/CoreModules/Avatar/Dialog/DialogModule.cs | |||
@@ -133,13 +133,14 @@ namespace OpenSim.Region.CoreModules.Avatar.Dialog | |||
133 | UUID objectID, UUID ownerID, string message, UUID textureID, | 133 | UUID objectID, UUID ownerID, string message, UUID textureID, |
134 | int ch, string[] buttonlabels) | 134 | int ch, string[] buttonlabels) |
135 | { | 135 | { |
136 | UserAccount account = m_scene.UserAccountService.GetUserAccount( | 136 | string username = m_scene.UserManagementModule.GetUserName(ownerID); |
137 | m_scene.RegionInfo.ScopeID, ownerID); | 137 | string ownerFirstName, ownerLastName = String.Empty; |
138 | string ownerFirstName, ownerLastName; | 138 | if (!String.IsNullOrEmpty(username)) |
139 | if (account != null) | ||
140 | { | 139 | { |
141 | ownerFirstName = account.FirstName; | 140 | string[] parts = username.Split(' '); |
142 | ownerLastName = account.LastName; | 141 | ownerFirstName = parts[0]; |
142 | if (parts.Length > 1) | ||
143 | ownerLastName = username.Split(' ')[1]; | ||
143 | } | 144 | } |
144 | else | 145 | else |
145 | { | 146 | { |
@@ -170,17 +171,16 @@ namespace OpenSim.Region.CoreModules.Avatar.Dialog | |||
170 | } | 171 | } |
171 | 172 | ||
172 | public void SendTextBoxToUser(UUID avatarid, string message, | 173 | public void SendTextBoxToUser(UUID avatarid, string message, |
173 | int chatChannel, string name, UUID objectid, UUID ownerid) | 174 | int chatChannel, string name, UUID objectid, UUID ownerID) |
174 | { | 175 | { |
175 | UserAccount account = m_scene.UserAccountService.GetUserAccount( | 176 | string username = m_scene.UserManagementModule.GetUserName(ownerID); |
176 | m_scene.RegionInfo.ScopeID, ownerid); | 177 | string ownerFirstName, ownerLastName = String.Empty; |
177 | string ownerFirstName, ownerLastName; | 178 | if (!String.IsNullOrEmpty(username)) |
178 | UUID ownerID = UUID.Zero; | ||
179 | if (account != null) | ||
180 | { | 179 | { |
181 | ownerFirstName = account.FirstName; | 180 | string[] parts = username.Split(' '); |
182 | ownerLastName = account.LastName; | 181 | ownerFirstName = parts[0]; |
183 | ownerID = account.PrincipalID; | 182 | if (parts.Length > 1) |
183 | ownerLastName = username.Split(' ')[1]; | ||
184 | } | 184 | } |
185 | else | 185 | else |
186 | { | 186 | { |
diff --git a/OpenSim/Region/CoreModules/Avatar/Friends/CallingCardModule.cs b/OpenSim/Region/CoreModules/Avatar/Friends/CallingCardModule.cs index 5ec0ea9..eb23e83 100644 --- a/OpenSim/Region/CoreModules/Avatar/Friends/CallingCardModule.cs +++ b/OpenSim/Region/CoreModules/Avatar/Friends/CallingCardModule.cs | |||
@@ -36,6 +36,7 @@ using OpenSim.Region.Framework.Interfaces; | |||
36 | using OpenSim.Region.Framework.Scenes; | 36 | using OpenSim.Region.Framework.Scenes; |
37 | using OpenSim.Services.Interfaces; | 37 | using OpenSim.Services.Interfaces; |
38 | using Mono.Addins; | 38 | using Mono.Addins; |
39 | using PermissionMask = OpenSim.Framework.PermissionMask; | ||
39 | 40 | ||
40 | namespace OpenSim.Region.CoreModules.Avatar.Friends | 41 | namespace OpenSim.Region.CoreModules.Avatar.Friends |
41 | { | 42 | { |
@@ -180,7 +181,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Friends | |||
180 | if (folderID == UUID.Zero) | 181 | if (folderID == UUID.Zero) |
181 | { | 182 | { |
182 | InventoryFolderBase folder = inv.GetFolderForType(userID, | 183 | InventoryFolderBase folder = inv.GetFolderForType(userID, |
183 | AssetType.CallingCard); | 184 | FolderType.CallingCard); |
184 | 185 | ||
185 | if (folder == null) // Nowhere to put it | 186 | if (folder == null) // Nowhere to put it |
186 | return UUID.Zero; | 187 | return UUID.Zero; |
@@ -236,7 +237,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Friends | |||
236 | IInventoryService invService = m_Scenes[0].InventoryService; | 237 | IInventoryService invService = m_Scenes[0].InventoryService; |
237 | 238 | ||
238 | InventoryFolderBase trashFolder = | 239 | InventoryFolderBase trashFolder = |
239 | invService.GetFolderForType(client.AgentId, AssetType.TrashFolder); | 240 | invService.GetFolderForType(client.AgentId, FolderType.Trash); |
240 | 241 | ||
241 | InventoryItemBase item = new InventoryItemBase(transactionID, client.AgentId); | 242 | InventoryItemBase item = new InventoryItemBase(transactionID, client.AgentId); |
242 | item = invService.GetItem(item); | 243 | item = invService.GetItem(item); |
diff --git a/OpenSim/Region/CoreModules/Avatar/Friends/FriendsModule.cs b/OpenSim/Region/CoreModules/Avatar/Friends/FriendsModule.cs index 8056030..08e7dd2 100644 --- a/OpenSim/Region/CoreModules/Avatar/Friends/FriendsModule.cs +++ b/OpenSim/Region/CoreModules/Avatar/Friends/FriendsModule.cs | |||
@@ -38,7 +38,6 @@ using OpenMetaverse; | |||
38 | using Mono.Addins; | 38 | using Mono.Addins; |
39 | using OpenSim.Framework; | 39 | using OpenSim.Framework; |
40 | using OpenSim.Framework.Servers.HttpServer; | 40 | using OpenSim.Framework.Servers.HttpServer; |
41 | using OpenSim.Framework.Communications; | ||
42 | using OpenSim.Framework.Servers; | 41 | using OpenSim.Framework.Servers; |
43 | using OpenSim.Region.Framework.Interfaces; | 42 | using OpenSim.Region.Framework.Interfaces; |
44 | using OpenSim.Region.Framework.Scenes; | 43 | using OpenSim.Region.Framework.Scenes; |
@@ -94,6 +93,12 @@ namespace OpenSim.Region.CoreModules.Avatar.Friends | |||
94 | protected Dictionary<UUID, UserFriendData> m_Friends = new Dictionary<UUID, UserFriendData>(); | 93 | protected Dictionary<UUID, UserFriendData> m_Friends = new Dictionary<UUID, UserFriendData>(); |
95 | 94 | ||
96 | /// <summary> | 95 | /// <summary> |
96 | /// Maintain a record of clients that need to notify about their online status. This only | ||
97 | /// needs to be done on login. Subsequent online/offline friend changes are sent by a different mechanism. | ||
98 | /// </summary> | ||
99 | protected HashSet<UUID> m_NeedsToNotifyStatus = new HashSet<UUID>(); | ||
100 | |||
101 | /// <summary> | ||
97 | /// Maintain a record of viewers that need to be sent notifications for friends that are online. This only | 102 | /// Maintain a record of viewers that need to be sent notifications for friends that are online. This only |
98 | /// needs to be done on login. Subsequent online/offline friend changes are sent by a different mechanism. | 103 | /// needs to be done on login. Subsequent online/offline friend changes are sent by a different mechanism. |
99 | /// </summary> | 104 | /// </summary> |
@@ -324,6 +329,15 @@ namespace OpenSim.Region.CoreModules.Avatar.Friends | |||
324 | private void OnMakeRootAgent(ScenePresence sp) | 329 | private void OnMakeRootAgent(ScenePresence sp) |
325 | { | 330 | { |
326 | RecacheFriends(sp.ControllingClient); | 331 | RecacheFriends(sp.ControllingClient); |
332 | |||
333 | lock (m_NeedsToNotifyStatus) | ||
334 | { | ||
335 | if (m_NeedsToNotifyStatus.Remove(sp.UUID)) | ||
336 | { | ||
337 | // Inform the friends that this user is online. This can only be done once the client is a Root Agent. | ||
338 | StatusChange(sp.UUID, true); | ||
339 | } | ||
340 | } | ||
327 | } | 341 | } |
328 | 342 | ||
329 | private void OnClientLogin(IClientAPI client) | 343 | private void OnClientLogin(IClientAPI client) |
@@ -331,8 +345,13 @@ namespace OpenSim.Region.CoreModules.Avatar.Friends | |||
331 | UUID agentID = client.AgentId; | 345 | UUID agentID = client.AgentId; |
332 | 346 | ||
333 | //m_log.DebugFormat("[XXX]: OnClientLogin!"); | 347 | //m_log.DebugFormat("[XXX]: OnClientLogin!"); |
334 | // Inform the friends that this user is online | 348 | |
335 | StatusChange(agentID, true); | 349 | // Register that we need to send this user's status to friends. This can only be done |
350 | // once the client becomes a Root Agent, because as part of sending out the presence | ||
351 | // we also get back the presence of the HG friends, and we need to send that to the | ||
352 | // client, but that can only be done when the client is a Root Agent. | ||
353 | lock (m_NeedsToNotifyStatus) | ||
354 | m_NeedsToNotifyStatus.Add(agentID); | ||
336 | 355 | ||
337 | // Register that we need to send the list of online friends to this user | 356 | // Register that we need to send the list of online friends to this user |
338 | lock (m_NeedsListOfOnlineFriends) | 357 | lock (m_NeedsListOfOnlineFriends) |
@@ -371,7 +390,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Friends | |||
371 | foreach (string fid in outstanding) | 390 | foreach (string fid in outstanding) |
372 | { | 391 | { |
373 | UUID fromAgentID; | 392 | UUID fromAgentID; |
374 | string firstname = "Unknown", lastname = "User"; | 393 | string firstname = "Unknown", lastname = "UserFMSFOIN"; |
375 | if (!GetAgentInfo(client.Scene.RegionInfo.ScopeID, fid, out fromAgentID, out firstname, out lastname)) | 394 | if (!GetAgentInfo(client.Scene.RegionInfo.ScopeID, fid, out fromAgentID, out firstname, out lastname)) |
376 | { | 395 | { |
377 | m_log.DebugFormat("[FRIENDS MODULE]: skipping malformed friend {0}", fid); | 396 | m_log.DebugFormat("[FRIENDS MODULE]: skipping malformed friend {0}", fid); |
@@ -397,7 +416,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Friends | |||
397 | 416 | ||
398 | protected virtual bool GetAgentInfo(UUID scopeID, string fid, out UUID agentID, out string first, out string last) | 417 | protected virtual bool GetAgentInfo(UUID scopeID, string fid, out UUID agentID, out string first, out string last) |
399 | { | 418 | { |
400 | first = "Unknown"; last = "User"; | 419 | first = "Unknown"; last = "UserFMGAI"; |
401 | if (!UUID.TryParse(fid, out agentID)) | 420 | if (!UUID.TryParse(fid, out agentID)) |
402 | return false; | 421 | return false; |
403 | 422 | ||
@@ -491,13 +510,15 @@ namespace OpenSim.Region.CoreModules.Avatar.Friends | |||
491 | 510 | ||
492 | // Notify about this user status | 511 | // Notify about this user status |
493 | StatusNotify(friendList, agentID, online); | 512 | StatusNotify(friendList, agentID, online); |
494 | } | 513 | }, null, "FriendsModule.StatusChange" |
495 | ); | 514 | ); |
496 | } | 515 | } |
497 | } | 516 | } |
498 | 517 | ||
499 | protected virtual void StatusNotify(List<FriendInfo> friendList, UUID userID, bool online) | 518 | protected virtual void StatusNotify(List<FriendInfo> friendList, UUID userID, bool online) |
500 | { | 519 | { |
520 | //m_log.DebugFormat("[FRIENDS]: Entering StatusNotify for {0}", userID); | ||
521 | |||
501 | List<string> friendStringIds = friendList.ConvertAll<string>(friend => friend.Friend); | 522 | List<string> friendStringIds = friendList.ConvertAll<string>(friend => friend.Friend); |
502 | List<string> remoteFriendStringIds = new List<string>(); | 523 | List<string> remoteFriendStringIds = new List<string>(); |
503 | foreach (string friendStringId in friendStringIds) | 524 | foreach (string friendStringId in friendStringIds) |
@@ -523,12 +544,17 @@ namespace OpenSim.Region.CoreModules.Avatar.Friends | |||
523 | foreach (PresenceInfo friendSession in friendSessions) | 544 | foreach (PresenceInfo friendSession in friendSessions) |
524 | { | 545 | { |
525 | // let's guard against sessions-gone-bad | 546 | // let's guard against sessions-gone-bad |
526 | if (friendSession.RegionID != UUID.Zero) | 547 | if (friendSession != null && friendSession.RegionID != UUID.Zero) |
527 | { | 548 | { |
549 | //m_log.DebugFormat("[FRIENDS]: Get region {0}", friendSession.RegionID); | ||
528 | GridRegion region = GridService.GetRegionByUUID(m_Scenes[0].RegionInfo.ScopeID, friendSession.RegionID); | 550 | GridRegion region = GridService.GetRegionByUUID(m_Scenes[0].RegionInfo.ScopeID, friendSession.RegionID); |
529 | //m_log.DebugFormat("[FRIENDS]: Remote Notify to region {0}", region.RegionName); | 551 | if (region != null) |
530 | m_FriendsSimConnector.StatusNotify(region, userID, friendSession.UserID, online); | 552 | { |
553 | m_FriendsSimConnector.StatusNotify(region, userID, friendSession.UserID, online); | ||
554 | } | ||
531 | } | 555 | } |
556 | //else | ||
557 | // m_log.DebugFormat("[FRIENDS]: friend session is null or the region is UUID.Zero"); | ||
532 | } | 558 | } |
533 | } | 559 | } |
534 | 560 | ||
@@ -685,7 +711,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Friends | |||
685 | // | 711 | // |
686 | 712 | ||
687 | // Try local | 713 | // Try local |
688 | if (LocalFriendshipTerminated(exfriendID)) | 714 | if (LocalFriendshipTerminated(client.AgentId, exfriendID)) |
689 | return; | 715 | return; |
690 | 716 | ||
691 | PresenceInfo[] friendSessions = PresenceService.GetAgents(new string[] { exfriendID.ToString() }); | 717 | PresenceInfo[] friendSessions = PresenceService.GetAgents(new string[] { exfriendID.ToString() }); |
@@ -827,13 +853,13 @@ namespace OpenSim.Region.CoreModules.Avatar.Friends | |||
827 | return false; | 853 | return false; |
828 | } | 854 | } |
829 | 855 | ||
830 | public bool LocalFriendshipTerminated(UUID exfriendID) | 856 | public bool LocalFriendshipTerminated(UUID userID, UUID exfriendID) |
831 | { | 857 | { |
832 | IClientAPI friendClient = LocateClientObject(exfriendID); | 858 | IClientAPI friendClient = LocateClientObject(exfriendID); |
833 | if (friendClient != null) | 859 | if (friendClient != null) |
834 | { | 860 | { |
835 | // the friend in this sim as root agent | 861 | // the friend in this sim as root agent |
836 | friendClient.SendTerminateFriend(exfriendID); | 862 | friendClient.SendTerminateFriend(userID); |
837 | // update local cache | 863 | // update local cache |
838 | RecacheFriends(friendClient); | 864 | RecacheFriends(friendClient); |
839 | // we're done | 865 | // we're done |
diff --git a/OpenSim/Region/CoreModules/Avatar/Friends/FriendsRequestHandler.cs b/OpenSim/Region/CoreModules/Avatar/Friends/FriendsRequestHandler.cs index 637beef..13512a2 100644 --- a/OpenSim/Region/CoreModules/Avatar/Friends/FriendsRequestHandler.cs +++ b/OpenSim/Region/CoreModules/Avatar/Friends/FriendsRequestHandler.cs | |||
@@ -1,4 +1,4 @@ | |||
1 | /* | 1 | /* |
2 | * Copyright (c) Contributors, http://opensimulator.org/ | 2 | * Copyright (c) Contributors, http://opensimulator.org/ |
3 | * See CONTRIBUTORS.TXT for a full list of copyright holders. | 3 | * See CONTRIBUTORS.TXT for a full list of copyright holders. |
4 | * | 4 | * |
@@ -42,19 +42,27 @@ using log4net; | |||
42 | 42 | ||
43 | namespace OpenSim.Region.CoreModules.Avatar.Friends | 43 | namespace OpenSim.Region.CoreModules.Avatar.Friends |
44 | { | 44 | { |
45 | public class FriendsRequestHandler : BaseStreamHandler | 45 | public class FriendsRequestHandler : BaseStreamHandlerBasicDOSProtector |
46 | { | 46 | { |
47 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | 47 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); |
48 | 48 | ||
49 | private FriendsModule m_FriendsModule; | 49 | private FriendsModule m_FriendsModule; |
50 | 50 | ||
51 | public FriendsRequestHandler(FriendsModule fmodule) | 51 | public FriendsRequestHandler(FriendsModule fmodule) |
52 | : base("POST", "/friends") | 52 | : base("POST", "/friends", new BasicDosProtectorOptions() |
53 | { | ||
54 | AllowXForwardedFor = true, | ||
55 | ForgetTimeSpan = TimeSpan.FromMinutes(2), | ||
56 | MaxRequestsInTimeframe = 20, | ||
57 | ReportingName = "FRIENDSDOSPROTECTOR", | ||
58 | RequestTimeSpan = TimeSpan.FromSeconds(5), | ||
59 | ThrottledAction = BasicDOSProtector.ThrottleAction.DoThrottledMethod | ||
60 | }) | ||
53 | { | 61 | { |
54 | m_FriendsModule = fmodule; | 62 | m_FriendsModule = fmodule; |
55 | } | 63 | } |
56 | 64 | ||
57 | public override byte[] Handle( | 65 | protected override byte[] ProcessRequest( |
58 | string path, Stream requestData, IOSHttpRequest httpRequest, IOSHttpResponse httpResponse) | 66 | string path, Stream requestData, IOSHttpRequest httpRequest, IOSHttpResponse httpResponse) |
59 | { | 67 | { |
60 | StreamReader sr = new StreamReader(requestData); | 68 | StreamReader sr = new StreamReader(requestData); |
@@ -193,7 +201,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Friends | |||
193 | if (!UUID.TryParse(request["ToID"].ToString(), out toID)) | 201 | if (!UUID.TryParse(request["ToID"].ToString(), out toID)) |
194 | return FailureResult(); | 202 | return FailureResult(); |
195 | 203 | ||
196 | if (m_FriendsModule.LocalFriendshipTerminated(toID)) | 204 | if (m_FriendsModule.LocalFriendshipTerminated(fromID, toID)) |
197 | return SuccessResult(); | 205 | return SuccessResult(); |
198 | 206 | ||
199 | return FailureResult(); | 207 | return FailureResult(); |
@@ -281,18 +289,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Friends | |||
281 | 289 | ||
282 | rootElement.AppendChild(result); | 290 | rootElement.AppendChild(result); |
283 | 291 | ||
284 | return DocToBytes(doc); | 292 | return Util.DocToBytes(doc); |
285 | } | ||
286 | |||
287 | private byte[] DocToBytes(XmlDocument doc) | ||
288 | { | ||
289 | MemoryStream ms = new MemoryStream(); | ||
290 | XmlTextWriter xw = new XmlTextWriter(ms, null); | ||
291 | xw.Formatting = Formatting.Indented; | ||
292 | doc.WriteTo(xw); | ||
293 | xw.Flush(); | ||
294 | |||
295 | return ms.ToArray(); | ||
296 | } | 293 | } |
297 | 294 | ||
298 | #endregion | 295 | #endregion |
diff --git a/OpenSim/Region/CoreModules/Avatar/Friends/HGFriendsModule.cs b/OpenSim/Region/CoreModules/Avatar/Friends/HGFriendsModule.cs index bf5c0bb..27b7376 100644 --- a/OpenSim/Region/CoreModules/Avatar/Friends/HGFriendsModule.cs +++ b/OpenSim/Region/CoreModules/Avatar/Friends/HGFriendsModule.cs | |||
@@ -183,6 +183,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Friends | |||
183 | if (Util.ParseUniversalUserIdentifier(finfo.Friend, out id, out url, out first, out last, out tmp)) | 183 | if (Util.ParseUniversalUserIdentifier(finfo.Friend, out id, out url, out first, out last, out tmp)) |
184 | { | 184 | { |
185 | IUserManagement uMan = m_Scenes[0].RequestModuleInterface<IUserManagement>(); | 185 | IUserManagement uMan = m_Scenes[0].RequestModuleInterface<IUserManagement>(); |
186 | m_log.DebugFormat("[HGFRIENDS MODULE]: caching {0}", finfo.Friend); | ||
186 | uMan.AddUser(id, url + ";" + first + " " + last); | 187 | uMan.AddUser(id, url + ";" + first + " " + last); |
187 | } | 188 | } |
188 | } | 189 | } |
@@ -238,6 +239,8 @@ namespace OpenSim.Region.CoreModules.Avatar.Friends | |||
238 | fList.Add(s.Substring(0, 36)); | 239 | fList.Add(s.Substring(0, 36)); |
239 | } | 240 | } |
240 | 241 | ||
242 | // FIXME: also query the presence status of friends in other grids (like in HGStatusNotifier.Notify()) | ||
243 | |||
241 | PresenceInfo[] presence = PresenceService.GetAgents(fList.ToArray()); | 244 | PresenceInfo[] presence = PresenceService.GetAgents(fList.ToArray()); |
242 | foreach (PresenceInfo pi in presence) | 245 | foreach (PresenceInfo pi in presence) |
243 | { | 246 | { |
@@ -251,7 +254,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Friends | |||
251 | 254 | ||
252 | protected override void StatusNotify(List<FriendInfo> friendList, UUID userID, bool online) | 255 | protected override void StatusNotify(List<FriendInfo> friendList, UUID userID, bool online) |
253 | { | 256 | { |
254 | // m_log.DebugFormat("[HGFRIENDS MODULE]: Entering StatusNotify for {0}", userID); | 257 | //m_log.DebugFormat("[HGFRIENDS MODULE]: Entering StatusNotify for {0}", userID); |
255 | 258 | ||
256 | // First, let's divide the friends on a per-domain basis | 259 | // First, let's divide the friends on a per-domain basis |
257 | Dictionary<string, List<FriendInfo>> friendsPerDomain = new Dictionary<string, List<FriendInfo>>(); | 260 | Dictionary<string, List<FriendInfo>> friendsPerDomain = new Dictionary<string, List<FriendInfo>>(); |
@@ -293,7 +296,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Friends | |||
293 | 296 | ||
294 | protected override bool GetAgentInfo(UUID scopeID, string fid, out UUID agentID, out string first, out string last) | 297 | protected override bool GetAgentInfo(UUID scopeID, string fid, out UUID agentID, out string first, out string last) |
295 | { | 298 | { |
296 | first = "Unknown"; last = "User"; | 299 | first = "Unknown"; last = "UserHGGAI"; |
297 | if (base.GetAgentInfo(scopeID, fid, out agentID, out first, out last)) | 300 | if (base.GetAgentInfo(scopeID, fid, out agentID, out first, out last)) |
298 | return true; | 301 | return true; |
299 | 302 | ||
@@ -349,7 +352,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Friends | |||
349 | 352 | ||
350 | public override FriendInfo[] GetFriendsFromService(IClientAPI client) | 353 | public override FriendInfo[] GetFriendsFromService(IClientAPI client) |
351 | { | 354 | { |
352 | // m_log.DebugFormat("[HGFRIENDS MODULE]: Entering GetFriendsFromService for {0}", client.Name); | 355 | // m_log.DebugFormat("[HGFRIENDS MODULE]: Entering GetFriendsFromService for {0}", client.Name); |
353 | Boolean agentIsLocal = true; | 356 | Boolean agentIsLocal = true; |
354 | if (UserManagementModule != null) | 357 | if (UserManagementModule != null) |
355 | agentIsLocal = UserManagementModule.IsLocalGridUser(client.AgentId); | 358 | agentIsLocal = UserManagementModule.IsLocalGridUser(client.AgentId); |
@@ -362,13 +365,12 @@ namespace OpenSim.Region.CoreModules.Avatar.Friends | |||
362 | AgentCircuitData agentClientCircuit = ((Scene)(client.Scene)).AuthenticateHandler.GetAgentCircuitData(client.CircuitCode); | 365 | AgentCircuitData agentClientCircuit = ((Scene)(client.Scene)).AuthenticateHandler.GetAgentCircuitData(client.CircuitCode); |
363 | if (agentClientCircuit != null) | 366 | if (agentClientCircuit != null) |
364 | { | 367 | { |
365 | //[XXX] string agentUUI = Util.ProduceUserUniversalIdentifier(agentClientCircuit); | 368 | // Note that this is calling a different interface than base; this one calls with a string param! |
366 | |||
367 | finfos = FriendsService.GetFriends(client.AgentId.ToString()); | 369 | finfos = FriendsService.GetFriends(client.AgentId.ToString()); |
368 | m_log.DebugFormat("[HGFRIENDS MODULE]: Fetched {0} local friends for visitor {1}", finfos.Length, client.AgentId.ToString()); | 370 | m_log.DebugFormat("[HGFRIENDS MODULE]: Fetched {0} local friends for visitor {1}", finfos.Length, client.AgentId.ToString()); |
369 | } | 371 | } |
370 | 372 | ||
371 | // m_log.DebugFormat("[HGFRIENDS MODULE]: Exiting GetFriendsFromService for {0}", client.Name); | 373 | // m_log.DebugFormat("[HGFRIENDS MODULE]: Exiting GetFriendsFromService for {0}", client.Name); |
372 | 374 | ||
373 | return finfos; | 375 | return finfos; |
374 | } | 376 | } |
@@ -658,7 +660,8 @@ namespace OpenSim.Region.CoreModules.Avatar.Friends | |||
658 | FriendsService.Delete(friendUUI, agentID.ToString()); | 660 | FriendsService.Delete(friendUUI, agentID.ToString()); |
659 | 661 | ||
660 | // notify the exfriend's service | 662 | // notify the exfriend's service |
661 | Util.FireAndForget(delegate { Delete(exfriendID, agentID, friendUUI); }); | 663 | Util.FireAndForget( |
664 | delegate { Delete(exfriendID, agentID, friendUUI); }, null, "HGFriendsModule.DeleteFriendshipForeignFriend"); | ||
662 | 665 | ||
663 | m_log.DebugFormat("[HGFRIENDS MODULE]: {0} terminated {1}", agentID, friendUUI); | 666 | m_log.DebugFormat("[HGFRIENDS MODULE]: {0} terminated {1}", agentID, friendUUI); |
664 | return true; | 667 | return true; |
@@ -676,7 +679,8 @@ namespace OpenSim.Region.CoreModules.Avatar.Friends | |||
676 | FriendsService.Delete(agentUUI, exfriendID.ToString()); | 679 | FriendsService.Delete(agentUUI, exfriendID.ToString()); |
677 | 680 | ||
678 | // notify the agent's service? | 681 | // notify the agent's service? |
679 | Util.FireAndForget(delegate { Delete(agentID, exfriendID, agentUUI); }); | 682 | Util.FireAndForget( |
683 | delegate { Delete(agentID, exfriendID, agentUUI); }, null, "HGFriendsModule.DeleteFriendshipLocalFriend"); | ||
680 | 684 | ||
681 | m_log.DebugFormat("[HGFRIENDS MODULE]: {0} terminated {1}", agentUUI, exfriendID); | 685 | m_log.DebugFormat("[HGFRIENDS MODULE]: {0} terminated {1}", agentUUI, exfriendID); |
682 | return true; | 686 | return true; |
diff --git a/OpenSim/Region/CoreModules/Avatar/Friends/Tests/FriendModuleTests.cs b/OpenSim/Region/CoreModules/Avatar/Friends/Tests/FriendModuleTests.cs index 961117e..e6fd54e 100644 --- a/OpenSim/Region/CoreModules/Avatar/Friends/Tests/FriendModuleTests.cs +++ b/OpenSim/Region/CoreModules/Avatar/Friends/Tests/FriendModuleTests.cs | |||
@@ -35,7 +35,6 @@ using OpenSim.Framework; | |||
35 | using OpenSim.Region.CoreModules.Avatar.Friends; | 35 | using OpenSim.Region.CoreModules.Avatar.Friends; |
36 | using OpenSim.Region.Framework.Scenes; | 36 | using OpenSim.Region.Framework.Scenes; |
37 | using OpenSim.Tests.Common; | 37 | using OpenSim.Tests.Common; |
38 | using OpenSim.Tests.Common.Mock; | ||
39 | 38 | ||
40 | namespace OpenSim.Region.CoreModules.Avatar.Friends.Tests | 39 | namespace OpenSim.Region.CoreModules.Avatar.Friends.Tests |
41 | { | 40 | { |
diff --git a/OpenSim/Region/CoreModules/Avatar/Gods/GodsModule.cs b/OpenSim/Region/CoreModules/Avatar/Gods/GodsModule.cs index 5a7446f..3b6d970 100644 --- a/OpenSim/Region/CoreModules/Avatar/Gods/GodsModule.cs +++ b/OpenSim/Region/CoreModules/Avatar/Gods/GodsModule.cs | |||
@@ -26,20 +26,37 @@ | |||
26 | */ | 26 | */ |
27 | 27 | ||
28 | using System; | 28 | using System; |
29 | using System.Collections; | ||
29 | using System.Collections.Generic; | 30 | using System.Collections.Generic; |
31 | using System.Collections.Specialized; | ||
32 | using System.IO; | ||
33 | using System.Reflection; | ||
34 | using System.Web; | ||
35 | using System.Xml; | ||
36 | using log4net; | ||
37 | using Mono.Addins; | ||
30 | using Nini.Config; | 38 | using Nini.Config; |
31 | using OpenMetaverse; | 39 | using OpenMetaverse; |
40 | using OpenMetaverse.Messages.Linden; | ||
41 | using OpenMetaverse.StructuredData; | ||
32 | using OpenSim.Framework; | 42 | using OpenSim.Framework; |
43 | using OpenSim.Framework.Capabilities; | ||
44 | using OpenSim.Framework.Servers; | ||
45 | using OpenSim.Framework.Servers.HttpServer; | ||
33 | using OpenSim.Region.Framework.Scenes; | 46 | using OpenSim.Region.Framework.Scenes; |
34 | using OpenSim.Region.Framework.Interfaces; | 47 | using OpenSim.Region.Framework.Interfaces; |
35 | 48 | using Caps = OpenSim.Framework.Capabilities.Caps; | |
36 | using Mono.Addins; | 49 | using OSDArray = OpenMetaverse.StructuredData.OSDArray; |
50 | using OSDMap = OpenMetaverse.StructuredData.OSDMap; | ||
37 | 51 | ||
38 | namespace OpenSim.Region.CoreModules.Avatar.Gods | 52 | namespace OpenSim.Region.CoreModules.Avatar.Gods |
39 | { | 53 | { |
40 | [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "GodsModule")] | 54 | [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "GodsModule")] |
41 | public class GodsModule : INonSharedRegionModule, IGodsModule | 55 | public class GodsModule : INonSharedRegionModule, IGodsModule |
42 | { | 56 | { |
57 | private static readonly ILog m_log = | ||
58 | LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | ||
59 | |||
43 | /// <summary>Special UUID for actions that apply to all agents</summary> | 60 | /// <summary>Special UUID for actions that apply to all agents</summary> |
44 | private static readonly UUID ALL_AGENTS = new UUID("44e87126-e794-4ded-05b3-7c42da3d5cdb"); | 61 | private static readonly UUID ALL_AGENTS = new UUID("44e87126-e794-4ded-05b3-7c42da3d5cdb"); |
45 | 62 | ||
@@ -65,6 +82,9 @@ namespace OpenSim.Region.CoreModules.Avatar.Gods | |||
65 | m_scene = scene; | 82 | m_scene = scene; |
66 | m_scene.RegisterModuleInterface<IGodsModule>(this); | 83 | m_scene.RegisterModuleInterface<IGodsModule>(this); |
67 | m_scene.EventManager.OnNewClient += SubscribeToClientEvents; | 84 | m_scene.EventManager.OnNewClient += SubscribeToClientEvents; |
85 | m_scene.EventManager.OnRegisterCaps += OnRegisterCaps; | ||
86 | scene.EventManager.OnIncomingInstantMessage += | ||
87 | OnIncomingInstantMessage; | ||
68 | } | 88 | } |
69 | 89 | ||
70 | public void RemoveRegion(Scene scene) | 90 | public void RemoveRegion(Scene scene) |
@@ -98,6 +118,47 @@ namespace OpenSim.Region.CoreModules.Avatar.Gods | |||
98 | client.OnRequestGodlikePowers -= RequestGodlikePowers; | 118 | client.OnRequestGodlikePowers -= RequestGodlikePowers; |
99 | } | 119 | } |
100 | 120 | ||
121 | private void OnRegisterCaps(UUID agentID, Caps caps) | ||
122 | { | ||
123 | string uri = "/CAPS/" + UUID.Random(); | ||
124 | |||
125 | caps.RegisterHandler( | ||
126 | "UntrustedSimulatorMessage", | ||
127 | new RestStreamHandler("POST", uri, HandleUntrustedSimulatorMessage, "UntrustedSimulatorMessage", null)); | ||
128 | } | ||
129 | |||
130 | private string HandleUntrustedSimulatorMessage(string request, | ||
131 | string path, string param, IOSHttpRequest httpRequest, | ||
132 | IOSHttpResponse httpResponse) | ||
133 | { | ||
134 | OSDMap osd = (OSDMap)OSDParser.DeserializeLLSDXml(request); | ||
135 | |||
136 | string message = osd["message"].AsString(); | ||
137 | |||
138 | if (message == "GodKickUser") | ||
139 | { | ||
140 | OSDMap body = (OSDMap)osd["body"]; | ||
141 | OSDArray userInfo = (OSDArray)body["UserInfo"]; | ||
142 | OSDMap userData = (OSDMap)userInfo[0]; | ||
143 | |||
144 | UUID agentID = userData["AgentID"].AsUUID(); | ||
145 | UUID godID = userData["GodID"].AsUUID(); | ||
146 | UUID godSessionID = userData["GodSessionID"].AsUUID(); | ||
147 | uint kickFlags = userData["KickFlags"].AsUInteger(); | ||
148 | string reason = userData["Reason"].AsString(); | ||
149 | ScenePresence god = m_scene.GetScenePresence(godID); | ||
150 | if (god == null || god.ControllingClient.SessionId != godSessionID) | ||
151 | return String.Empty; | ||
152 | |||
153 | KickUser(godID, godSessionID, agentID, kickFlags, Util.StringToBytes1024(reason)); | ||
154 | } | ||
155 | else | ||
156 | { | ||
157 | m_log.ErrorFormat("[GOD]: Unhandled UntrustedSimulatorMessage: {0}", message); | ||
158 | } | ||
159 | return String.Empty; | ||
160 | } | ||
161 | |||
101 | public void RequestGodlikePowers( | 162 | public void RequestGodlikePowers( |
102 | UUID agentID, UUID sessionID, UUID token, bool godLike, IClientAPI controllingClient) | 163 | UUID agentID, UUID sessionID, UUID token, bool godLike, IClientAPI controllingClient) |
103 | { | 164 | { |
@@ -146,76 +207,85 @@ namespace OpenSim.Region.CoreModules.Avatar.Gods | |||
146 | /// <param name="reason">The message to send to the user after it's been turned into a field</param> | 207 | /// <param name="reason">The message to send to the user after it's been turned into a field</param> |
147 | public void KickUser(UUID godID, UUID sessionID, UUID agentID, uint kickflags, byte[] reason) | 208 | public void KickUser(UUID godID, UUID sessionID, UUID agentID, uint kickflags, byte[] reason) |
148 | { | 209 | { |
149 | UUID kickUserID = ALL_AGENTS; | 210 | if (!m_scene.Permissions.IsGod(godID)) |
150 | 211 | return; | |
212 | |||
151 | ScenePresence sp = m_scene.GetScenePresence(agentID); | 213 | ScenePresence sp = m_scene.GetScenePresence(agentID); |
152 | 214 | ||
153 | if (sp != null || agentID == kickUserID) | 215 | if (sp == null && agentID != ALL_AGENTS) |
154 | { | 216 | { |
155 | if (m_scene.Permissions.IsGod(godID)) | 217 | IMessageTransferModule transferModule = |
218 | m_scene.RequestModuleInterface<IMessageTransferModule>(); | ||
219 | if (transferModule != null) | ||
156 | { | 220 | { |
157 | if (kickflags == 0) | 221 | m_log.DebugFormat("[GODS]: Sending nonlocal kill for agent {0}", agentID); |
158 | { | 222 | transferModule.SendInstantMessage(new GridInstantMessage( |
159 | if (agentID == kickUserID) | 223 | m_scene, godID, "God", agentID, (byte)250, false, |
160 | { | 224 | Utils.BytesToString(reason), UUID.Zero, true, |
161 | string reasonStr = Utils.BytesToString(reason); | 225 | new Vector3(), new byte[] {(byte)kickflags}, true), |
162 | 226 | delegate(bool success) {} ); | |
163 | m_scene.ForEachClient( | 227 | } |
164 | delegate(IClientAPI controller) | 228 | return; |
165 | { | 229 | } |
166 | if (controller.AgentId != godID) | ||
167 | controller.Kick(reasonStr); | ||
168 | } | ||
169 | ); | ||
170 | |||
171 | // This is a bit crude. It seems the client will be null before it actually stops the thread | ||
172 | // The thread will kill itself eventually :/ | ||
173 | // Is there another way to make sure *all* clients get this 'inter region' message? | ||
174 | m_scene.ForEachRootClient( | ||
175 | delegate(IClientAPI client) | ||
176 | { | ||
177 | if (client.AgentId != godID) | ||
178 | { | ||
179 | client.Close(); | ||
180 | } | ||
181 | } | ||
182 | ); | ||
183 | } | ||
184 | else | ||
185 | { | ||
186 | m_scene.SceneGraph.removeUserCount(!sp.IsChildAgent); | ||
187 | 230 | ||
188 | sp.ControllingClient.Kick(Utils.BytesToString(reason)); | 231 | switch (kickflags) |
189 | sp.ControllingClient.Close(); | 232 | { |
190 | } | 233 | case 0: |
191 | } | 234 | if (sp != null) |
192 | 235 | { | |
193 | if (kickflags == 1) | 236 | KickPresence(sp, Utils.BytesToString(reason)); |
194 | { | ||
195 | sp.AllowMovement = false; | ||
196 | if (DialogModule != null) | ||
197 | { | ||
198 | DialogModule.SendAlertToUser(agentID, Utils.BytesToString(reason)); | ||
199 | DialogModule.SendAlertToUser(godID, "User Frozen"); | ||
200 | } | ||
201 | } | ||
202 | |||
203 | if (kickflags == 2) | ||
204 | { | ||
205 | sp.AllowMovement = true; | ||
206 | if (DialogModule != null) | ||
207 | { | ||
208 | DialogModule.SendAlertToUser(agentID, Utils.BytesToString(reason)); | ||
209 | DialogModule.SendAlertToUser(godID, "User Unfrozen"); | ||
210 | } | ||
211 | } | ||
212 | } | 237 | } |
213 | else | 238 | else if (agentID == ALL_AGENTS) |
214 | { | 239 | { |
215 | if (DialogModule != null) | 240 | m_scene.ForEachRootScenePresence( |
216 | DialogModule.SendAlertToUser(godID, "Kick request denied"); | 241 | delegate(ScenePresence p) |
242 | { | ||
243 | if (p.UUID != godID && (!m_scene.Permissions.IsGod(p.UUID))) | ||
244 | KickPresence(p, Utils.BytesToString(reason)); | ||
245 | } | ||
246 | ); | ||
217 | } | 247 | } |
248 | break; | ||
249 | case 1: | ||
250 | if (sp != null) | ||
251 | { | ||
252 | sp.AllowMovement = false; | ||
253 | m_dialogModule.SendAlertToUser(agentID, Utils.BytesToString(reason)); | ||
254 | m_dialogModule.SendAlertToUser(godID, "User Frozen"); | ||
255 | } | ||
256 | break; | ||
257 | case 2: | ||
258 | if (sp != null) | ||
259 | { | ||
260 | sp.AllowMovement = true; | ||
261 | m_dialogModule.SendAlertToUser(agentID, Utils.BytesToString(reason)); | ||
262 | m_dialogModule.SendAlertToUser(godID, "User Unfrozen"); | ||
263 | } | ||
264 | break; | ||
265 | default: | ||
266 | break; | ||
267 | } | ||
268 | } | ||
269 | |||
270 | private void KickPresence(ScenePresence sp, string reason) | ||
271 | { | ||
272 | if (sp.IsChildAgent) | ||
273 | return; | ||
274 | sp.ControllingClient.Kick(reason); | ||
275 | sp.Scene.CloseAgent(sp.UUID, true); | ||
276 | } | ||
277 | |||
278 | private void OnIncomingInstantMessage(GridInstantMessage msg) | ||
279 | { | ||
280 | if (msg.dialog == (uint)250) // Nonlocal kick | ||
281 | { | ||
282 | UUID agentID = new UUID(msg.toAgentID); | ||
283 | string reason = msg.message; | ||
284 | UUID godID = new UUID(msg.fromAgentID); | ||
285 | uint kickMode = (uint)msg.binaryBucket[0]; | ||
286 | |||
287 | KickUser(godID, UUID.Zero, agentID, kickMode, Util.StringToBytes1024(reason)); | ||
218 | } | 288 | } |
219 | } | 289 | } |
220 | } | 290 | } |
221 | } \ No newline at end of file | 291 | } |
diff --git a/OpenSim/Region/CoreModules/Avatar/InstantMessage/HGMessageTransferModule.cs b/OpenSim/Region/CoreModules/Avatar/InstantMessage/HGMessageTransferModule.cs index 7bf19c2..a1b918a 100644 --- a/OpenSim/Region/CoreModules/Avatar/InstantMessage/HGMessageTransferModule.cs +++ b/OpenSim/Region/CoreModules/Avatar/InstantMessage/HGMessageTransferModule.cs | |||
@@ -210,10 +210,10 @@ namespace OpenSim.Region.CoreModules.Avatar.InstantMessage | |||
210 | success = m_IMService.OutgoingInstantMessage(im, url, foreigner); | 210 | success = m_IMService.OutgoingInstantMessage(im, url, foreigner); |
211 | 211 | ||
212 | if (!success && !foreigner) | 212 | if (!success && !foreigner) |
213 | HandleUndeliveredMessage(im, result); | 213 | HandleUndeliverableMessage(im, result); |
214 | else | 214 | else |
215 | result(success); | 215 | result(success); |
216 | }); | 216 | }, null, "HGMessageTransferModule.SendInstantMessage"); |
217 | 217 | ||
218 | return; | 218 | return; |
219 | } | 219 | } |
@@ -246,7 +246,7 @@ namespace OpenSim.Region.CoreModules.Avatar.InstantMessage | |||
246 | return successful; | 246 | return successful; |
247 | } | 247 | } |
248 | 248 | ||
249 | protected void HandleUndeliveredMessage(GridInstantMessage im, MessageResultNotification result) | 249 | public void HandleUndeliverableMessage(GridInstantMessage im, MessageResultNotification result) |
250 | { | 250 | { |
251 | UndeliveredMessage handlerUndeliveredMessage = OnUndeliveredMessage; | 251 | UndeliveredMessage handlerUndeliveredMessage = OnUndeliveredMessage; |
252 | 252 | ||
@@ -282,7 +282,17 @@ namespace OpenSim.Region.CoreModules.Avatar.InstantMessage | |||
282 | string uasURL = circuit.ServiceURLs["HomeURI"].ToString(); | 282 | string uasURL = circuit.ServiceURLs["HomeURI"].ToString(); |
283 | m_log.DebugFormat("[HG MESSAGE TRANSFER]: getting UUI of user {0} from {1}", toAgent, uasURL); | 283 | m_log.DebugFormat("[HG MESSAGE TRANSFER]: getting UUI of user {0} from {1}", toAgent, uasURL); |
284 | UserAgentServiceConnector uasConn = new UserAgentServiceConnector(uasURL); | 284 | UserAgentServiceConnector uasConn = new UserAgentServiceConnector(uasURL); |
285 | return uasConn.GetUUI(fromAgent, toAgent); | 285 | |
286 | string agentUUI = string.Empty; | ||
287 | try | ||
288 | { | ||
289 | agentUUI = uasConn.GetUUI(fromAgent, toAgent); | ||
290 | } | ||
291 | catch (Exception e) { | ||
292 | m_log.Debug("[HG MESSAGE TRANSFER]: GetUUI call failed ", e); | ||
293 | } | ||
294 | |||
295 | return agentUUI; | ||
286 | } | 296 | } |
287 | } | 297 | } |
288 | } | 298 | } |
diff --git a/OpenSim/Region/CoreModules/Avatar/InstantMessage/MessageTransferModule.cs b/OpenSim/Region/CoreModules/Avatar/InstantMessage/MessageTransferModule.cs index fa935cd..2462ff8 100644 --- a/OpenSim/Region/CoreModules/Avatar/InstantMessage/MessageTransferModule.cs +++ b/OpenSim/Region/CoreModules/Avatar/InstantMessage/MessageTransferModule.cs | |||
@@ -181,7 +181,7 @@ namespace OpenSim.Region.CoreModules.Avatar.InstantMessage | |||
181 | SendGridInstantMessageViaXMLRPC(im, result); | 181 | SendGridInstantMessageViaXMLRPC(im, result); |
182 | } | 182 | } |
183 | 183 | ||
184 | private void HandleUndeliveredMessage(GridInstantMessage im, MessageResultNotification result) | 184 | public void HandleUndeliverableMessage(GridInstantMessage im, MessageResultNotification result) |
185 | { | 185 | { |
186 | UndeliveredMessage handlerUndeliveredMessage = OnUndeliveredMessage; | 186 | UndeliveredMessage handlerUndeliveredMessage = OnUndeliveredMessage; |
187 | 187 | ||
@@ -372,7 +372,7 @@ namespace OpenSim.Region.CoreModules.Avatar.InstantMessage | |||
372 | gim.fromAgentName = fromAgentName; | 372 | gim.fromAgentName = fromAgentName; |
373 | gim.fromGroup = fromGroup; | 373 | gim.fromGroup = fromGroup; |
374 | gim.imSessionID = imSessionID.Guid; | 374 | gim.imSessionID = imSessionID.Guid; |
375 | gim.RegionID = UUID.Zero.Guid; // RegionID.Guid; | 375 | gim.RegionID = RegionID.Guid; |
376 | gim.timestamp = timestamp; | 376 | gim.timestamp = timestamp; |
377 | gim.toAgentID = toAgentID.Guid; | 377 | gim.toAgentID = toAgentID.Guid; |
378 | gim.message = message; | 378 | gim.message = message; |
@@ -428,7 +428,7 @@ namespace OpenSim.Region.CoreModules.Avatar.InstantMessage | |||
428 | /// <summary> | 428 | /// <summary> |
429 | /// delegate for sending a grid instant message asynchronously | 429 | /// delegate for sending a grid instant message asynchronously |
430 | /// </summary> | 430 | /// </summary> |
431 | public delegate void GridInstantMessageDelegate(GridInstantMessage im, MessageResultNotification result, UUID prevRegionID); | 431 | public delegate void GridInstantMessageDelegate(GridInstantMessage im, MessageResultNotification result); |
432 | 432 | ||
433 | protected virtual void GridInstantMessageCompleted(IAsyncResult iar) | 433 | protected virtual void GridInstantMessageCompleted(IAsyncResult iar) |
434 | { | 434 | { |
@@ -442,138 +442,87 @@ namespace OpenSim.Region.CoreModules.Avatar.InstantMessage | |||
442 | { | 442 | { |
443 | GridInstantMessageDelegate d = SendGridInstantMessageViaXMLRPCAsync; | 443 | GridInstantMessageDelegate d = SendGridInstantMessageViaXMLRPCAsync; |
444 | 444 | ||
445 | d.BeginInvoke(im, result, UUID.Zero, GridInstantMessageCompleted, d); | 445 | d.BeginInvoke(im, result, GridInstantMessageCompleted, d); |
446 | } | 446 | } |
447 | 447 | ||
448 | /// <summary> | 448 | /// <summary> |
449 | /// Recursive SendGridInstantMessage over XMLRPC method. | 449 | /// Internal SendGridInstantMessage over XMLRPC method. |
450 | /// This is called from within a dedicated thread. | ||
451 | /// The first time this is called, prevRegionHandle will be 0 Subsequent times this is called from | ||
452 | /// itself, prevRegionHandle will be the last region handle that we tried to send. | ||
453 | /// If the handles are the same, we look up the user's location using the grid. | ||
454 | /// If the handles are still the same, we end. The send failed. | ||
455 | /// </summary> | 450 | /// </summary> |
456 | /// <param name="prevRegionHandle"> | 451 | /// <remarks> |
457 | /// Pass in 0 the first time this method is called. It will be called recursively with the last | 452 | /// This is called from within a dedicated thread. |
458 | /// regionhandle tried | 453 | /// </remarks> |
459 | /// </param> | 454 | private void SendGridInstantMessageViaXMLRPCAsync(GridInstantMessage im, MessageResultNotification result) |
460 | protected virtual void SendGridInstantMessageViaXMLRPCAsync(GridInstantMessage im, MessageResultNotification result, UUID prevRegionID) | ||
461 | { | 455 | { |
462 | UUID toAgentID = new UUID(im.toAgentID); | 456 | UUID toAgentID = new UUID(im.toAgentID); |
463 | 457 | UUID regionID; | |
464 | PresenceInfo upd = null; | 458 | bool needToLookupAgent; |
465 | |||
466 | bool lookupAgent = false; | ||
467 | 459 | ||
468 | lock (m_UserRegionMap) | 460 | lock (m_UserRegionMap) |
461 | needToLookupAgent = !m_UserRegionMap.TryGetValue(toAgentID, out regionID); | ||
462 | |||
463 | while (true) | ||
469 | { | 464 | { |
470 | if (m_UserRegionMap.ContainsKey(toAgentID)) | 465 | if (needToLookupAgent) |
471 | { | 466 | { |
472 | upd = new PresenceInfo(); | 467 | PresenceInfo[] presences = PresenceService.GetAgents(new string[] { toAgentID.ToString() }); |
473 | upd.RegionID = m_UserRegionMap[toAgentID]; | ||
474 | 468 | ||
475 | // We need to compare the current regionhandle with the previous region handle | 469 | UUID foundRegionID = UUID.Zero; |
476 | // or the recursive loop will never end because it will never try to lookup the agent again | ||
477 | if (prevRegionID == upd.RegionID) | ||
478 | { | ||
479 | lookupAgent = true; | ||
480 | } | ||
481 | } | ||
482 | else | ||
483 | { | ||
484 | lookupAgent = true; | ||
485 | } | ||
486 | } | ||
487 | |||
488 | 470 | ||
489 | // Are we needing to look-up an agent? | 471 | if (presences != null) |
490 | if (lookupAgent) | ||
491 | { | ||
492 | // Non-cached user agent lookup. | ||
493 | PresenceInfo[] presences = PresenceService.GetAgents(new string[] { toAgentID.ToString() }); | ||
494 | if (presences != null && presences.Length > 0) | ||
495 | { | ||
496 | foreach (PresenceInfo p in presences) | ||
497 | { | 472 | { |
498 | if (p.RegionID != UUID.Zero) | 473 | foreach (PresenceInfo p in presences) |
499 | { | 474 | { |
500 | upd = p; | 475 | if (p.RegionID != UUID.Zero) |
501 | break; | 476 | { |
477 | foundRegionID = p.RegionID; | ||
478 | break; | ||
479 | } | ||
502 | } | 480 | } |
503 | } | 481 | } |
504 | } | ||
505 | 482 | ||
506 | if (upd != null) | 483 | // If not found or the found region is the same as the last lookup, then message is undeliverable |
507 | { | 484 | if (foundRegionID == UUID.Zero || foundRegionID == regionID) |
508 | // check if we've tried this before.. | 485 | break; |
509 | // This is one way to end the recursive loop | 486 | else |
510 | // | 487 | regionID = foundRegionID; |
511 | if (upd.RegionID == prevRegionID) | ||
512 | { | ||
513 | // m_log.Error("[GRID INSTANT MESSAGE]: Unable to deliver an instant message"); | ||
514 | HandleUndeliveredMessage(im, result); | ||
515 | return; | ||
516 | } | ||
517 | } | 488 | } |
518 | else | 489 | |
490 | GridRegion reginfo = m_Scenes[0].GridService.GetRegionByUUID(m_Scenes[0].RegionInfo.ScopeID, regionID); | ||
491 | if (reginfo == null) | ||
519 | { | 492 | { |
520 | // m_log.Error("[GRID INSTANT MESSAGE]: Unable to deliver an instant message"); | 493 | m_log.WarnFormat("[GRID INSTANT MESSAGE]: Unable to find region {0}", regionID); |
521 | HandleUndeliveredMessage(im, result); | 494 | break; |
522 | return; | ||
523 | } | 495 | } |
524 | } | ||
525 | 496 | ||
526 | if (upd != null) | 497 | // Try to send the message to the agent via the retrieved region. |
527 | { | 498 | Hashtable msgdata = ConvertGridInstantMessageToXMLRPC(im); |
528 | GridRegion reginfo = m_Scenes[0].GridService.GetRegionByUUID(m_Scenes[0].RegionInfo.ScopeID, | 499 | msgdata["region_handle"] = 0; |
529 | upd.RegionID); | 500 | bool imresult = doIMSending(reginfo, msgdata); |
530 | if (reginfo != null) | 501 | |
502 | // If the message delivery was successful, then cache the entry. | ||
503 | if (imresult) | ||
531 | { | 504 | { |
532 | Hashtable msgdata = ConvertGridInstantMessageToXMLRPC(im); | 505 | lock (m_UserRegionMap) |
533 | // Not actually used anymore, left in for compatibility | ||
534 | // Remove at next interface change | ||
535 | // | ||
536 | msgdata["region_handle"] = 0; | ||
537 | bool imresult = doIMSending(reginfo, msgdata); | ||
538 | if (imresult) | ||
539 | { | ||
540 | // IM delivery successful, so store the Agent's location in our local cache. | ||
541 | lock (m_UserRegionMap) | ||
542 | { | ||
543 | if (m_UserRegionMap.ContainsKey(toAgentID)) | ||
544 | { | ||
545 | m_UserRegionMap[toAgentID] = upd.RegionID; | ||
546 | } | ||
547 | else | ||
548 | { | ||
549 | m_UserRegionMap.Add(toAgentID, upd.RegionID); | ||
550 | } | ||
551 | } | ||
552 | result(true); | ||
553 | } | ||
554 | else | ||
555 | { | 506 | { |
556 | // try again, but lookup user this time. | 507 | m_UserRegionMap[toAgentID] = regionID; |
557 | // Warning, this must call the Async version | ||
558 | // of this method or we'll be making thousands of threads | ||
559 | // The version within the spawned thread is SendGridInstantMessageViaXMLRPCAsync | ||
560 | // The version that spawns the thread is SendGridInstantMessageViaXMLRPC | ||
561 | |||
562 | // This is recursive!!!!! | ||
563 | SendGridInstantMessageViaXMLRPCAsync(im, result, | ||
564 | upd.RegionID); | ||
565 | } | 508 | } |
509 | result(true); | ||
510 | return; | ||
566 | } | 511 | } |
567 | else | 512 | |
568 | { | 513 | // If we reach this point in the first iteration of the while, then we may have unsuccessfully tried |
569 | m_log.WarnFormat("[GRID INSTANT MESSAGE]: Unable to find region {0}", upd.RegionID); | 514 | // to use a locally cached region ID. All subsequent attempts need to lookup agent details from |
570 | HandleUndeliveredMessage(im, result); | 515 | // the presence service. |
571 | } | 516 | needToLookupAgent = true; |
572 | } | ||
573 | else | ||
574 | { | ||
575 | HandleUndeliveredMessage(im, result); | ||
576 | } | 517 | } |
518 | |||
519 | // If we reached this point then the message was not deliverable. Remove the bad cache entry and | ||
520 | // signal the delivery failure. | ||
521 | lock (m_UserRegionMap) | ||
522 | m_UserRegionMap.Remove(toAgentID); | ||
523 | |||
524 | // m_log.Error("[GRID INSTANT MESSAGE]: Unable to deliver an instant message"); | ||
525 | HandleUndeliverableMessage(im, result); | ||
577 | } | 526 | } |
578 | 527 | ||
579 | /// <summary> | 528 | /// <summary> |
@@ -584,7 +533,6 @@ namespace OpenSim.Region.CoreModules.Avatar.InstantMessage | |||
584 | /// <returns>Bool if the message was successfully delivered at the other side.</returns> | 533 | /// <returns>Bool if the message was successfully delivered at the other side.</returns> |
585 | protected virtual bool doIMSending(GridRegion reginfo, Hashtable xmlrpcdata) | 534 | protected virtual bool doIMSending(GridRegion reginfo, Hashtable xmlrpcdata) |
586 | { | 535 | { |
587 | |||
588 | ArrayList SendParams = new ArrayList(); | 536 | ArrayList SendParams = new ArrayList(); |
589 | SendParams.Add(xmlrpcdata); | 537 | SendParams.Add(xmlrpcdata); |
590 | XmlRpcRequest GridReq = new XmlRpcRequest("grid_instant_message", SendParams); | 538 | XmlRpcRequest GridReq = new XmlRpcRequest("grid_instant_message", SendParams); |
@@ -672,7 +620,7 @@ namespace OpenSim.Region.CoreModules.Avatar.InstantMessage | |||
672 | gim["position_x"] = msg.Position.X.ToString(); | 620 | gim["position_x"] = msg.Position.X.ToString(); |
673 | gim["position_y"] = msg.Position.Y.ToString(); | 621 | gim["position_y"] = msg.Position.Y.ToString(); |
674 | gim["position_z"] = msg.Position.Z.ToString(); | 622 | gim["position_z"] = msg.Position.Z.ToString(); |
675 | gim["region_id"] = msg.RegionID.ToString(); | 623 | gim["region_id"] = new UUID(msg.RegionID).ToString(); |
676 | gim["binary_bucket"] = Convert.ToBase64String(msg.binaryBucket,Base64FormattingOptions.None); | 624 | gim["binary_bucket"] = Convert.ToBase64String(msg.binaryBucket,Base64FormattingOptions.None); |
677 | return gim; | 625 | return gim; |
678 | } | 626 | } |
diff --git a/OpenSim/Region/CoreModules/Avatar/InstantMessage/MuteListModule.cs b/OpenSim/Region/CoreModules/Avatar/InstantMessage/MuteListModule.cs index 7ce2813..315d372 100644 --- a/OpenSim/Region/CoreModules/Avatar/InstantMessage/MuteListModule.cs +++ b/OpenSim/Region/CoreModules/Avatar/InstantMessage/MuteListModule.cs | |||
@@ -32,7 +32,6 @@ using Nini.Config; | |||
32 | using Mono.Addins; | 32 | using Mono.Addins; |
33 | using OpenMetaverse; | 33 | using OpenMetaverse; |
34 | using OpenSim.Framework; | 34 | using OpenSim.Framework; |
35 | using OpenSim.Framework.Communications; | ||
36 | using OpenSim.Framework.Servers; | 35 | using OpenSim.Framework.Servers; |
37 | using OpenSim.Framework.Client; | 36 | using OpenSim.Framework.Client; |
38 | using OpenSim.Region.Framework.Interfaces; | 37 | using OpenSim.Region.Framework.Interfaces; |
diff --git a/OpenSim/Region/CoreModules/Avatar/InstantMessage/OfflineMessageModule.cs b/OpenSim/Region/CoreModules/Avatar/InstantMessage/OfflineMessageModule.cs index 7d763fa..9cdb1c2 100644 --- a/OpenSim/Region/CoreModules/Avatar/InstantMessage/OfflineMessageModule.cs +++ b/OpenSim/Region/CoreModules/Avatar/InstantMessage/OfflineMessageModule.cs | |||
@@ -32,7 +32,6 @@ using Mono.Addins; | |||
32 | using Nini.Config; | 32 | using Nini.Config; |
33 | using OpenMetaverse; | 33 | using OpenMetaverse; |
34 | using OpenSim.Framework; | 34 | using OpenSim.Framework; |
35 | using OpenSim.Framework.Communications; | ||
36 | using OpenSim.Framework.Servers; | 35 | using OpenSim.Framework.Servers; |
37 | using OpenSim.Framework.Client; | 36 | using OpenSim.Framework.Client; |
38 | using OpenSim.Region.Framework.Interfaces; | 37 | using OpenSim.Region.Framework.Interfaces; |
@@ -182,7 +181,10 @@ namespace OpenSim.Region.CoreModules.Avatar.InstantMessage | |||
182 | "POST", m_RestURL + "/RetrieveMessages/", client.AgentId); | 181 | "POST", m_RestURL + "/RetrieveMessages/", client.AgentId); |
183 | 182 | ||
184 | if (msglist == null) | 183 | if (msglist == null) |
184 | { | ||
185 | m_log.WarnFormat("[OFFLINE MESSAGING]: WARNING null message list."); | 185 | m_log.WarnFormat("[OFFLINE MESSAGING]: WARNING null message list."); |
186 | return; | ||
187 | } | ||
186 | 188 | ||
187 | foreach (GridInstantMessage im in msglist) | 189 | foreach (GridInstantMessage im in msglist) |
188 | { | 190 | { |
@@ -223,12 +225,8 @@ namespace OpenSim.Region.CoreModules.Avatar.InstantMessage | |||
223 | return; | 225 | return; |
224 | } | 226 | } |
225 | 227 | ||
226 | Scene scene = FindScene(new UUID(im.fromAgentID)); | ||
227 | if (scene == null) | ||
228 | scene = m_SceneList[0]; | ||
229 | |||
230 | bool success = SynchronousRestObjectRequester.MakeRequest<GridInstantMessage, bool>( | 228 | bool success = SynchronousRestObjectRequester.MakeRequest<GridInstantMessage, bool>( |
231 | "POST", m_RestURL+"/SaveMessage/", im); | 229 | "POST", m_RestURL+"/SaveMessage/", im, 10000); |
232 | 230 | ||
233 | if (im.dialog == (byte)InstantMessageDialog.MessageFromAgent) | 231 | if (im.dialog == (byte)InstantMessageDialog.MessageFromAgent) |
234 | { | 232 | { |
diff --git a/OpenSim/Region/CoreModules/Avatar/InstantMessage/PresenceModule.cs b/OpenSim/Region/CoreModules/Avatar/InstantMessage/PresenceModule.cs index 4c678c2..c2440d8 100644 --- a/OpenSim/Region/CoreModules/Avatar/InstantMessage/PresenceModule.cs +++ b/OpenSim/Region/CoreModules/Avatar/InstantMessage/PresenceModule.cs | |||
@@ -49,8 +49,10 @@ namespace OpenSim.Region.CoreModules.Avatar.InstantMessage | |||
49 | private static readonly ILog m_log = LogManager.GetLogger( | 49 | private static readonly ILog m_log = LogManager.GetLogger( |
50 | MethodBase.GetCurrentMethod().DeclaringType); | 50 | MethodBase.GetCurrentMethod().DeclaringType); |
51 | 51 | ||
52 | #pragma warning disable 0067 | ||
52 | public event PresenceChange OnPresenceChange; | 53 | public event PresenceChange OnPresenceChange; |
53 | public event BulkPresenceData OnBulkPresenceData; | 54 | public event BulkPresenceData OnBulkPresenceData; |
55 | #pragma warning restore 0067 | ||
54 | 56 | ||
55 | protected List<Scene> m_Scenes = new List<Scene>(); | 57 | protected List<Scene> m_Scenes = new List<Scene>(); |
56 | 58 | ||
diff --git a/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/InventoryArchiveReadRequest.cs b/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/InventoryArchiveReadRequest.cs index ecbd07f..4a06fd1 100644 --- a/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/InventoryArchiveReadRequest.cs +++ b/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/InventoryArchiveReadRequest.cs | |||
@@ -61,16 +61,22 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver | |||
61 | 61 | ||
62 | private UserAccount m_userInfo; | 62 | private UserAccount m_userInfo; |
63 | private string m_invPath; | 63 | private string m_invPath; |
64 | |||
65 | /// <value> | ||
66 | /// ID of this request | ||
67 | /// </value> | ||
68 | protected UUID m_id; | ||
64 | 69 | ||
65 | /// <summary> | 70 | /// <summary> |
66 | /// Do we want to merge this load with existing inventory? | 71 | /// Do we want to merge this load with existing inventory? |
67 | /// </summary> | 72 | /// </summary> |
68 | protected bool m_merge; | 73 | protected bool m_merge; |
69 | 74 | ||
70 | /// <value> | 75 | protected IInventoryService m_InventoryService; |
71 | /// We only use this to request modules | 76 | protected IAssetService m_AssetService; |
72 | /// </value> | 77 | protected IUserAccountService m_UserAccountService; |
73 | protected Scene m_scene; | 78 | |
79 | private InventoryArchiverModule m_module; | ||
74 | 80 | ||
75 | /// <value> | 81 | /// <value> |
76 | /// The stream from which the inventory archive will be loaded. | 82 | /// The stream from which the inventory archive will be loaded. |
@@ -115,12 +121,29 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver | |||
115 | /// Record the creator id that should be associated with an asset. This is used to adjust asset creator ids | 121 | /// Record the creator id that should be associated with an asset. This is used to adjust asset creator ids |
116 | /// after OSP resolution (since OSP creators are only stored in the item | 122 | /// after OSP resolution (since OSP creators are only stored in the item |
117 | /// </summary> | 123 | /// </summary> |
118 | protected Dictionary<UUID, UUID> m_creatorIdForAssetId = new Dictionary<UUID, UUID>(); | 124 | protected Dictionary<UUID, UUID> m_creatorIdForAssetId = new Dictionary<UUID, UUID>(); |
125 | |||
126 | public InventoryArchiveReadRequest( | ||
127 | IInventoryService inv, IAssetService assets, IUserAccountService uacc, UserAccount userInfo, string invPath, string loadPath, bool merge) | ||
128 | : this(UUID.Zero, null, | ||
129 | inv, | ||
130 | assets, | ||
131 | uacc, | ||
132 | userInfo, | ||
133 | invPath, | ||
134 | loadPath, | ||
135 | merge) | ||
136 | { | ||
137 | } | ||
119 | 138 | ||
120 | public InventoryArchiveReadRequest( | 139 | public InventoryArchiveReadRequest( |
121 | Scene scene, UserAccount userInfo, string invPath, string loadPath, bool merge) | 140 | UUID id, InventoryArchiverModule module, IInventoryService inv, IAssetService assets, IUserAccountService uacc, UserAccount userInfo, string invPath, string loadPath, bool merge) |
122 | : this( | 141 | : this( |
123 | scene, | 142 | id, |
143 | module, | ||
144 | inv, | ||
145 | assets, | ||
146 | uacc, | ||
124 | userInfo, | 147 | userInfo, |
125 | invPath, | 148 | invPath, |
126 | new GZipStream(ArchiveHelpers.GetStream(loadPath), CompressionMode.Decompress), | 149 | new GZipStream(ArchiveHelpers.GetStream(loadPath), CompressionMode.Decompress), |
@@ -129,13 +152,17 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver | |||
129 | } | 152 | } |
130 | 153 | ||
131 | public InventoryArchiveReadRequest( | 154 | public InventoryArchiveReadRequest( |
132 | Scene scene, UserAccount userInfo, string invPath, Stream loadStream, bool merge) | 155 | UUID id, InventoryArchiverModule module, IInventoryService inv, IAssetService assets, IUserAccountService uacc, UserAccount userInfo, string invPath, Stream loadStream, bool merge) |
133 | { | 156 | { |
134 | m_scene = scene; | 157 | m_id = id; |
158 | m_InventoryService = inv; | ||
159 | m_AssetService = assets; | ||
160 | m_UserAccountService = uacc; | ||
135 | m_merge = merge; | 161 | m_merge = merge; |
136 | m_userInfo = userInfo; | 162 | m_userInfo = userInfo; |
137 | m_invPath = invPath; | 163 | m_invPath = invPath; |
138 | m_loadStream = loadStream; | 164 | m_loadStream = loadStream; |
165 | m_module = module; | ||
139 | 166 | ||
140 | // FIXME: Do not perform this check since older versions of OpenSim do save the control file after other things | 167 | // FIXME: Do not perform this check since older versions of OpenSim do save the control file after other things |
141 | // (I thought they weren't). We will need to bump the version number and perform this check on all | 168 | // (I thought they weren't). We will need to bump the version number and perform this check on all |
@@ -158,11 +185,13 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver | |||
158 | { | 185 | { |
159 | try | 186 | try |
160 | { | 187 | { |
188 | Exception reportedException = null; | ||
189 | |||
161 | string filePath = "ERROR"; | 190 | string filePath = "ERROR"; |
162 | 191 | ||
163 | List<InventoryFolderBase> folderCandidates | 192 | List<InventoryFolderBase> folderCandidates |
164 | = InventoryArchiveUtils.FindFolderByPath( | 193 | = InventoryArchiveUtils.FindFoldersByPath( |
165 | m_scene.InventoryService, m_userInfo.PrincipalID, m_invPath); | 194 | m_InventoryService, m_userInfo.PrincipalID, m_invPath); |
166 | 195 | ||
167 | if (folderCandidates.Count == 0) | 196 | if (folderCandidates.Count == 0) |
168 | { | 197 | { |
@@ -194,14 +223,25 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver | |||
194 | } | 223 | } |
195 | 224 | ||
196 | archive.Close(); | 225 | archive.Close(); |
197 | 226 | ||
198 | m_log.DebugFormat( | 227 | m_log.DebugFormat( |
199 | "[INVENTORY ARCHIVER]: Successfully loaded {0} assets with {1} failures", | 228 | "[INVENTORY ARCHIVER]: Successfully loaded {0} assets with {1} failures", |
200 | m_successfulAssetRestores, m_failedAssetRestores); | 229 | m_successfulAssetRestores, m_failedAssetRestores); |
201 | m_log.InfoFormat("[INVENTORY ARCHIVER]: Successfully loaded {0} items", m_successfulItemRestores); | 230 | |
231 | //Alicia: When this is called by LibraryModule or Tests, m_module will be null as event is not required | ||
232 | if(m_module != null) | ||
233 | m_module.TriggerInventoryArchiveLoaded(m_id, true, m_userInfo, m_invPath, m_loadStream, reportedException, m_successfulItemRestores); | ||
202 | 234 | ||
203 | return m_loadedNodes; | 235 | return m_loadedNodes; |
204 | } | 236 | } |
237 | catch(Exception Ex) | ||
238 | { | ||
239 | // Trigger saved event with failed result and exception data | ||
240 | if (m_module != null) | ||
241 | m_module.TriggerInventoryArchiveLoaded(m_id, false, m_userInfo, m_invPath, m_loadStream, Ex, 0); | ||
242 | |||
243 | return m_loadedNodes; | ||
244 | } | ||
205 | finally | 245 | finally |
206 | { | 246 | { |
207 | m_loadStream.Close(); | 247 | m_loadStream.Close(); |
@@ -296,8 +336,8 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver | |||
296 | // iar name and try to find that instead. | 336 | // iar name and try to find that instead. |
297 | string plainPath = ArchiveConstants.ExtractPlainPathFromIarPath(archivePath); | 337 | string plainPath = ArchiveConstants.ExtractPlainPathFromIarPath(archivePath); |
298 | List<InventoryFolderBase> folderCandidates | 338 | List<InventoryFolderBase> folderCandidates |
299 | = InventoryArchiveUtils.FindFolderByPath( | 339 | = InventoryArchiveUtils.FindFoldersByPath( |
300 | m_scene.InventoryService, m_userInfo.PrincipalID, plainPath); | 340 | m_InventoryService, m_userInfo.PrincipalID, plainPath); |
301 | 341 | ||
302 | if (folderCandidates.Count != 0) | 342 | if (folderCandidates.Count != 0) |
303 | { | 343 | { |
@@ -372,15 +412,11 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver | |||
372 | newFolderName = InventoryArchiveUtils.UnescapeArchivePath(newFolderName); | 412 | newFolderName = InventoryArchiveUtils.UnescapeArchivePath(newFolderName); |
373 | UUID newFolderId = UUID.Random(); | 413 | UUID newFolderId = UUID.Random(); |
374 | 414 | ||
375 | // Asset type has to be Unknown here rather than Folder, otherwise the created folder can't be | ||
376 | // deleted once the client has relogged. | ||
377 | // The root folder appears to be labelled AssetType.Folder (shows up as "Category" in the client) | ||
378 | // even though there is a AssetType.RootCategory | ||
379 | destFolder | 415 | destFolder |
380 | = new InventoryFolderBase( | 416 | = new InventoryFolderBase( |
381 | newFolderId, newFolderName, m_userInfo.PrincipalID, | 417 | newFolderId, newFolderName, m_userInfo.PrincipalID, |
382 | (short)AssetType.Unknown, destFolder.ID, 1); | 418 | (short)FolderType.None, destFolder.ID, 1); |
383 | m_scene.InventoryService.AddFolder(destFolder); | 419 | m_InventoryService.AddFolder(destFolder); |
384 | 420 | ||
385 | // Record that we have now created this folder | 421 | // Record that we have now created this folder |
386 | iarPathExisting += rawDirsToCreate[i] + "/"; | 422 | iarPathExisting += rawDirsToCreate[i] + "/"; |
@@ -406,7 +442,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver | |||
406 | // Don't use the item ID that's in the file | 442 | // Don't use the item ID that's in the file |
407 | item.ID = UUID.Random(); | 443 | item.ID = UUID.Random(); |
408 | 444 | ||
409 | UUID ospResolvedId = OspResolver.ResolveOspa(item.CreatorId, m_scene.UserAccountService); | 445 | UUID ospResolvedId = OspResolver.ResolveOspa(item.CreatorId, m_UserAccountService); |
410 | if (UUID.Zero != ospResolvedId) // The user exists in this grid | 446 | if (UUID.Zero != ospResolvedId) // The user exists in this grid |
411 | { | 447 | { |
412 | // m_log.DebugFormat("[INVENTORY ARCHIVER]: Found creator {0} via OSPA resolution", ospResolvedId); | 448 | // m_log.DebugFormat("[INVENTORY ARCHIVER]: Found creator {0} via OSPA resolution", ospResolvedId); |
@@ -418,7 +454,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver | |||
418 | item.CreatorId = ospResolvedId.ToString(); | 454 | item.CreatorId = ospResolvedId.ToString(); |
419 | item.CreatorData = string.Empty; | 455 | item.CreatorData = string.Empty; |
420 | } | 456 | } |
421 | else if (item.CreatorData == null || item.CreatorData == String.Empty) | 457 | else if (string.IsNullOrEmpty(item.CreatorData)) |
422 | { | 458 | { |
423 | item.CreatorId = m_userInfo.PrincipalID.ToString(); | 459 | item.CreatorId = m_userInfo.PrincipalID.ToString(); |
424 | // item.CreatorIdAsUuid = new UUID(item.CreatorId); | 460 | // item.CreatorIdAsUuid = new UUID(item.CreatorId); |
@@ -436,7 +472,8 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver | |||
436 | // relying on native tar tools. | 472 | // relying on native tar tools. |
437 | m_creatorIdForAssetId[item.AssetID] = item.CreatorIdAsUuid; | 473 | m_creatorIdForAssetId[item.AssetID] = item.CreatorIdAsUuid; |
438 | 474 | ||
439 | m_scene.AddInventoryItem(item); | 475 | if (!m_InventoryService.AddItem(item)) |
476 | m_log.WarnFormat("[INVENTORY ARCHIVER]: Unable to save item {0} in folder {1}", item.Name, item.Folder); | ||
440 | 477 | ||
441 | return item; | 478 | return item; |
442 | } | 479 | } |
@@ -479,52 +516,24 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver | |||
479 | { | 516 | { |
480 | if (m_creatorIdForAssetId.ContainsKey(assetId)) | 517 | if (m_creatorIdForAssetId.ContainsKey(assetId)) |
481 | { | 518 | { |
482 | string xmlData = Utils.BytesToString(data); | 519 | data = SceneObjectSerializer.ModifySerializedObject(assetId, data, |
483 | List<SceneObjectGroup> sceneObjects = new List<SceneObjectGroup>(); | 520 | sog => { |
521 | bool modified = false; | ||
522 | |||
523 | foreach (SceneObjectPart sop in sog.Parts) | ||
524 | { | ||
525 | if (string.IsNullOrEmpty(sop.CreatorData)) | ||
526 | { | ||
527 | sop.CreatorID = m_creatorIdForAssetId[assetId]; | ||
528 | modified = true; | ||
529 | } | ||
530 | } | ||
531 | |||
532 | return modified; | ||
533 | }); | ||
484 | 534 | ||
485 | CoalescedSceneObjects coa = null; | 535 | if (data == null) |
486 | if (CoalescedSceneObjectsSerializer.TryFromXml(xmlData, out coa)) | 536 | return false; |
487 | { | ||
488 | // m_log.DebugFormat( | ||
489 | // "[INVENTORY ARCHIVER]: Loaded coalescence {0} has {1} objects", assetId, coa.Count); | ||
490 | |||
491 | if (coa.Objects.Count == 0) | ||
492 | { | ||
493 | m_log.WarnFormat( | ||
494 | "[INVENTORY ARCHIVE READ REQUEST]: Aborting load of coalesced object from asset {0} as it has zero loaded components", | ||
495 | assetId); | ||
496 | return false; | ||
497 | } | ||
498 | |||
499 | sceneObjects.AddRange(coa.Objects); | ||
500 | } | ||
501 | else | ||
502 | { | ||
503 | SceneObjectGroup deserializedObject = SceneObjectSerializer.FromOriginalXmlFormat(xmlData); | ||
504 | |||
505 | if (deserializedObject != null) | ||
506 | { | ||
507 | sceneObjects.Add(deserializedObject); | ||
508 | } | ||
509 | else | ||
510 | { | ||
511 | m_log.WarnFormat( | ||
512 | "[INVENTORY ARCHIVE READ REQUEST]: Aborting load of object from asset {0} as deserialization failed", | ||
513 | assetId); | ||
514 | |||
515 | return false; | ||
516 | } | ||
517 | } | ||
518 | |||
519 | foreach (SceneObjectGroup sog in sceneObjects) | ||
520 | foreach (SceneObjectPart sop in sog.Parts) | ||
521 | if (sop.CreatorData == null || sop.CreatorData == "") | ||
522 | sop.CreatorID = m_creatorIdForAssetId[assetId]; | ||
523 | |||
524 | if (coa != null) | ||
525 | data = Utils.StringToBytes(CoalescedSceneObjectsSerializer.ToXml(coa)); | ||
526 | else | ||
527 | data = Utils.StringToBytes(SceneObjectSerializer.ToOriginalXmlFormat(sceneObjects[0])); | ||
528 | } | 537 | } |
529 | } | 538 | } |
530 | 539 | ||
@@ -533,7 +542,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver | |||
533 | AssetBase asset = new AssetBase(assetId, "From IAR", assetType, UUID.Zero.ToString()); | 542 | AssetBase asset = new AssetBase(assetId, "From IAR", assetType, UUID.Zero.ToString()); |
534 | asset.Data = data; | 543 | asset.Data = data; |
535 | 544 | ||
536 | m_scene.AssetService.Store(asset); | 545 | m_AssetService.Store(asset); |
537 | 546 | ||
538 | return true; | 547 | return true; |
539 | } | 548 | } |
@@ -546,7 +555,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver | |||
546 | return false; | 555 | return false; |
547 | } | 556 | } |
548 | } | 557 | } |
549 | 558 | ||
550 | /// <summary> | 559 | /// <summary> |
551 | /// Load control file | 560 | /// Load control file |
552 | /// </summary> | 561 | /// </summary> |
@@ -652,4 +661,4 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver | |||
652 | m_assetsLoaded = true; | 661 | m_assetsLoaded = true; |
653 | } | 662 | } |
654 | } | 663 | } |
655 | } \ No newline at end of file | 664 | } |
diff --git a/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/InventoryArchiveUtils.cs b/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/InventoryArchiveUtils.cs index 0d90a15..dbaf2aa 100644 --- a/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/InventoryArchiveUtils.cs +++ b/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/InventoryArchiveUtils.cs | |||
@@ -52,13 +52,82 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver | |||
52 | /// <summary> | 52 | /// <summary> |
53 | /// Find a folder given a PATH_DELIMITER delimited path starting from a user's root folder | 53 | /// Find a folder given a PATH_DELIMITER delimited path starting from a user's root folder |
54 | /// </summary> | 54 | /// </summary> |
55 | /// <remarks> | ||
56 | /// This method does not handle paths that contain multiple delimitors | ||
57 | /// | ||
58 | /// FIXME: We have no way of distinguishing folders with the same path | ||
55 | /// | 59 | /// |
60 | /// FIXME: Delimitors which occur in names themselves are not currently escapable. | ||
61 | /// </remarks> | ||
62 | /// <param name="inventoryService"> | ||
63 | /// Inventory service to query | ||
64 | /// </param> | ||
65 | /// <param name="userId"> | ||
66 | /// User id to search | ||
67 | /// </param> | ||
68 | /// <param name="path"> | ||
69 | /// The path to the required folder. | ||
70 | /// It this is empty or consists only of the PATH_DELIMTER then this folder itself is returned. | ||
71 | /// </param> | ||
72 | /// <returns>The folder found. Please note that if there are multiple folders with the same name then an | ||
73 | /// unspecified one will be returned. If no such folder eixsts then null is returned</returns> | ||
74 | public static InventoryFolderBase FindFolderByPath( | ||
75 | IInventoryService inventoryService, UUID userId, string path) | ||
76 | { | ||
77 | List<InventoryFolderBase> folders = FindFoldersByPath(inventoryService, userId, path); | ||
78 | |||
79 | if (folders.Count == 0) | ||
80 | return null; | ||
81 | else | ||
82 | return folders[0]; | ||
83 | } | ||
84 | |||
85 | /// <summary> | ||
86 | /// Find a folder given a PATH_DELIMITER delimited path starting from a given folder | ||
87 | /// </summary> | ||
88 | /// <remarks> | ||
56 | /// This method does not handle paths that contain multiple delimitors | 89 | /// This method does not handle paths that contain multiple delimitors |
57 | /// | 90 | /// |
58 | /// FIXME: We have no way of distinguishing folders with the same path | 91 | /// FIXME: We have no way of distinguishing folders with the same path |
59 | /// | 92 | /// |
60 | /// FIXME: Delimitors which occur in names themselves are not currently escapable. | 93 | /// FIXME: Delimitors which occur in names themselves are not currently escapable. |
94 | /// </remarks> | ||
95 | /// <param name="inventoryService"> | ||
96 | /// Inventory service to query | ||
97 | /// </param> | ||
98 | /// <param name="startFolder"> | ||
99 | /// The folder from which the path starts | ||
100 | /// </param> | ||
101 | /// <param name="path"> | ||
102 | /// The path to the required folder. | ||
103 | /// It this is empty or consists only of the PATH_DELIMTER then this folder itself is returned. | ||
104 | /// </param> | ||
105 | /// <returns>The folder found. Please note that if there are multiple folders with the same name then an | ||
106 | /// unspecified one will be returned. If no such folder eixsts then null is returned</returns> | ||
107 | public static InventoryFolderBase FindFolderByPath( | ||
108 | IInventoryService inventoryService, InventoryFolderBase startFolder, string path) | ||
109 | { | ||
110 | if (null == startFolder) | ||
111 | return null; | ||
112 | |||
113 | List<InventoryFolderBase> folders = FindFoldersByPath(inventoryService, startFolder, path); | ||
114 | |||
115 | if (folders.Count == 0) | ||
116 | return null; | ||
117 | else | ||
118 | return folders[0]; | ||
119 | } | ||
120 | |||
121 | /// <summary> | ||
122 | /// Find a set of folders given a PATH_DELIMITER delimited path starting from a user's root folder | ||
123 | /// </summary> | ||
124 | /// <remarks> | ||
125 | /// This method does not handle paths that contain multiple delimitors | ||
126 | /// | ||
127 | /// FIXME: We have no way of distinguishing folders with the same path | ||
61 | /// | 128 | /// |
129 | /// FIXME: Delimitors which occur in names themselves are not currently escapable. | ||
130 | /// </remarks> | ||
62 | /// <param name="inventoryService"> | 131 | /// <param name="inventoryService"> |
63 | /// Inventory service to query | 132 | /// Inventory service to query |
64 | /// </param> | 133 | /// </param> |
@@ -70,7 +139,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver | |||
70 | /// It this is empty or consists only of the PATH_DELIMTER then this folder itself is returned. | 139 | /// It this is empty or consists only of the PATH_DELIMTER then this folder itself is returned. |
71 | /// </param> | 140 | /// </param> |
72 | /// <returns>An empty list if the folder is not found, otherwise a list of all folders that match the name</returns> | 141 | /// <returns>An empty list if the folder is not found, otherwise a list of all folders that match the name</returns> |
73 | public static List<InventoryFolderBase> FindFolderByPath( | 142 | public static List<InventoryFolderBase> FindFoldersByPath( |
74 | IInventoryService inventoryService, UUID userId, string path) | 143 | IInventoryService inventoryService, UUID userId, string path) |
75 | { | 144 | { |
76 | InventoryFolderBase rootFolder = inventoryService.GetRootFolder(userId); | 145 | InventoryFolderBase rootFolder = inventoryService.GetRootFolder(userId); |
@@ -78,19 +147,19 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver | |||
78 | if (null == rootFolder) | 147 | if (null == rootFolder) |
79 | return new List<InventoryFolderBase>(); | 148 | return new List<InventoryFolderBase>(); |
80 | 149 | ||
81 | return FindFolderByPath(inventoryService, rootFolder, path); | 150 | return FindFoldersByPath(inventoryService, rootFolder, path); |
82 | } | 151 | } |
83 | 152 | ||
84 | /// <summary> | 153 | /// <summary> |
85 | /// Find a folder given a PATH_DELIMITER delimited path starting from this folder | 154 | /// Find a set of folders given a PATH_DELIMITER delimited path starting from this folder |
86 | /// </summary> | 155 | /// </summary> |
87 | /// | 156 | /// <remarks> |
88 | /// This method does not handle paths that contain multiple delimitors | 157 | /// This method does not handle paths that contain multiple delimitors |
89 | /// | 158 | /// |
90 | /// FIXME: We have no way of distinguishing folders with the same path. | 159 | /// FIXME: We have no way of distinguishing folders with the same path. |
91 | /// | 160 | /// |
92 | /// FIXME: Delimitors which occur in names themselves are not currently escapable. | 161 | /// FIXME: Delimitors which occur in names themselves are not currently escapable. |
93 | /// | 162 | /// </remarks> |
94 | /// <param name="inventoryService"> | 163 | /// <param name="inventoryService"> |
95 | /// Inventory service to query | 164 | /// Inventory service to query |
96 | /// </param> | 165 | /// </param> |
@@ -102,7 +171,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver | |||
102 | /// It this is empty or consists only of the PATH_DELIMTER then this folder itself is returned. | 171 | /// It this is empty or consists only of the PATH_DELIMTER then this folder itself is returned. |
103 | /// </param> | 172 | /// </param> |
104 | /// <returns>An empty list if the folder is not found, otherwise a list of all folders that match the name</returns> | 173 | /// <returns>An empty list if the folder is not found, otherwise a list of all folders that match the name</returns> |
105 | public static List<InventoryFolderBase> FindFolderByPath( | 174 | public static List<InventoryFolderBase> FindFoldersByPath( |
106 | IInventoryService inventoryService, InventoryFolderBase startFolder, string path) | 175 | IInventoryService inventoryService, InventoryFolderBase startFolder, string path) |
107 | { | 176 | { |
108 | List<InventoryFolderBase> foundFolders = new List<InventoryFolderBase>(); | 177 | List<InventoryFolderBase> foundFolders = new List<InventoryFolderBase>(); |
@@ -133,12 +202,15 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver | |||
133 | 202 | ||
134 | InventoryCollection contents = inventoryService.GetFolderContent(startFolder.Owner, startFolder.ID); | 203 | InventoryCollection contents = inventoryService.GetFolderContent(startFolder.Owner, startFolder.ID); |
135 | 204 | ||
205 | // m_log.DebugFormat( | ||
206 | // "Found {0} folders in {1} for {2}", contents.Folders.Count, startFolder.Name, startFolder.Owner); | ||
207 | |||
136 | foreach (InventoryFolderBase folder in contents.Folders) | 208 | foreach (InventoryFolderBase folder in contents.Folders) |
137 | { | 209 | { |
138 | if (folder.Name == components[0]) | 210 | if (folder.Name == components[0]) |
139 | { | 211 | { |
140 | if (components.Length > 1) | 212 | if (components.Length > 1) |
141 | foundFolders.AddRange(FindFolderByPath(inventoryService, folder, components[1])); | 213 | foundFolders.AddRange(FindFoldersByPath(inventoryService, folder, components[1])); |
142 | else | 214 | else |
143 | foundFolders.Add(folder); | 215 | foundFolders.Add(folder); |
144 | } | 216 | } |
diff --git a/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/InventoryArchiveWriteRequest.cs b/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/InventoryArchiveWriteRequest.cs index d0e88f6..f002ad7 100644 --- a/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/InventoryArchiveWriteRequest.cs +++ b/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/InventoryArchiveWriteRequest.cs | |||
@@ -34,6 +34,7 @@ using System.Xml; | |||
34 | using log4net; | 34 | using log4net; |
35 | using OpenMetaverse; | 35 | using OpenMetaverse; |
36 | using OpenSim.Framework; | 36 | using OpenSim.Framework; |
37 | using OpenSim.Framework.Monitoring; | ||
37 | using OpenSim.Framework.Serialization; | 38 | using OpenSim.Framework.Serialization; |
38 | using OpenSim.Framework.Serialization.External; | 39 | using OpenSim.Framework.Serialization.External; |
39 | using OpenSim.Region.CoreModules.World.Archiver; | 40 | using OpenSim.Region.CoreModules.World.Archiver; |
@@ -42,6 +43,8 @@ using OpenSim.Services.Interfaces; | |||
42 | using Ionic.Zlib; | 43 | using Ionic.Zlib; |
43 | using GZipStream = Ionic.Zlib.GZipStream; | 44 | using GZipStream = Ionic.Zlib.GZipStream; |
44 | using CompressionMode = Ionic.Zlib.CompressionMode; | 45 | using CompressionMode = Ionic.Zlib.CompressionMode; |
46 | using CompressionLevel = Ionic.Zlib.CompressionLevel; | ||
47 | using PermissionMask = OpenSim.Framework.PermissionMask; | ||
45 | 48 | ||
46 | namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver | 49 | namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver |
47 | { | 50 | { |
@@ -54,6 +57,22 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver | |||
54 | /// </summary> | 57 | /// </summary> |
55 | public bool SaveAssets { get; set; } | 58 | public bool SaveAssets { get; set; } |
56 | 59 | ||
60 | /// <summary> | ||
61 | /// Determines which items will be included in the archive, according to their permissions. | ||
62 | /// Default is null, meaning no permission checks. | ||
63 | /// </summary> | ||
64 | public string FilterContent { get; set; } | ||
65 | |||
66 | /// <summary> | ||
67 | /// Counter for inventory items saved to archive for passing to compltion event | ||
68 | /// </summary> | ||
69 | public int CountItems { get; set; } | ||
70 | |||
71 | /// <summary> | ||
72 | /// Counter for inventory items skipped due to permission filter option for passing to compltion event | ||
73 | /// </summary> | ||
74 | public int CountFiltered { get; set; } | ||
75 | |||
57 | /// <value> | 76 | /// <value> |
58 | /// Used to select all inventory nodes in a folder but not the folder itself | 77 | /// Used to select all inventory nodes in a folder but not the folder itself |
59 | /// </value> | 78 | /// </value> |
@@ -73,12 +92,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver | |||
73 | /// <value> | 92 | /// <value> |
74 | /// ID of this request | 93 | /// ID of this request |
75 | /// </value> | 94 | /// </value> |
76 | protected Guid m_id; | 95 | protected UUID m_id; |
77 | |||
78 | /// <value> | ||
79 | /// Used to collect the uuids of the assets that we need to save into the archive | ||
80 | /// </value> | ||
81 | protected Dictionary<UUID, AssetType> m_assetUuids = new Dictionary<UUID, AssetType>(); | ||
82 | 96 | ||
83 | /// <value> | 97 | /// <value> |
84 | /// Used to collect the uuids of the users that we need to save into the archive | 98 | /// Used to collect the uuids of the users that we need to save into the archive |
@@ -94,7 +108,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver | |||
94 | /// Constructor | 108 | /// Constructor |
95 | /// </summary> | 109 | /// </summary> |
96 | public InventoryArchiveWriteRequest( | 110 | public InventoryArchiveWriteRequest( |
97 | Guid id, InventoryArchiverModule module, Scene scene, | 111 | UUID id, InventoryArchiverModule module, Scene scene, |
98 | UserAccount userInfo, string invPath, string savePath) | 112 | UserAccount userInfo, string invPath, string savePath) |
99 | : this( | 113 | : this( |
100 | id, | 114 | id, |
@@ -110,7 +124,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver | |||
110 | /// Constructor | 124 | /// Constructor |
111 | /// </summary> | 125 | /// </summary> |
112 | public InventoryArchiveWriteRequest( | 126 | public InventoryArchiveWriteRequest( |
113 | Guid id, InventoryArchiverModule module, Scene scene, | 127 | UUID id, InventoryArchiverModule module, Scene scene, |
114 | UserAccount userInfo, string invPath, Stream saveStream) | 128 | UserAccount userInfo, string invPath, Stream saveStream) |
115 | { | 129 | { |
116 | m_id = id; | 130 | m_id = id; |
@@ -122,9 +136,10 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver | |||
122 | m_assetGatherer = new UuidGatherer(m_scene.AssetService); | 136 | m_assetGatherer = new UuidGatherer(m_scene.AssetService); |
123 | 137 | ||
124 | SaveAssets = true; | 138 | SaveAssets = true; |
139 | FilterContent = null; | ||
125 | } | 140 | } |
126 | 141 | ||
127 | protected void ReceivedAllAssets(ICollection<UUID> assetsFoundUuids, ICollection<UUID> assetsNotFoundUuids) | 142 | protected void ReceivedAllAssets(ICollection<UUID> assetsFoundUuids, ICollection<UUID> assetsNotFoundUuids, bool timedOut) |
128 | { | 143 | { |
129 | Exception reportedException = null; | 144 | Exception reportedException = null; |
130 | bool succeeded = true; | 145 | bool succeeded = true; |
@@ -143,8 +158,14 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver | |||
143 | m_saveStream.Close(); | 158 | m_saveStream.Close(); |
144 | } | 159 | } |
145 | 160 | ||
161 | if (timedOut) | ||
162 | { | ||
163 | succeeded = false; | ||
164 | reportedException = new Exception("Loading assets timed out"); | ||
165 | } | ||
166 | |||
146 | m_module.TriggerInventoryArchiveSaved( | 167 | m_module.TriggerInventoryArchiveSaved( |
147 | m_id, succeeded, m_userInfo, m_invPath, m_saveStream, reportedException); | 168 | m_id, succeeded, m_userInfo, m_invPath, m_saveStream, reportedException, CountItems, CountFiltered); |
148 | } | 169 | } |
149 | 170 | ||
150 | protected void SaveInvItem(InventoryItemBase inventoryItem, string path, Dictionary<string, object> options, IUserAccountService userAccountService) | 171 | protected void SaveInvItem(InventoryItemBase inventoryItem, string path, Dictionary<string, object> options, IUserAccountService userAccountService) |
@@ -160,10 +181,26 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver | |||
160 | "[INVENTORY ARCHIVER]: Skipping inventory item {0} {1} at {2}", | 181 | "[INVENTORY ARCHIVER]: Skipping inventory item {0} {1} at {2}", |
161 | inventoryItem.Name, inventoryItem.ID, path); | 182 | inventoryItem.Name, inventoryItem.ID, path); |
162 | } | 183 | } |
184 | |||
185 | CountFiltered++; | ||
186 | |||
163 | return; | 187 | return; |
164 | } | 188 | } |
165 | } | 189 | } |
166 | 190 | ||
191 | // Check For Permissions Filter Flags | ||
192 | if (!CanUserArchiveObject(m_userInfo.PrincipalID, inventoryItem)) | ||
193 | { | ||
194 | m_log.InfoFormat( | ||
195 | "[INVENTORY ARCHIVER]: Insufficient permissions, skipping inventory item {0} {1} at {2}", | ||
196 | inventoryItem.Name, inventoryItem.ID, path); | ||
197 | |||
198 | // Count Items Excluded | ||
199 | CountFiltered++; | ||
200 | |||
201 | return; | ||
202 | } | ||
203 | |||
167 | if (options.ContainsKey("verbose")) | 204 | if (options.ContainsKey("verbose")) |
168 | m_log.InfoFormat( | 205 | m_log.InfoFormat( |
169 | "[INVENTORY ARCHIVER]: Saving item {0} {1} (asset UUID {2})", | 206 | "[INVENTORY ARCHIVER]: Saving item {0} {1} (asset UUID {2})", |
@@ -179,9 +216,12 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver | |||
179 | 216 | ||
180 | AssetType itemAssetType = (AssetType)inventoryItem.AssetType; | 217 | AssetType itemAssetType = (AssetType)inventoryItem.AssetType; |
181 | 218 | ||
219 | // Count inventory items (different to asset count) | ||
220 | CountItems++; | ||
221 | |||
182 | // Don't chase down link asset items as they actually point to their target item IDs rather than an asset | 222 | // Don't chase down link asset items as they actually point to their target item IDs rather than an asset |
183 | if (SaveAssets && itemAssetType != AssetType.Link && itemAssetType != AssetType.LinkFolder) | 223 | if (SaveAssets && itemAssetType != AssetType.Link && itemAssetType != AssetType.LinkFolder) |
184 | m_assetGatherer.GatherAssetUuids(inventoryItem.AssetID, (AssetType)inventoryItem.AssetType, m_assetUuids); | 224 | m_assetGatherer.AddForInspection(inventoryItem.AssetID); |
185 | } | 225 | } |
186 | 226 | ||
187 | /// <summary> | 227 | /// <summary> |
@@ -237,6 +277,35 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver | |||
237 | } | 277 | } |
238 | 278 | ||
239 | /// <summary> | 279 | /// <summary> |
280 | /// Checks whether the user has permission to export an inventory item to an IAR. | ||
281 | /// </summary> | ||
282 | /// <param name="UserID">The user</param> | ||
283 | /// <param name="InvItem">The inventory item</param> | ||
284 | /// <returns>Whether the user is allowed to export the object to an IAR</returns> | ||
285 | private bool CanUserArchiveObject(UUID UserID, InventoryItemBase InvItem) | ||
286 | { | ||
287 | if (FilterContent == null) | ||
288 | return true;// Default To Allow Export | ||
289 | |||
290 | bool permitted = true; | ||
291 | |||
292 | bool canCopy = (InvItem.CurrentPermissions & (uint)PermissionMask.Copy) != 0; | ||
293 | bool canTransfer = (InvItem.CurrentPermissions & (uint)PermissionMask.Transfer) != 0; | ||
294 | bool canMod = (InvItem.CurrentPermissions & (uint)PermissionMask.Modify) != 0; | ||
295 | |||
296 | if (FilterContent.Contains("C") && !canCopy) | ||
297 | permitted = false; | ||
298 | |||
299 | if (FilterContent.Contains("T") && !canTransfer) | ||
300 | permitted = false; | ||
301 | |||
302 | if (FilterContent.Contains("M") && !canMod) | ||
303 | permitted = false; | ||
304 | |||
305 | return permitted; | ||
306 | } | ||
307 | |||
308 | /// <summary> | ||
240 | /// Execute the inventory write request | 309 | /// Execute the inventory write request |
241 | /// </summary> | 310 | /// </summary> |
242 | public void Execute(Dictionary<string, object> options, IUserAccountService userAccountService) | 311 | public void Execute(Dictionary<string, object> options, IUserAccountService userAccountService) |
@@ -244,6 +313,14 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver | |||
244 | if (options.ContainsKey("noassets") && (bool)options["noassets"]) | 313 | if (options.ContainsKey("noassets") && (bool)options["noassets"]) |
245 | SaveAssets = false; | 314 | SaveAssets = false; |
246 | 315 | ||
316 | // Set Permission filter if flag is set | ||
317 | if (options.ContainsKey("checkPermissions")) | ||
318 | { | ||
319 | Object temp; | ||
320 | if (options.TryGetValue("checkPermissions", out temp)) | ||
321 | FilterContent = temp.ToString().ToUpper(); | ||
322 | } | ||
323 | |||
247 | try | 324 | try |
248 | { | 325 | { |
249 | InventoryFolderBase inventoryFolder = null; | 326 | InventoryFolderBase inventoryFolder = null; |
@@ -266,6 +343,12 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver | |||
266 | saveFolderContentsOnly = true; | 343 | saveFolderContentsOnly = true; |
267 | maxComponentIndex--; | 344 | maxComponentIndex--; |
268 | } | 345 | } |
346 | else if (maxComponentIndex == -1) | ||
347 | { | ||
348 | // If the user has just specified "/", then don't save the root "My Inventory" folder. This is | ||
349 | // more intuitive then requiring the user to specify "/*" for this. | ||
350 | saveFolderContentsOnly = true; | ||
351 | } | ||
269 | 352 | ||
270 | m_invPath = String.Empty; | 353 | m_invPath = String.Empty; |
271 | for (int i = 0; i <= maxComponentIndex; i++) | 354 | for (int i = 0; i <= maxComponentIndex; i++) |
@@ -283,7 +366,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver | |||
283 | { | 366 | { |
284 | m_invPath = m_invPath.Remove(m_invPath.LastIndexOf(InventoryFolderImpl.PATH_DELIMITER)); | 367 | m_invPath = m_invPath.Remove(m_invPath.LastIndexOf(InventoryFolderImpl.PATH_DELIMITER)); |
285 | List<InventoryFolderBase> candidateFolders | 368 | List<InventoryFolderBase> candidateFolders |
286 | = InventoryArchiveUtils.FindFolderByPath(m_scene.InventoryService, rootFolder, m_invPath); | 369 | = InventoryArchiveUtils.FindFoldersByPath(m_scene.InventoryService, rootFolder, m_invPath); |
287 | if (candidateFolders.Count > 0) | 370 | if (candidateFolders.Count > 0) |
288 | inventoryFolder = candidateFolders[0]; | 371 | inventoryFolder = candidateFolders[0]; |
289 | } | 372 | } |
@@ -297,7 +380,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver | |||
297 | // We couldn't find the path indicated | 380 | // We couldn't find the path indicated |
298 | string errorMessage = string.Format("Aborted save. Could not find inventory path {0}", m_invPath); | 381 | string errorMessage = string.Format("Aborted save. Could not find inventory path {0}", m_invPath); |
299 | Exception e = new InventoryArchiverException(errorMessage); | 382 | Exception e = new InventoryArchiverException(errorMessage); |
300 | m_module.TriggerInventoryArchiveSaved(m_id, false, m_userInfo, m_invPath, m_saveStream, e); | 383 | m_module.TriggerInventoryArchiveSaved(m_id, false, m_userInfo, m_invPath, m_saveStream, e, 0, 0); |
301 | throw e; | 384 | throw e; |
302 | } | 385 | } |
303 | 386 | ||
@@ -335,22 +418,25 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver | |||
335 | 418 | ||
336 | if (SaveAssets) | 419 | if (SaveAssets) |
337 | { | 420 | { |
338 | m_log.DebugFormat("[INVENTORY ARCHIVER]: Saving {0} assets for items", m_assetUuids.Count); | 421 | m_assetGatherer.GatherAll(); |
422 | |||
423 | m_log.DebugFormat( | ||
424 | "[INVENTORY ARCHIVER]: Saving {0} assets for items", m_assetGatherer.GatheredUuids.Count); | ||
339 | 425 | ||
340 | AssetsRequest ar | 426 | AssetsRequest ar |
341 | = new AssetsRequest( | 427 | = new AssetsRequest( |
342 | new AssetsArchiver(m_archiveWriter), | 428 | new AssetsArchiver(m_archiveWriter), |
343 | m_assetUuids, m_scene.AssetService, | 429 | m_assetGatherer.GatheredUuids, m_scene.AssetService, |
344 | m_scene.UserAccountService, m_scene.RegionInfo.ScopeID, | 430 | m_scene.UserAccountService, m_scene.RegionInfo.ScopeID, |
345 | options, ReceivedAllAssets); | 431 | options, ReceivedAllAssets); |
346 | 432 | ||
347 | Util.FireAndForget(o => ar.Execute()); | 433 | WorkManager.RunInThread(o => ar.Execute(), null, string.Format("AssetsRequest ({0})", m_scene.Name)); |
348 | } | 434 | } |
349 | else | 435 | else |
350 | { | 436 | { |
351 | m_log.DebugFormat("[INVENTORY ARCHIVER]: Not saving assets since --noassets was specified"); | 437 | m_log.DebugFormat("[INVENTORY ARCHIVER]: Not saving assets since --noassets was specified"); |
352 | 438 | ||
353 | ReceivedAllAssets(new List<UUID>(), new List<UUID>()); | 439 | ReceivedAllAssets(new List<UUID>(), new List<UUID>(), false); |
354 | } | 440 | } |
355 | } | 441 | } |
356 | catch (Exception) | 442 | catch (Exception) |
diff --git a/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/InventoryArchiverModule.cs b/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/InventoryArchiverModule.cs index 849449b..8847414 100644 --- a/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/InventoryArchiverModule.cs +++ b/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/InventoryArchiverModule.cs | |||
@@ -34,7 +34,6 @@ using NDesk.Options; | |||
34 | using Nini.Config; | 34 | using Nini.Config; |
35 | using OpenMetaverse; | 35 | using OpenMetaverse; |
36 | using OpenSim.Framework; | 36 | using OpenSim.Framework; |
37 | using OpenSim.Framework.Communications; | ||
38 | using OpenSim.Framework.Console; | 37 | using OpenSim.Framework.Console; |
39 | using OpenSim.Region.Framework.Interfaces; | 38 | using OpenSim.Region.Framework.Interfaces; |
40 | using OpenSim.Region.Framework.Scenes; | 39 | using OpenSim.Region.Framework.Scenes; |
@@ -57,6 +56,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver | |||
57 | // public bool DisablePresenceChecks { get; set; } | 56 | // public bool DisablePresenceChecks { get; set; } |
58 | 57 | ||
59 | public event InventoryArchiveSaved OnInventoryArchiveSaved; | 58 | public event InventoryArchiveSaved OnInventoryArchiveSaved; |
59 | public event InventoryArchiveLoaded OnInventoryArchiveLoaded; | ||
60 | 60 | ||
61 | /// <summary> | 61 | /// <summary> |
62 | /// The file to load and save inventory if no filename has been specified | 62 | /// The file to load and save inventory if no filename has been specified |
@@ -64,9 +64,9 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver | |||
64 | protected const string DEFAULT_INV_BACKUP_FILENAME = "user-inventory.iar"; | 64 | protected const string DEFAULT_INV_BACKUP_FILENAME = "user-inventory.iar"; |
65 | 65 | ||
66 | /// <value> | 66 | /// <value> |
67 | /// Pending save completions initiated from the console | 67 | /// Pending save and load completions initiated from the console |
68 | /// </value> | 68 | /// </value> |
69 | protected List<Guid> m_pendingConsoleSaves = new List<Guid>(); | 69 | protected List<UUID> m_pendingConsoleTasks = new List<UUID>(); |
70 | 70 | ||
71 | /// <value> | 71 | /// <value> |
72 | /// All scenes that this module knows about | 72 | /// All scenes that this module knows about |
@@ -111,6 +111,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver | |||
111 | { | 111 | { |
112 | scene.RegisterModuleInterface<IInventoryArchiverModule>(this); | 112 | scene.RegisterModuleInterface<IInventoryArchiverModule>(this); |
113 | OnInventoryArchiveSaved += SaveInvConsoleCommandCompleted; | 113 | OnInventoryArchiveSaved += SaveInvConsoleCommandCompleted; |
114 | OnInventoryArchiveLoaded += LoadInvConsoleCommandCompleted; | ||
114 | 115 | ||
115 | scene.AddCommand( | 116 | scene.AddCommand( |
116 | "Archiving", this, "load iar", | 117 | "Archiving", this, "load iar", |
@@ -139,7 +140,9 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver | |||
139 | + "-e|--exclude=<name/uuid> don't save the inventory item in archive" + Environment.NewLine | 140 | + "-e|--exclude=<name/uuid> don't save the inventory item in archive" + Environment.NewLine |
140 | + "-f|--excludefolder=<folder/uuid> don't save contents of the folder in archive" + Environment.NewLine | 141 | + "-f|--excludefolder=<folder/uuid> don't save contents of the folder in archive" + Environment.NewLine |
141 | + "-v|--verbose extra debug messages.\n" | 142 | + "-v|--verbose extra debug messages.\n" |
142 | + "--noassets stops assets being saved to the IAR.", | 143 | + "--noassets stops assets being saved to the IAR." |
144 | + "--perm=<permissions> stops items with insufficient permissions from being saved to the IAR.\n" | ||
145 | + " <permissions> can contain one or more of these characters: \"C\" = Copy, \"T\" = Transfer, \"M\" = Modify.\n", | ||
143 | HandleSaveInvConsoleCommand); | 146 | HandleSaveInvConsoleCommand); |
144 | 147 | ||
145 | m_aScene = scene; | 148 | m_aScene = scene; |
@@ -175,22 +178,34 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver | |||
175 | /// Trigger the inventory archive saved event. | 178 | /// Trigger the inventory archive saved event. |
176 | /// </summary> | 179 | /// </summary> |
177 | protected internal void TriggerInventoryArchiveSaved( | 180 | protected internal void TriggerInventoryArchiveSaved( |
178 | Guid id, bool succeeded, UserAccount userInfo, string invPath, Stream saveStream, | 181 | UUID id, bool succeeded, UserAccount userInfo, string invPath, Stream saveStream, |
179 | Exception reportedException) | 182 | Exception reportedException, int SaveCount, int FilterCount) |
180 | { | 183 | { |
181 | InventoryArchiveSaved handlerInventoryArchiveSaved = OnInventoryArchiveSaved; | 184 | InventoryArchiveSaved handlerInventoryArchiveSaved = OnInventoryArchiveSaved; |
182 | if (handlerInventoryArchiveSaved != null) | 185 | if (handlerInventoryArchiveSaved != null) |
183 | handlerInventoryArchiveSaved(id, succeeded, userInfo, invPath, saveStream, reportedException); | 186 | handlerInventoryArchiveSaved(id, succeeded, userInfo, invPath, saveStream, reportedException, SaveCount , FilterCount); |
187 | } | ||
188 | |||
189 | /// <summary> | ||
190 | /// Trigger the inventory archive loaded event. | ||
191 | /// </summary> | ||
192 | protected internal void TriggerInventoryArchiveLoaded( | ||
193 | UUID id, bool succeeded, UserAccount userInfo, string invPath, Stream loadStream, | ||
194 | Exception reportedException, int LoadCount) | ||
195 | { | ||
196 | InventoryArchiveLoaded handlerInventoryArchiveLoaded = OnInventoryArchiveLoaded; | ||
197 | if (handlerInventoryArchiveLoaded != null) | ||
198 | handlerInventoryArchiveLoaded(id, succeeded, userInfo, invPath, loadStream, reportedException, LoadCount); | ||
184 | } | 199 | } |
185 | 200 | ||
186 | public bool ArchiveInventory( | 201 | public bool ArchiveInventory( |
187 | Guid id, string firstName, string lastName, string invPath, string pass, Stream saveStream) | 202 | UUID id, string firstName, string lastName, string invPath, string pass, Stream saveStream) |
188 | { | 203 | { |
189 | return ArchiveInventory(id, firstName, lastName, invPath, pass, saveStream, new Dictionary<string, object>()); | 204 | return ArchiveInventory(id, firstName, lastName, invPath, pass, saveStream, new Dictionary<string, object>()); |
190 | } | 205 | } |
191 | 206 | ||
192 | public bool ArchiveInventory( | 207 | public bool ArchiveInventory( |
193 | Guid id, string firstName, string lastName, string invPath, string pass, Stream saveStream, | 208 | UUID id, string firstName, string lastName, string invPath, string pass, Stream saveStream, |
194 | Dictionary<string, object> options) | 209 | Dictionary<string, object> options) |
195 | { | 210 | { |
196 | if (m_scenes.Count > 0) | 211 | if (m_scenes.Count > 0) |
@@ -230,7 +245,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver | |||
230 | } | 245 | } |
231 | 246 | ||
232 | public bool ArchiveInventory( | 247 | public bool ArchiveInventory( |
233 | Guid id, string firstName, string lastName, string invPath, string pass, string savePath, | 248 | UUID id, string firstName, string lastName, string invPath, string pass, string savePath, |
234 | Dictionary<string, object> options) | 249 | Dictionary<string, object> options) |
235 | { | 250 | { |
236 | // if (!ConsoleUtil.CheckFileDoesNotExist(MainConsole.Instance, savePath)) | 251 | // if (!ConsoleUtil.CheckFileDoesNotExist(MainConsole.Instance, savePath)) |
@@ -272,13 +287,13 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver | |||
272 | return false; | 287 | return false; |
273 | } | 288 | } |
274 | 289 | ||
275 | public bool DearchiveInventory(string firstName, string lastName, string invPath, string pass, Stream loadStream) | 290 | public bool DearchiveInventory(UUID id, string firstName, string lastName, string invPath, string pass, Stream loadStream) |
276 | { | 291 | { |
277 | return DearchiveInventory(firstName, lastName, invPath, pass, loadStream, new Dictionary<string, object>()); | 292 | return DearchiveInventory(id, firstName, lastName, invPath, pass, loadStream, new Dictionary<string, object>()); |
278 | } | 293 | } |
279 | 294 | ||
280 | public bool DearchiveInventory( | 295 | public bool DearchiveInventory( |
281 | string firstName, string lastName, string invPath, string pass, Stream loadStream, | 296 | UUID id, string firstName, string lastName, string invPath, string pass, Stream loadStream, |
282 | Dictionary<string, object> options) | 297 | Dictionary<string, object> options) |
283 | { | 298 | { |
284 | if (m_scenes.Count > 0) | 299 | if (m_scenes.Count > 0) |
@@ -294,7 +309,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver | |||
294 | 309 | ||
295 | try | 310 | try |
296 | { | 311 | { |
297 | request = new InventoryArchiveReadRequest(m_aScene, userInfo, invPath, loadStream, merge); | 312 | request = new InventoryArchiveReadRequest(id, this, m_aScene.InventoryService, m_aScene.AssetService, m_aScene.UserAccountService, userInfo, invPath, loadStream, merge); |
298 | } | 313 | } |
299 | catch (EntryPointNotFoundException e) | 314 | catch (EntryPointNotFoundException e) |
300 | { | 315 | { |
@@ -326,7 +341,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver | |||
326 | } | 341 | } |
327 | 342 | ||
328 | public bool DearchiveInventory( | 343 | public bool DearchiveInventory( |
329 | string firstName, string lastName, string invPath, string pass, string loadPath, | 344 | UUID id, string firstName, string lastName, string invPath, string pass, string loadPath, |
330 | Dictionary<string, object> options) | 345 | Dictionary<string, object> options) |
331 | { | 346 | { |
332 | if (m_scenes.Count > 0) | 347 | if (m_scenes.Count > 0) |
@@ -342,7 +357,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver | |||
342 | 357 | ||
343 | try | 358 | try |
344 | { | 359 | { |
345 | request = new InventoryArchiveReadRequest(m_aScene, userInfo, invPath, loadPath, merge); | 360 | request = new InventoryArchiveReadRequest(id, this, m_aScene.InventoryService, m_aScene.AssetService, m_aScene.UserAccountService, userInfo, invPath, loadPath, merge); |
346 | } | 361 | } |
347 | catch (EntryPointNotFoundException e) | 362 | catch (EntryPointNotFoundException e) |
348 | { | 363 | { |
@@ -378,6 +393,8 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver | |||
378 | { | 393 | { |
379 | try | 394 | try |
380 | { | 395 | { |
396 | UUID id = UUID.Random(); | ||
397 | |||
381 | Dictionary<string, object> options = new Dictionary<string, object>(); | 398 | Dictionary<string, object> options = new Dictionary<string, object>(); |
382 | OptionSet optionSet = new OptionSet().Add("m|merge", delegate (string v) { options["merge"] = v != null; }); | 399 | OptionSet optionSet = new OptionSet().Add("m|merge", delegate (string v) { options["merge"] = v != null; }); |
383 | 400 | ||
@@ -400,10 +417,10 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver | |||
400 | "[INVENTORY ARCHIVER]: Loading archive {0} to inventory path {1} for {2} {3}", | 417 | "[INVENTORY ARCHIVER]: Loading archive {0} to inventory path {1} for {2} {3}", |
401 | loadPath, invPath, firstName, lastName); | 418 | loadPath, invPath, firstName, lastName); |
402 | 419 | ||
403 | if (DearchiveInventory(firstName, lastName, invPath, pass, loadPath, options)) | 420 | lock (m_pendingConsoleTasks) |
404 | m_log.InfoFormat( | 421 | m_pendingConsoleTasks.Add(id); |
405 | "[INVENTORY ARCHIVER]: Loaded archive {0} for {1} {2}", | 422 | |
406 | loadPath, firstName, lastName); | 423 | DearchiveInventory(id, firstName, lastName, invPath, pass, loadPath, options); |
407 | } | 424 | } |
408 | catch (InventoryArchiverException e) | 425 | catch (InventoryArchiverException e) |
409 | { | 426 | { |
@@ -417,7 +434,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver | |||
417 | /// <param name="cmdparams"></param> | 434 | /// <param name="cmdparams"></param> |
418 | protected void HandleSaveInvConsoleCommand(string module, string[] cmdparams) | 435 | protected void HandleSaveInvConsoleCommand(string module, string[] cmdparams) |
419 | { | 436 | { |
420 | Guid id = Guid.NewGuid(); | 437 | UUID id = UUID.Random(); |
421 | 438 | ||
422 | Dictionary<string, object> options = new Dictionary<string, object>(); | 439 | Dictionary<string, object> options = new Dictionary<string, object>(); |
423 | 440 | ||
@@ -439,6 +456,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver | |||
439 | options["excludefolders"] = new List<String>(); | 456 | options["excludefolders"] = new List<String>(); |
440 | ((List<String>)options["excludefolders"]).Add(v); | 457 | ((List<String>)options["excludefolders"]).Add(v); |
441 | }); | 458 | }); |
459 | ops.Add("perm=", delegate(string v) { options["checkPermissions"] = v; }); | ||
442 | 460 | ||
443 | List<string> mainParams = ops.Parse(cmdparams); | 461 | List<string> mainParams = ops.Parse(cmdparams); |
444 | 462 | ||
@@ -464,8 +482,8 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver | |||
464 | "[INVENTORY ARCHIVER]: Saving archive {0} using inventory path {1} for {2} {3}", | 482 | "[INVENTORY ARCHIVER]: Saving archive {0} using inventory path {1} for {2} {3}", |
465 | savePath, invPath, firstName, lastName); | 483 | savePath, invPath, firstName, lastName); |
466 | 484 | ||
467 | lock (m_pendingConsoleSaves) | 485 | lock (m_pendingConsoleTasks) |
468 | m_pendingConsoleSaves.Add(id); | 486 | m_pendingConsoleTasks.Add(id); |
469 | 487 | ||
470 | ArchiveInventory(id, firstName, lastName, invPath, pass, savePath, options); | 488 | ArchiveInventory(id, firstName, lastName, invPath, pass, savePath, options); |
471 | } | 489 | } |
@@ -476,20 +494,24 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver | |||
476 | } | 494 | } |
477 | 495 | ||
478 | private void SaveInvConsoleCommandCompleted( | 496 | private void SaveInvConsoleCommandCompleted( |
479 | Guid id, bool succeeded, UserAccount userInfo, string invPath, Stream saveStream, | 497 | UUID id, bool succeeded, UserAccount userInfo, string invPath, Stream saveStream, |
480 | Exception reportedException) | 498 | Exception reportedException, int SaveCount, int FilterCount) |
481 | { | 499 | { |
482 | lock (m_pendingConsoleSaves) | 500 | lock (m_pendingConsoleTasks) |
483 | { | 501 | { |
484 | if (m_pendingConsoleSaves.Contains(id)) | 502 | if (m_pendingConsoleTasks.Contains(id)) |
485 | m_pendingConsoleSaves.Remove(id); | 503 | m_pendingConsoleTasks.Remove(id); |
486 | else | 504 | else |
487 | return; | 505 | return; |
488 | } | 506 | } |
489 | 507 | ||
490 | if (succeeded) | 508 | if (succeeded) |
491 | { | 509 | { |
492 | m_log.InfoFormat("[INVENTORY ARCHIVER]: Saved archive for {0} {1}", userInfo.FirstName, userInfo.LastName); | 510 | // Report success and include item count and filter count (Skipped items due to --perm or --exclude switches) |
511 | if(FilterCount == 0) | ||
512 | m_log.InfoFormat("[INVENTORY ARCHIVER]: Saved archive with {0} items for {1} {2}", SaveCount, userInfo.FirstName, userInfo.LastName); | ||
513 | else | ||
514 | m_log.InfoFormat("[INVENTORY ARCHIVER]: Saved archive with {0} items for {1} {2}. Skipped {3} items due to exclude and/or perm switches", SaveCount, userInfo.FirstName, userInfo.LastName, FilterCount); | ||
493 | } | 515 | } |
494 | else | 516 | else |
495 | { | 517 | { |
@@ -499,6 +521,30 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver | |||
499 | } | 521 | } |
500 | } | 522 | } |
501 | 523 | ||
524 | private void LoadInvConsoleCommandCompleted( | ||
525 | UUID id, bool succeeded, UserAccount userInfo, string invPath, Stream loadStream, | ||
526 | Exception reportedException, int LoadCount) | ||
527 | { | ||
528 | lock (m_pendingConsoleTasks) | ||
529 | { | ||
530 | if (m_pendingConsoleTasks.Contains(id)) | ||
531 | m_pendingConsoleTasks.Remove(id); | ||
532 | else | ||
533 | return; | ||
534 | } | ||
535 | |||
536 | if (succeeded) | ||
537 | { | ||
538 | m_log.InfoFormat("[INVENTORY ARCHIVER]: Loaded {0} items from archive {1} for {2} {3}", LoadCount, invPath, userInfo.FirstName, userInfo.LastName); | ||
539 | } | ||
540 | else | ||
541 | { | ||
542 | m_log.ErrorFormat( | ||
543 | "[INVENTORY ARCHIVER]: Archive load for {0} {1} failed - {2}", | ||
544 | userInfo.FirstName, userInfo.LastName, reportedException.Message); | ||
545 | } | ||
546 | } | ||
547 | |||
502 | /// <summary> | 548 | /// <summary> |
503 | /// Get user information for the given name. | 549 | /// Get user information for the given name. |
504 | /// </summary> | 550 | /// </summary> |
@@ -536,7 +582,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver | |||
536 | } | 582 | } |
537 | catch (Exception e) | 583 | catch (Exception e) |
538 | { | 584 | { |
539 | m_log.ErrorFormat("[INVENTORY ARCHIVER]: Could not authenticate password, {0}", e.Message); | 585 | m_log.ErrorFormat("[INVENTORY ARCHIVER]: Could not authenticate password, {0}", e); |
540 | return null; | 586 | return null; |
541 | } | 587 | } |
542 | } | 588 | } |
diff --git a/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/Tests/PathTests.cs b/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/Tests/InventoryArchiveLoadPathTests.cs index 6eb3605..c2e645f 100644 --- a/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/Tests/PathTests.cs +++ b/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/Tests/InventoryArchiveLoadPathTests.cs | |||
@@ -36,137 +36,19 @@ using OpenSim.Data; | |||
36 | using OpenSim.Framework; | 36 | using OpenSim.Framework; |
37 | using OpenSim.Framework.Serialization; | 37 | using OpenSim.Framework.Serialization; |
38 | using OpenSim.Framework.Serialization.External; | 38 | using OpenSim.Framework.Serialization.External; |
39 | using OpenSim.Framework.Communications; | ||
40 | using OpenSim.Region.CoreModules.Avatar.Inventory.Archiver; | 39 | using OpenSim.Region.CoreModules.Avatar.Inventory.Archiver; |
41 | using OpenSim.Region.CoreModules.World.Serialiser; | 40 | using OpenSim.Region.CoreModules.World.Serialiser; |
42 | using OpenSim.Region.Framework.Scenes; | 41 | using OpenSim.Region.Framework.Scenes; |
43 | using OpenSim.Region.Framework.Scenes.Serialization; | 42 | using OpenSim.Region.Framework.Scenes.Serialization; |
44 | using OpenSim.Services.Interfaces; | 43 | using OpenSim.Services.Interfaces; |
45 | using OpenSim.Tests.Common; | 44 | using OpenSim.Tests.Common; |
46 | using OpenSim.Tests.Common.Mock; | ||
47 | 45 | ||
48 | namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver.Tests | 46 | namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver.Tests |
49 | { | 47 | { |
50 | [TestFixture] | 48 | [TestFixture] |
51 | public class PathTests : InventoryArchiveTestCase | 49 | public class InventoryArchiveLoadPathTests : InventoryArchiveTestCase |
52 | { | 50 | { |
53 | /// <summary> | 51 | /// <summary> |
54 | /// Test saving an inventory path to a V0.1 OpenSim Inventory Archive | ||
55 | /// (subject to change since there is no fixed format yet). | ||
56 | /// </summary> | ||
57 | [Test] | ||
58 | public void TestSavePathToIarV0_1() | ||
59 | { | ||
60 | TestHelpers.InMethod(); | ||
61 | // log4net.Config.XmlConfigurator.Configure(); | ||
62 | |||
63 | InventoryArchiverModule archiverModule = new InventoryArchiverModule(); | ||
64 | |||
65 | Scene scene = new SceneHelpers().SetupScene(); | ||
66 | SceneHelpers.SetupSceneModules(scene, archiverModule); | ||
67 | |||
68 | // Create user | ||
69 | string userFirstName = "Jock"; | ||
70 | string userLastName = "Stirrup"; | ||
71 | string userPassword = "troll"; | ||
72 | UUID userId = UUID.Parse("00000000-0000-0000-0000-000000000020"); | ||
73 | UserAccountHelpers.CreateUserWithInventory(scene, userFirstName, userLastName, userId, userPassword); | ||
74 | |||
75 | // Create asset | ||
76 | SceneObjectGroup object1; | ||
77 | SceneObjectPart part1; | ||
78 | { | ||
79 | string partName = "My Little Dog Object"; | ||
80 | UUID ownerId = UUID.Parse("00000000-0000-0000-0000-000000000040"); | ||
81 | PrimitiveBaseShape shape = PrimitiveBaseShape.CreateSphere(); | ||
82 | Vector3 groupPosition = new Vector3(10, 20, 30); | ||
83 | Quaternion rotationOffset = new Quaternion(20, 30, 40, 50); | ||
84 | Vector3 offsetPosition = new Vector3(5, 10, 15); | ||
85 | |||
86 | part1 = new SceneObjectPart(ownerId, shape, groupPosition, rotationOffset, offsetPosition); | ||
87 | part1.Name = partName; | ||
88 | |||
89 | object1 = new SceneObjectGroup(part1); | ||
90 | scene.AddNewSceneObject(object1, false); | ||
91 | } | ||
92 | |||
93 | UUID asset1Id = UUID.Parse("00000000-0000-0000-0000-000000000060"); | ||
94 | AssetBase asset1 = AssetHelpers.CreateAsset(asset1Id, object1); | ||
95 | scene.AssetService.Store(asset1); | ||
96 | |||
97 | // Create item | ||
98 | UUID item1Id = UUID.Parse("00000000-0000-0000-0000-000000000080"); | ||
99 | InventoryItemBase item1 = new InventoryItemBase(); | ||
100 | item1.Name = "My Little Dog"; | ||
101 | item1.AssetID = asset1.FullID; | ||
102 | item1.ID = item1Id; | ||
103 | InventoryFolderBase objsFolder | ||
104 | = InventoryArchiveUtils.FindFolderByPath(scene.InventoryService, userId, "Objects")[0]; | ||
105 | item1.Folder = objsFolder.ID; | ||
106 | scene.AddInventoryItem(item1); | ||
107 | |||
108 | MemoryStream archiveWriteStream = new MemoryStream(); | ||
109 | archiverModule.OnInventoryArchiveSaved += SaveCompleted; | ||
110 | |||
111 | // Test saving a particular path | ||
112 | mre.Reset(); | ||
113 | archiverModule.ArchiveInventory( | ||
114 | Guid.NewGuid(), userFirstName, userLastName, "Objects", userPassword, archiveWriteStream); | ||
115 | mre.WaitOne(60000, false); | ||
116 | |||
117 | byte[] archive = archiveWriteStream.ToArray(); | ||
118 | MemoryStream archiveReadStream = new MemoryStream(archive); | ||
119 | TarArchiveReader tar = new TarArchiveReader(archiveReadStream); | ||
120 | |||
121 | //bool gotControlFile = false; | ||
122 | bool gotObject1File = false; | ||
123 | //bool gotObject2File = false; | ||
124 | string expectedObject1FileName = InventoryArchiveWriteRequest.CreateArchiveItemName(item1); | ||
125 | string expectedObject1FilePath = string.Format( | ||
126 | "{0}{1}{2}", | ||
127 | ArchiveConstants.INVENTORY_PATH, | ||
128 | InventoryArchiveWriteRequest.CreateArchiveFolderName(objsFolder), | ||
129 | expectedObject1FileName); | ||
130 | |||
131 | string filePath; | ||
132 | TarArchiveReader.TarEntryType tarEntryType; | ||
133 | |||
134 | // Console.WriteLine("Reading archive"); | ||
135 | |||
136 | while (tar.ReadEntry(out filePath, out tarEntryType) != null) | ||
137 | { | ||
138 | // Console.WriteLine("Got {0}", filePath); | ||
139 | |||
140 | // if (ArchiveConstants.CONTROL_FILE_PATH == filePath) | ||
141 | // { | ||
142 | // gotControlFile = true; | ||
143 | // } | ||
144 | |||
145 | if (filePath.StartsWith(ArchiveConstants.INVENTORY_PATH) && filePath.EndsWith(".xml")) | ||
146 | { | ||
147 | // string fileName = filePath.Remove(0, "Objects/".Length); | ||
148 | // | ||
149 | // if (fileName.StartsWith(part1.Name)) | ||
150 | // { | ||
151 | Assert.That(expectedObject1FilePath, Is.EqualTo(filePath)); | ||
152 | gotObject1File = true; | ||
153 | // } | ||
154 | // else if (fileName.StartsWith(part2.Name)) | ||
155 | // { | ||
156 | // Assert.That(fileName, Is.EqualTo(expectedObject2FileName)); | ||
157 | // gotObject2File = true; | ||
158 | // } | ||
159 | } | ||
160 | } | ||
161 | |||
162 | // Assert.That(gotControlFile, Is.True, "No control file in archive"); | ||
163 | Assert.That(gotObject1File, Is.True, "No item1 file in archive"); | ||
164 | // Assert.That(gotObject2File, Is.True, "No object2 file in archive"); | ||
165 | |||
166 | // TODO: Test presence of more files and contents of files. | ||
167 | } | ||
168 | |||
169 | /// <summary> | ||
170 | /// Test loading an IAR to various different inventory paths. | 52 | /// Test loading an IAR to various different inventory paths. |
171 | /// </summary> | 53 | /// </summary> |
172 | [Test] | 54 | [Test] |
@@ -185,26 +67,26 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver.Tests | |||
185 | 67 | ||
186 | UserAccountHelpers.CreateUserWithInventory(scene, m_uaMT, "meowfood"); | 68 | UserAccountHelpers.CreateUserWithInventory(scene, m_uaMT, "meowfood"); |
187 | UserAccountHelpers.CreateUserWithInventory(scene, m_uaLL1, "hampshire"); | 69 | UserAccountHelpers.CreateUserWithInventory(scene, m_uaLL1, "hampshire"); |
188 | 70 | ||
189 | archiverModule.DearchiveInventory(m_uaMT.FirstName, m_uaMT.LastName, "/", "meowfood", m_iarStream); | 71 | archiverModule.DearchiveInventory(UUID.Random(), m_uaMT.FirstName, m_uaMT.LastName, "/", "meowfood", m_iarStream); |
190 | InventoryItemBase foundItem1 | 72 | InventoryItemBase foundItem1 |
191 | = InventoryArchiveUtils.FindItemByPath(scene.InventoryService, m_uaMT.PrincipalID, m_item1Name); | 73 | = InventoryArchiveUtils.FindItemByPath(scene.InventoryService, m_uaMT.PrincipalID, m_item1Name); |
192 | 74 | ||
193 | Assert.That(foundItem1, Is.Not.Null, "Didn't find loaded item 1"); | 75 | Assert.That(foundItem1, Is.Not.Null, "Didn't find loaded item 1"); |
194 | 76 | ||
195 | // Now try loading to a root child folder | 77 | // Now try loading to a root child folder |
196 | UserInventoryHelpers.CreateInventoryFolder(scene.InventoryService, m_uaMT.PrincipalID, "xA"); | 78 | UserInventoryHelpers.CreateInventoryFolder(scene.InventoryService, m_uaMT.PrincipalID, "xA", false); |
197 | MemoryStream archiveReadStream = new MemoryStream(m_iarStream.ToArray()); | 79 | MemoryStream archiveReadStream = new MemoryStream(m_iarStream.ToArray()); |
198 | archiverModule.DearchiveInventory(m_uaMT.FirstName, m_uaMT.LastName, "xA", "meowfood", archiveReadStream); | 80 | archiverModule.DearchiveInventory(UUID.Random(), m_uaMT.FirstName, m_uaMT.LastName, "xA", "meowfood", archiveReadStream); |
199 | 81 | ||
200 | InventoryItemBase foundItem2 | 82 | InventoryItemBase foundItem2 |
201 | = InventoryArchiveUtils.FindItemByPath(scene.InventoryService, m_uaMT.PrincipalID, "xA/" + m_item1Name); | 83 | = InventoryArchiveUtils.FindItemByPath(scene.InventoryService, m_uaMT.PrincipalID, "xA/" + m_item1Name); |
202 | Assert.That(foundItem2, Is.Not.Null, "Didn't find loaded item 2"); | 84 | Assert.That(foundItem2, Is.Not.Null, "Didn't find loaded item 2"); |
203 | 85 | ||
204 | // Now try loading to a more deeply nested folder | 86 | // Now try loading to a more deeply nested folder |
205 | UserInventoryHelpers.CreateInventoryFolder(scene.InventoryService, m_uaMT.PrincipalID, "xB/xC"); | 87 | UserInventoryHelpers.CreateInventoryFolder(scene.InventoryService, m_uaMT.PrincipalID, "xB/xC", false); |
206 | archiveReadStream = new MemoryStream(archiveReadStream.ToArray()); | 88 | archiveReadStream = new MemoryStream(archiveReadStream.ToArray()); |
207 | archiverModule.DearchiveInventory(m_uaMT.FirstName, m_uaMT.LastName, "xB/xC", "meowfood", archiveReadStream); | 89 | archiverModule.DearchiveInventory(UUID.Random(), m_uaMT.FirstName, m_uaMT.LastName, "xB/xC", "meowfood", archiveReadStream); |
208 | 90 | ||
209 | InventoryItemBase foundItem3 | 91 | InventoryItemBase foundItem3 |
210 | = InventoryArchiveUtils.FindItemByPath(scene.InventoryService, m_uaMT.PrincipalID, "xB/xC/" + m_item1Name); | 92 | = InventoryArchiveUtils.FindItemByPath(scene.InventoryService, m_uaMT.PrincipalID, "xB/xC/" + m_item1Name); |
@@ -226,7 +108,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver.Tests | |||
226 | SceneHelpers.SetupSceneModules(scene, serialiserModule, archiverModule); | 108 | SceneHelpers.SetupSceneModules(scene, serialiserModule, archiverModule); |
227 | 109 | ||
228 | UserAccountHelpers.CreateUserWithInventory(scene, m_uaMT, "password"); | 110 | UserAccountHelpers.CreateUserWithInventory(scene, m_uaMT, "password"); |
229 | archiverModule.DearchiveInventory(m_uaMT.FirstName, m_uaMT.LastName, "/Objects", "password", m_iarStream); | 111 | archiverModule.DearchiveInventory(UUID.Random(), m_uaMT.FirstName, m_uaMT.LastName, "/Objects", "password", m_iarStream); |
230 | 112 | ||
231 | InventoryItemBase foundItem1 | 113 | InventoryItemBase foundItem1 |
232 | = InventoryArchiveUtils.FindItemByPath( | 114 | = InventoryArchiveUtils.FindItemByPath( |
@@ -287,7 +169,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver.Tests | |||
287 | item1.AssetID = asset1.FullID; | 169 | item1.AssetID = asset1.FullID; |
288 | item1.ID = item1Id; | 170 | item1.ID = item1Id; |
289 | InventoryFolderBase objsFolder | 171 | InventoryFolderBase objsFolder |
290 | = InventoryArchiveUtils.FindFolderByPath(scene.InventoryService, userId, "Objects")[0]; | 172 | = InventoryArchiveUtils.FindFoldersByPath(scene.InventoryService, userId, "Objects")[0]; |
291 | item1.Folder = objsFolder.ID; | 173 | item1.Folder = objsFolder.ID; |
292 | scene.AddInventoryItem(item1); | 174 | scene.AddInventoryItem(item1); |
293 | 175 | ||
@@ -296,13 +178,13 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver.Tests | |||
296 | 178 | ||
297 | mre.Reset(); | 179 | mre.Reset(); |
298 | archiverModule.ArchiveInventory( | 180 | archiverModule.ArchiveInventory( |
299 | Guid.NewGuid(), userFirstName, userLastName, "Objects", userPassword, archiveWriteStream); | 181 | UUID.Random(), userFirstName, userLastName, "Objects", userPassword, archiveWriteStream); |
300 | mre.WaitOne(60000, false); | 182 | mre.WaitOne(60000, false); |
301 | 183 | ||
302 | // LOAD ITEM | 184 | // LOAD ITEM |
303 | MemoryStream archiveReadStream = new MemoryStream(archiveWriteStream.ToArray()); | 185 | MemoryStream archiveReadStream = new MemoryStream(archiveWriteStream.ToArray()); |
304 | 186 | ||
305 | archiverModule.DearchiveInventory(userFirstName, userLastName, "Scripts", userPassword, archiveReadStream); | 187 | archiverModule.DearchiveInventory(UUID.Random(), userFirstName, userLastName, "Scripts", userPassword, archiveReadStream); |
306 | 188 | ||
307 | InventoryItemBase foundItem1 | 189 | InventoryItemBase foundItem1 |
308 | = InventoryArchiveUtils.FindItemByPath( | 190 | = InventoryArchiveUtils.FindItemByPath( |
@@ -345,40 +227,40 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver.Tests | |||
345 | 227 | ||
346 | { | 228 | { |
347 | // Test replication of path1 | 229 | // Test replication of path1 |
348 | new InventoryArchiveReadRequest(scene, ua1, null, (Stream)null, false) | 230 | new InventoryArchiveReadRequest(UUID.Random(), null, scene.InventoryService, scene.AssetService, scene.UserAccountService, ua1, null, (Stream)null, false) |
349 | .ReplicateArchivePathToUserInventory( | 231 | .ReplicateArchivePathToUserInventory( |
350 | iarPath1, scene.InventoryService.GetRootFolder(ua1.PrincipalID), | 232 | iarPath1, scene.InventoryService.GetRootFolder(ua1.PrincipalID), |
351 | foldersCreated, nodesLoaded); | 233 | foldersCreated, nodesLoaded); |
352 | 234 | ||
353 | List<InventoryFolderBase> folder1Candidates | 235 | List<InventoryFolderBase> folder1Candidates |
354 | = InventoryArchiveUtils.FindFolderByPath(scene.InventoryService, ua1.PrincipalID, folder1Name); | 236 | = InventoryArchiveUtils.FindFoldersByPath(scene.InventoryService, ua1.PrincipalID, folder1Name); |
355 | Assert.That(folder1Candidates.Count, Is.EqualTo(1)); | 237 | Assert.That(folder1Candidates.Count, Is.EqualTo(1)); |
356 | 238 | ||
357 | InventoryFolderBase folder1 = folder1Candidates[0]; | 239 | InventoryFolderBase folder1 = folder1Candidates[0]; |
358 | List<InventoryFolderBase> folder2aCandidates | 240 | List<InventoryFolderBase> folder2aCandidates |
359 | = InventoryArchiveUtils.FindFolderByPath(scene.InventoryService, folder1, folder2aName); | 241 | = InventoryArchiveUtils.FindFoldersByPath(scene.InventoryService, folder1, folder2aName); |
360 | Assert.That(folder2aCandidates.Count, Is.EqualTo(1)); | 242 | Assert.That(folder2aCandidates.Count, Is.EqualTo(1)); |
361 | } | 243 | } |
362 | 244 | ||
363 | { | 245 | { |
364 | // Test replication of path2 | 246 | // Test replication of path2 |
365 | new InventoryArchiveReadRequest(scene, ua1, null, (Stream)null, false) | 247 | new InventoryArchiveReadRequest(UUID.Random(), null, scene.InventoryService, scene.AssetService, scene.UserAccountService, ua1, null, (Stream)null, false) |
366 | .ReplicateArchivePathToUserInventory( | 248 | .ReplicateArchivePathToUserInventory( |
367 | iarPath2, scene.InventoryService.GetRootFolder(ua1.PrincipalID), | 249 | iarPath2, scene.InventoryService.GetRootFolder(ua1.PrincipalID), |
368 | foldersCreated, nodesLoaded); | 250 | foldersCreated, nodesLoaded); |
369 | 251 | ||
370 | List<InventoryFolderBase> folder1Candidates | 252 | List<InventoryFolderBase> folder1Candidates |
371 | = InventoryArchiveUtils.FindFolderByPath(scene.InventoryService, ua1.PrincipalID, folder1Name); | 253 | = InventoryArchiveUtils.FindFoldersByPath(scene.InventoryService, ua1.PrincipalID, folder1Name); |
372 | Assert.That(folder1Candidates.Count, Is.EqualTo(1)); | 254 | Assert.That(folder1Candidates.Count, Is.EqualTo(1)); |
373 | 255 | ||
374 | InventoryFolderBase folder1 = folder1Candidates[0]; | 256 | InventoryFolderBase folder1 = folder1Candidates[0]; |
375 | 257 | ||
376 | List<InventoryFolderBase> folder2aCandidates | 258 | List<InventoryFolderBase> folder2aCandidates |
377 | = InventoryArchiveUtils.FindFolderByPath(scene.InventoryService, folder1, folder2aName); | 259 | = InventoryArchiveUtils.FindFoldersByPath(scene.InventoryService, folder1, folder2aName); |
378 | Assert.That(folder2aCandidates.Count, Is.EqualTo(1)); | 260 | Assert.That(folder2aCandidates.Count, Is.EqualTo(1)); |
379 | 261 | ||
380 | List<InventoryFolderBase> folder2bCandidates | 262 | List<InventoryFolderBase> folder2bCandidates |
381 | = InventoryArchiveUtils.FindFolderByPath(scene.InventoryService, folder1, folder2bName); | 263 | = InventoryArchiveUtils.FindFoldersByPath(scene.InventoryService, folder1, folder2bName); |
382 | Assert.That(folder2bCandidates.Count, Is.EqualTo(1)); | 264 | Assert.That(folder2bCandidates.Count, Is.EqualTo(1)); |
383 | } | 265 | } |
384 | } | 266 | } |
@@ -401,20 +283,20 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver.Tests | |||
401 | 283 | ||
402 | InventoryFolderBase folder1 | 284 | InventoryFolderBase folder1 |
403 | = UserInventoryHelpers.CreateInventoryFolder( | 285 | = UserInventoryHelpers.CreateInventoryFolder( |
404 | scene.InventoryService, ua1.PrincipalID, folder1ExistingName); | 286 | scene.InventoryService, ua1.PrincipalID, folder1ExistingName, false); |
405 | 287 | ||
406 | string folder1ArchiveName = InventoryArchiveWriteRequest.CreateArchiveFolderName(folder1ExistingName, UUID.Random()); | 288 | string folder1ArchiveName = InventoryArchiveWriteRequest.CreateArchiveFolderName(folder1ExistingName, UUID.Random()); |
407 | string folder2ArchiveName = InventoryArchiveWriteRequest.CreateArchiveFolderName(folder2Name, UUID.Random()); | 289 | string folder2ArchiveName = InventoryArchiveWriteRequest.CreateArchiveFolderName(folder2Name, UUID.Random()); |
408 | 290 | ||
409 | string itemArchivePath = string.Join("", new string[] { folder1ArchiveName, folder2ArchiveName }); | 291 | string itemArchivePath = string.Join("", new string[] { folder1ArchiveName, folder2ArchiveName }); |
410 | 292 | ||
411 | new InventoryArchiveReadRequest(scene, ua1, null, (Stream)null, false) | 293 | new InventoryArchiveReadRequest(UUID.Random(), null, scene.InventoryService, scene.AssetService, scene.UserAccountService, ua1, null, (Stream)null, false) |
412 | .ReplicateArchivePathToUserInventory( | 294 | .ReplicateArchivePathToUserInventory( |
413 | itemArchivePath, scene.InventoryService.GetRootFolder(ua1.PrincipalID), | 295 | itemArchivePath, scene.InventoryService.GetRootFolder(ua1.PrincipalID), |
414 | new Dictionary<string, InventoryFolderBase>(), new HashSet<InventoryNodeBase>()); | 296 | new Dictionary<string, InventoryFolderBase>(), new HashSet<InventoryNodeBase>()); |
415 | 297 | ||
416 | List<InventoryFolderBase> folder1PostCandidates | 298 | List<InventoryFolderBase> folder1PostCandidates |
417 | = InventoryArchiveUtils.FindFolderByPath(scene.InventoryService, ua1.PrincipalID, folder1ExistingName); | 299 | = InventoryArchiveUtils.FindFoldersByPath(scene.InventoryService, ua1.PrincipalID, folder1ExistingName); |
418 | Assert.That(folder1PostCandidates.Count, Is.EqualTo(2)); | 300 | Assert.That(folder1PostCandidates.Count, Is.EqualTo(2)); |
419 | 301 | ||
420 | // FIXME: Temporarily, we're going to do something messy to make sure we pick up the created folder. | 302 | // FIXME: Temporarily, we're going to do something messy to make sure we pick up the created folder. |
@@ -430,7 +312,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver.Tests | |||
430 | // Assert.That(folder1Post.ID, Is.EqualTo(folder1.ID)); | 312 | // Assert.That(folder1Post.ID, Is.EqualTo(folder1.ID)); |
431 | 313 | ||
432 | List<InventoryFolderBase> folder2PostCandidates | 314 | List<InventoryFolderBase> folder2PostCandidates |
433 | = InventoryArchiveUtils.FindFolderByPath(scene.InventoryService, folder1Post, "b"); | 315 | = InventoryArchiveUtils.FindFoldersByPath(scene.InventoryService, folder1Post, "b"); |
434 | Assert.That(folder2PostCandidates.Count, Is.EqualTo(1)); | 316 | Assert.That(folder2PostCandidates.Count, Is.EqualTo(1)); |
435 | } | 317 | } |
436 | 318 | ||
@@ -452,26 +334,27 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver.Tests | |||
452 | 334 | ||
453 | InventoryFolderBase folder1 | 335 | InventoryFolderBase folder1 |
454 | = UserInventoryHelpers.CreateInventoryFolder( | 336 | = UserInventoryHelpers.CreateInventoryFolder( |
455 | scene.InventoryService, ua1.PrincipalID, folder1ExistingName); | 337 | scene.InventoryService, ua1.PrincipalID, folder1ExistingName, false); |
456 | 338 | ||
457 | string folder1ArchiveName = InventoryArchiveWriteRequest.CreateArchiveFolderName(folder1ExistingName, UUID.Random()); | 339 | string folder1ArchiveName = InventoryArchiveWriteRequest.CreateArchiveFolderName(folder1ExistingName, UUID.Random()); |
458 | string folder2ArchiveName = InventoryArchiveWriteRequest.CreateArchiveFolderName(folder2Name, UUID.Random()); | 340 | string folder2ArchiveName = InventoryArchiveWriteRequest.CreateArchiveFolderName(folder2Name, UUID.Random()); |
459 | 341 | ||
460 | string itemArchivePath = string.Join("", new string[] { folder1ArchiveName, folder2ArchiveName }); | 342 | string itemArchivePath = string.Join("", new string[] { folder1ArchiveName, folder2ArchiveName }); |
461 | 343 | ||
462 | new InventoryArchiveReadRequest(scene, ua1, folder1ExistingName, (Stream)null, true) | 344 | new InventoryArchiveReadRequest(UUID.Random(), null, scene.InventoryService, scene.AssetService, scene.UserAccountService, ua1, folder1ExistingName, (Stream)null, true) |
463 | .ReplicateArchivePathToUserInventory( | 345 | .ReplicateArchivePathToUserInventory( |
464 | itemArchivePath, scene.InventoryService.GetRootFolder(ua1.PrincipalID), | 346 | itemArchivePath, scene.InventoryService.GetRootFolder(ua1.PrincipalID), |
465 | new Dictionary<string, InventoryFolderBase>(), new HashSet<InventoryNodeBase>()); | 347 | new Dictionary<string, InventoryFolderBase>(), new HashSet<InventoryNodeBase>()); |
466 | 348 | ||
467 | List<InventoryFolderBase> folder1PostCandidates | 349 | List<InventoryFolderBase> folder1PostCandidates |
468 | = InventoryArchiveUtils.FindFolderByPath(scene.InventoryService, ua1.PrincipalID, folder1ExistingName); | 350 | = InventoryArchiveUtils.FindFoldersByPath(scene.InventoryService, ua1.PrincipalID, folder1ExistingName); |
469 | Assert.That(folder1PostCandidates.Count, Is.EqualTo(1)); | 351 | Assert.That(folder1PostCandidates.Count, Is.EqualTo(1)); |
470 | Assert.That(folder1PostCandidates[0].ID, Is.EqualTo(folder1.ID)); | 352 | Assert.That(folder1PostCandidates[0].ID, Is.EqualTo(folder1.ID)); |
471 | 353 | ||
472 | List<InventoryFolderBase> folder2PostCandidates | 354 | List<InventoryFolderBase> folder2PostCandidates |
473 | = InventoryArchiveUtils.FindFolderByPath(scene.InventoryService, folder1PostCandidates[0], "b"); | 355 | = InventoryArchiveUtils.FindFoldersByPath(scene.InventoryService, folder1PostCandidates[0], "b"); |
474 | Assert.That(folder2PostCandidates.Count, Is.EqualTo(1)); | 356 | Assert.That(folder2PostCandidates.Count, Is.EqualTo(1)); |
475 | } | 357 | } |
476 | } | 358 | } |
477 | } \ No newline at end of file | 359 | } |
360 | |||
diff --git a/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/Tests/InventoryArchiveLoadTests.cs b/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/Tests/InventoryArchiveLoadTests.cs new file mode 100644 index 0000000..57b4f80 --- /dev/null +++ b/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/Tests/InventoryArchiveLoadTests.cs | |||
@@ -0,0 +1,192 @@ | |||
1 | /* | ||
2 | * Copyright (c) Contributors, http://opensimulator.org/ | ||
3 | * See CONTRIBUTORS.TXT for a full list of copyright holders. | ||
4 | * | ||
5 | * Redistribution and use in source and binary forms, with or without | ||
6 | * modification, are permitted provided that the following conditions are met: | ||
7 | * * Redistributions of source code must retain the above copyright | ||
8 | * notice, this list of conditions and the following disclaimer. | ||
9 | * * Redistributions in binary form must reproduce the above copyright | ||
10 | * notice, this list of conditions and the following disclaimer in the | ||
11 | * documentation and/or other materials provided with the distribution. | ||
12 | * * Neither the name of the OpenSimulator Project nor the | ||
13 | * names of its contributors may be used to endorse or promote products | ||
14 | * derived from this software without specific prior written permission. | ||
15 | * | ||
16 | * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY | ||
17 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||
18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
19 | * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY | ||
20 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||
21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||
22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | ||
23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
26 | */ | ||
27 | |||
28 | using System; | ||
29 | using System.Collections.Generic; | ||
30 | using System.IO; | ||
31 | using System.Reflection; | ||
32 | using System.Threading; | ||
33 | using NUnit.Framework; | ||
34 | using OpenMetaverse; | ||
35 | using OpenSim.Data; | ||
36 | using OpenSim.Framework; | ||
37 | using OpenSim.Framework.Serialization; | ||
38 | using OpenSim.Framework.Serialization.External; | ||
39 | using OpenSim.Region.CoreModules.Avatar.Inventory.Archiver; | ||
40 | using OpenSim.Region.CoreModules.World.Serialiser; | ||
41 | using OpenSim.Region.Framework.Scenes; | ||
42 | using OpenSim.Region.Framework.Scenes.Serialization; | ||
43 | using OpenSim.Services.Interfaces; | ||
44 | using OpenSim.Tests.Common; | ||
45 | |||
46 | namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver.Tests | ||
47 | { | ||
48 | [TestFixture] | ||
49 | public class InventoryArchiveLoadTests : InventoryArchiveTestCase | ||
50 | { | ||
51 | protected TestScene m_scene; | ||
52 | protected InventoryArchiverModule m_archiverModule; | ||
53 | |||
54 | [SetUp] | ||
55 | public override void SetUp() | ||
56 | { | ||
57 | base.SetUp(); | ||
58 | |||
59 | SerialiserModule serialiserModule = new SerialiserModule(); | ||
60 | m_archiverModule = new InventoryArchiverModule(); | ||
61 | |||
62 | m_scene = new SceneHelpers().SetupScene(); | ||
63 | SceneHelpers.SetupSceneModules(m_scene, serialiserModule, m_archiverModule); | ||
64 | } | ||
65 | |||
66 | [Test] | ||
67 | public void TestLoadCoalesecedItem() | ||
68 | { | ||
69 | TestHelpers.InMethod(); | ||
70 | // TestHelpers.EnableLogging(); | ||
71 | |||
72 | UserAccountHelpers.CreateUserWithInventory(m_scene, m_uaLL1, "password"); | ||
73 | m_archiverModule.DearchiveInventory(UUID.Random(), m_uaLL1.FirstName, m_uaLL1.LastName, "/", "password", m_iarStream); | ||
74 | |||
75 | InventoryItemBase coaItem | ||
76 | = InventoryArchiveUtils.FindItemByPath(m_scene.InventoryService, m_uaLL1.PrincipalID, m_coaItemName); | ||
77 | |||
78 | Assert.That(coaItem, Is.Not.Null, "Didn't find loaded item 1"); | ||
79 | |||
80 | string assetXml = AssetHelpers.ReadAssetAsString(m_scene.AssetService, coaItem.AssetID); | ||
81 | |||
82 | CoalescedSceneObjects coa; | ||
83 | bool readResult = CoalescedSceneObjectsSerializer.TryFromXml(assetXml, out coa); | ||
84 | |||
85 | Assert.That(readResult, Is.True); | ||
86 | Assert.That(coa.Count, Is.EqualTo(2)); | ||
87 | |||
88 | List<SceneObjectGroup> coaObjects = coa.Objects; | ||
89 | Assert.That(coaObjects[0].UUID, Is.EqualTo(UUID.Parse("00000000-0000-0000-0000-000000000120"))); | ||
90 | Assert.That(coaObjects[0].AbsolutePosition, Is.EqualTo(new Vector3(15, 30, 45))); | ||
91 | |||
92 | Assert.That(coaObjects[1].UUID, Is.EqualTo(UUID.Parse("00000000-0000-0000-0000-000000000140"))); | ||
93 | Assert.That(coaObjects[1].AbsolutePosition, Is.EqualTo(new Vector3(25, 50, 75))); | ||
94 | } | ||
95 | |||
96 | /// <summary> | ||
97 | /// Test case where a creator account exists for the creator UUID embedded in item metadata and serialized | ||
98 | /// objects. | ||
99 | /// </summary> | ||
100 | [Test] | ||
101 | public void TestLoadIarCreatorAccountPresent() | ||
102 | { | ||
103 | TestHelpers.InMethod(); | ||
104 | // log4net.Config.XmlConfigurator.Configure(); | ||
105 | |||
106 | UserAccountHelpers.CreateUserWithInventory(m_scene, m_uaLL1, "meowfood"); | ||
107 | |||
108 | m_archiverModule.DearchiveInventory(UUID.Random(), m_uaLL1.FirstName, m_uaLL1.LastName, "/", "meowfood", m_iarStream); | ||
109 | InventoryItemBase foundItem1 | ||
110 | = InventoryArchiveUtils.FindItemByPath(m_scene.InventoryService, m_uaLL1.PrincipalID, m_item1Name); | ||
111 | |||
112 | Assert.That( | ||
113 | foundItem1.CreatorId, Is.EqualTo(m_uaLL1.PrincipalID.ToString()), | ||
114 | "Loaded item non-uuid creator doesn't match original"); | ||
115 | Assert.That( | ||
116 | foundItem1.CreatorIdAsUuid, Is.EqualTo(m_uaLL1.PrincipalID), | ||
117 | "Loaded item uuid creator doesn't match original"); | ||
118 | Assert.That(foundItem1.Owner, Is.EqualTo(m_uaLL1.PrincipalID), | ||
119 | "Loaded item owner doesn't match inventory reciever"); | ||
120 | |||
121 | AssetBase asset1 = m_scene.AssetService.Get(foundItem1.AssetID.ToString()); | ||
122 | string xmlData = Utils.BytesToString(asset1.Data); | ||
123 | SceneObjectGroup sog1 = SceneObjectSerializer.FromOriginalXmlFormat(xmlData); | ||
124 | |||
125 | Assert.That(sog1.RootPart.CreatorID, Is.EqualTo(m_uaLL1.PrincipalID)); | ||
126 | } | ||
127 | |||
128 | // /// <summary> | ||
129 | // /// Test loading a V0.1 OpenSim Inventory Archive (subject to change since there is no fixed format yet) where | ||
130 | // /// an account exists with the same name as the creator, though not the same id. | ||
131 | // /// </summary> | ||
132 | // [Test] | ||
133 | // public void TestLoadIarV0_1SameNameCreator() | ||
134 | // { | ||
135 | // TestHelpers.InMethod(); | ||
136 | // TestHelpers.EnableLogging(); | ||
137 | // | ||
138 | // UserAccountHelpers.CreateUserWithInventory(m_scene, m_uaMT, "meowfood"); | ||
139 | // UserAccountHelpers.CreateUserWithInventory(m_scene, m_uaLL2, "hampshire"); | ||
140 | // | ||
141 | // m_archiverModule.DearchiveInventory(m_uaMT.FirstName, m_uaMT.LastName, "/", "meowfood", m_iarStream); | ||
142 | // InventoryItemBase foundItem1 | ||
143 | // = InventoryArchiveUtils.FindItemByPath(m_scene.InventoryService, m_uaMT.PrincipalID, m_item1Name); | ||
144 | // | ||
145 | // Assert.That( | ||
146 | // foundItem1.CreatorId, Is.EqualTo(m_uaLL2.PrincipalID.ToString()), | ||
147 | // "Loaded item non-uuid creator doesn't match original"); | ||
148 | // Assert.That( | ||
149 | // foundItem1.CreatorIdAsUuid, Is.EqualTo(m_uaLL2.PrincipalID), | ||
150 | // "Loaded item uuid creator doesn't match original"); | ||
151 | // Assert.That(foundItem1.Owner, Is.EqualTo(m_uaMT.PrincipalID), | ||
152 | // "Loaded item owner doesn't match inventory reciever"); | ||
153 | // | ||
154 | // AssetBase asset1 = m_scene.AssetService.Get(foundItem1.AssetID.ToString()); | ||
155 | // string xmlData = Utils.BytesToString(asset1.Data); | ||
156 | // SceneObjectGroup sog1 = SceneObjectSerializer.FromOriginalXmlFormat(xmlData); | ||
157 | // | ||
158 | // Assert.That(sog1.RootPart.CreatorID, Is.EqualTo(m_uaLL2.PrincipalID)); | ||
159 | // } | ||
160 | |||
161 | /// <summary> | ||
162 | /// Test loading a V0.1 OpenSim Inventory Archive (subject to change since there is no fixed format yet) where | ||
163 | /// the creator or an account with the creator's name does not exist within the system. | ||
164 | /// </summary> | ||
165 | [Test] | ||
166 | public void TestLoadIarV0_1AbsentCreator() | ||
167 | { | ||
168 | TestHelpers.InMethod(); | ||
169 | // log4net.Config.XmlConfigurator.Configure(); | ||
170 | |||
171 | UserAccountHelpers.CreateUserWithInventory(m_scene, m_uaMT, "password"); | ||
172 | m_archiverModule.DearchiveInventory(UUID.Random(), m_uaMT.FirstName, m_uaMT.LastName, "/", "password", m_iarStream); | ||
173 | |||
174 | InventoryItemBase foundItem1 | ||
175 | = InventoryArchiveUtils.FindItemByPath(m_scene.InventoryService, m_uaMT.PrincipalID, m_item1Name); | ||
176 | |||
177 | Assert.That(foundItem1, Is.Not.Null, "Didn't find loaded item 1"); | ||
178 | Assert.That( | ||
179 | foundItem1.CreatorId, Is.EqualTo(m_uaMT.PrincipalID.ToString()), | ||
180 | "Loaded item non-uuid creator doesn't match that of the loading user"); | ||
181 | Assert.That( | ||
182 | foundItem1.CreatorIdAsUuid, Is.EqualTo(m_uaMT.PrincipalID), | ||
183 | "Loaded item uuid creator doesn't match that of the loading user"); | ||
184 | |||
185 | AssetBase asset1 = m_scene.AssetService.Get(foundItem1.AssetID.ToString()); | ||
186 | string xmlData = Utils.BytesToString(asset1.Data); | ||
187 | SceneObjectGroup sog1 = SceneObjectSerializer.FromOriginalXmlFormat(xmlData); | ||
188 | |||
189 | Assert.That(sog1.RootPart.CreatorID, Is.EqualTo(m_uaMT.PrincipalID)); | ||
190 | } | ||
191 | } | ||
192 | } \ No newline at end of file | ||
diff --git a/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/Tests/InventoryArchiverTests.cs b/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/Tests/InventoryArchiveSaveTests.cs index 06f6e49..7265405 100644 --- a/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/Tests/InventoryArchiverTests.cs +++ b/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/Tests/InventoryArchiveSaveTests.cs | |||
@@ -36,19 +36,17 @@ using OpenSim.Data; | |||
36 | using OpenSim.Framework; | 36 | using OpenSim.Framework; |
37 | using OpenSim.Framework.Serialization; | 37 | using OpenSim.Framework.Serialization; |
38 | using OpenSim.Framework.Serialization.External; | 38 | using OpenSim.Framework.Serialization.External; |
39 | using OpenSim.Framework.Communications; | ||
40 | using OpenSim.Region.CoreModules.Avatar.Inventory.Archiver; | 39 | using OpenSim.Region.CoreModules.Avatar.Inventory.Archiver; |
41 | using OpenSim.Region.CoreModules.World.Serialiser; | 40 | using OpenSim.Region.CoreModules.World.Serialiser; |
42 | using OpenSim.Region.Framework.Scenes; | 41 | using OpenSim.Region.Framework.Scenes; |
43 | using OpenSim.Region.Framework.Scenes.Serialization; | 42 | using OpenSim.Region.Framework.Scenes.Serialization; |
44 | using OpenSim.Services.Interfaces; | 43 | using OpenSim.Services.Interfaces; |
45 | using OpenSim.Tests.Common; | 44 | using OpenSim.Tests.Common; |
46 | using OpenSim.Tests.Common.Mock; | ||
47 | 45 | ||
48 | namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver.Tests | 46 | namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver.Tests |
49 | { | 47 | { |
50 | [TestFixture] | 48 | [TestFixture] |
51 | public class InventoryArchiverTests : InventoryArchiveTestCase | 49 | public class InventoryArchiveSaveTests : InventoryArchiveTestCase |
52 | { | 50 | { |
53 | protected TestScene m_scene; | 51 | protected TestScene m_scene; |
54 | protected InventoryArchiverModule m_archiverModule; | 52 | protected InventoryArchiverModule m_archiverModule; |
@@ -64,36 +62,6 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver.Tests | |||
64 | m_scene = new SceneHelpers().SetupScene(); | 62 | m_scene = new SceneHelpers().SetupScene(); |
65 | SceneHelpers.SetupSceneModules(m_scene, serialiserModule, m_archiverModule); | 63 | SceneHelpers.SetupSceneModules(m_scene, serialiserModule, m_archiverModule); |
66 | } | 64 | } |
67 | |||
68 | [Test] | ||
69 | public void TestLoadCoalesecedItem() | ||
70 | { | ||
71 | TestHelpers.InMethod(); | ||
72 | // TestHelpers.EnableLogging(); | ||
73 | |||
74 | UserAccountHelpers.CreateUserWithInventory(m_scene, m_uaLL1, "password"); | ||
75 | m_archiverModule.DearchiveInventory(m_uaLL1.FirstName, m_uaLL1.LastName, "/", "password", m_iarStream); | ||
76 | |||
77 | InventoryItemBase coaItem | ||
78 | = InventoryArchiveUtils.FindItemByPath(m_scene.InventoryService, m_uaLL1.PrincipalID, m_coaItemName); | ||
79 | |||
80 | Assert.That(coaItem, Is.Not.Null, "Didn't find loaded item 1"); | ||
81 | |||
82 | string assetXml = AssetHelpers.ReadAssetAsString(m_scene.AssetService, coaItem.AssetID); | ||
83 | |||
84 | CoalescedSceneObjects coa; | ||
85 | bool readResult = CoalescedSceneObjectsSerializer.TryFromXml(assetXml, out coa); | ||
86 | |||
87 | Assert.That(readResult, Is.True); | ||
88 | Assert.That(coa.Count, Is.EqualTo(2)); | ||
89 | |||
90 | List<SceneObjectGroup> coaObjects = coa.Objects; | ||
91 | Assert.That(coaObjects[0].UUID, Is.EqualTo(UUID.Parse("00000000-0000-0000-0000-000000000120"))); | ||
92 | Assert.That(coaObjects[0].AbsolutePosition, Is.EqualTo(new Vector3(15, 30, 45))); | ||
93 | |||
94 | Assert.That(coaObjects[1].UUID, Is.EqualTo(UUID.Parse("00000000-0000-0000-0000-000000000140"))); | ||
95 | Assert.That(coaObjects[1].AbsolutePosition, Is.EqualTo(new Vector3(25, 50, 75))); | ||
96 | } | ||
97 | 65 | ||
98 | /// <summary> | 66 | /// <summary> |
99 | /// Test that the IAR has the required files in the right order. | 67 | /// Test that the IAR has the required files in the right order. |
@@ -115,12 +83,145 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver.Tests | |||
115 | byte[] data = tar.ReadEntry(out filePath, out tarEntryType); | 83 | byte[] data = tar.ReadEntry(out filePath, out tarEntryType); |
116 | Assert.That(filePath, Is.EqualTo(ArchiveConstants.CONTROL_FILE_PATH)); | 84 | Assert.That(filePath, Is.EqualTo(ArchiveConstants.CONTROL_FILE_PATH)); |
117 | 85 | ||
118 | InventoryArchiveReadRequest iarr | 86 | InventoryArchiveReadRequest iarr |
119 | = new InventoryArchiveReadRequest(null, null, null, (Stream)null, false); | 87 | = new InventoryArchiveReadRequest(UUID.Random(), null, null, null, null, null, null, (Stream)null, false); |
120 | iarr.LoadControlFile(filePath, data); | 88 | iarr.LoadControlFile(filePath, data); |
121 | 89 | ||
122 | Assert.That(iarr.ControlFileLoaded, Is.True); | 90 | Assert.That(iarr.ControlFileLoaded, Is.True); |
123 | } | 91 | } |
92 | |||
93 | [Test] | ||
94 | public void TestSaveRootFolderToIar() | ||
95 | { | ||
96 | TestHelpers.InMethod(); | ||
97 | // TestHelpers.EnableLogging(); | ||
98 | |||
99 | string userFirstName = "Jock"; | ||
100 | string userLastName = "Stirrup"; | ||
101 | string userPassword = "troll"; | ||
102 | UUID userId = TestHelpers.ParseTail(0x20); | ||
103 | |||
104 | UserAccountHelpers.CreateUserWithInventory(m_scene, userFirstName, userLastName, userId, userPassword); | ||
105 | |||
106 | MemoryStream archiveWriteStream = new MemoryStream(); | ||
107 | m_archiverModule.OnInventoryArchiveSaved += SaveCompleted; | ||
108 | |||
109 | mre.Reset(); | ||
110 | m_archiverModule.ArchiveInventory( | ||
111 | UUID.Random(), userFirstName, userLastName, "/", userPassword, archiveWriteStream); | ||
112 | mre.WaitOne(60000, false); | ||
113 | |||
114 | // Test created iar | ||
115 | byte[] archive = archiveWriteStream.ToArray(); | ||
116 | MemoryStream archiveReadStream = new MemoryStream(archive); | ||
117 | TarArchiveReader tar = new TarArchiveReader(archiveReadStream); | ||
118 | |||
119 | // InventoryArchiveUtils. | ||
120 | bool gotObjectsFolder = false; | ||
121 | |||
122 | string objectsFolderName | ||
123 | = string.Format( | ||
124 | "{0}{1}", | ||
125 | ArchiveConstants.INVENTORY_PATH, | ||
126 | InventoryArchiveWriteRequest.CreateArchiveFolderName( | ||
127 | UserInventoryHelpers.GetInventoryFolder(m_scene.InventoryService, userId, "Objects"))); | ||
128 | |||
129 | string filePath; | ||
130 | TarArchiveReader.TarEntryType tarEntryType; | ||
131 | |||
132 | while (tar.ReadEntry(out filePath, out tarEntryType) != null) | ||
133 | { | ||
134 | // Console.WriteLine("Got {0}", filePath); | ||
135 | |||
136 | // Lazily, we only bother to look for the system objects folder created when we call CreateUserWithInventory() | ||
137 | // XXX: But really we need to stop all that stuff being created in tests or check for such folders | ||
138 | // more thoroughly | ||
139 | if (filePath == objectsFolderName) | ||
140 | gotObjectsFolder = true; | ||
141 | } | ||
142 | |||
143 | Assert.That(gotObjectsFolder, Is.True); | ||
144 | } | ||
145 | |||
146 | [Test] | ||
147 | public void TestSaveNonRootFolderToIar() | ||
148 | { | ||
149 | TestHelpers.InMethod(); | ||
150 | // TestHelpers.EnableLogging(); | ||
151 | |||
152 | string userFirstName = "Jock"; | ||
153 | string userLastName = "Stirrup"; | ||
154 | string userPassword = "troll"; | ||
155 | UUID userId = TestHelpers.ParseTail(0x20); | ||
156 | |||
157 | UserAccountHelpers.CreateUserWithInventory(m_scene, userFirstName, userLastName, userId, userPassword); | ||
158 | |||
159 | // Create base folder | ||
160 | InventoryFolderBase f1 | ||
161 | = UserInventoryHelpers.CreateInventoryFolder(m_scene.InventoryService, userId, "f1", true); | ||
162 | |||
163 | // Create item1 | ||
164 | SceneObjectGroup so1 = SceneHelpers.CreateSceneObject(1, userId, "My Little Dog Object", 0x5); | ||
165 | InventoryItemBase i1 = UserInventoryHelpers.AddInventoryItem(m_scene, so1, 0x50, 0x60, "f1"); | ||
166 | |||
167 | // Create embedded folder | ||
168 | InventoryFolderBase f1_1 | ||
169 | = UserInventoryHelpers.CreateInventoryFolder(m_scene.InventoryService, userId, "f1/f1.1", true); | ||
170 | |||
171 | // Create embedded item | ||
172 | SceneObjectGroup so1_1 = SceneHelpers.CreateSceneObject(1, userId, "My Little Cat Object", 0x6); | ||
173 | InventoryItemBase i2 = UserInventoryHelpers.AddInventoryItem(m_scene, so1_1, 0x500, 0x600, "f1/f1.1"); | ||
174 | |||
175 | MemoryStream archiveWriteStream = new MemoryStream(); | ||
176 | m_archiverModule.OnInventoryArchiveSaved += SaveCompleted; | ||
177 | |||
178 | mre.Reset(); | ||
179 | m_archiverModule.ArchiveInventory( | ||
180 | UUID.Random(), userFirstName, userLastName, "f1", userPassword, archiveWriteStream); | ||
181 | mre.WaitOne(60000, false); | ||
182 | |||
183 | // Test created iar | ||
184 | byte[] archive = archiveWriteStream.ToArray(); | ||
185 | MemoryStream archiveReadStream = new MemoryStream(archive); | ||
186 | TarArchiveReader tar = new TarArchiveReader(archiveReadStream); | ||
187 | |||
188 | // InventoryArchiveUtils. | ||
189 | bool gotf1 = false, gotf1_1 = false, gotso1 = false, gotso2 = false; | ||
190 | |||
191 | string f1FileName | ||
192 | = string.Format("{0}{1}", ArchiveConstants.INVENTORY_PATH, InventoryArchiveWriteRequest.CreateArchiveFolderName(f1)); | ||
193 | string f1_1FileName | ||
194 | = string.Format("{0}{1}", f1FileName, InventoryArchiveWriteRequest.CreateArchiveFolderName(f1_1)); | ||
195 | string so1FileName | ||
196 | = string.Format("{0}{1}", f1FileName, InventoryArchiveWriteRequest.CreateArchiveItemName(i1)); | ||
197 | string so2FileName | ||
198 | = string.Format("{0}{1}", f1_1FileName, InventoryArchiveWriteRequest.CreateArchiveItemName(i2)); | ||
199 | |||
200 | string filePath; | ||
201 | TarArchiveReader.TarEntryType tarEntryType; | ||
202 | |||
203 | while (tar.ReadEntry(out filePath, out tarEntryType) != null) | ||
204 | { | ||
205 | // Console.WriteLine("Got {0}", filePath); | ||
206 | |||
207 | if (filePath == f1FileName) | ||
208 | gotf1 = true; | ||
209 | else if (filePath == f1_1FileName) | ||
210 | gotf1_1 = true; | ||
211 | else if (filePath == so1FileName) | ||
212 | gotso1 = true; | ||
213 | else if (filePath == so2FileName) | ||
214 | gotso2 = true; | ||
215 | } | ||
216 | |||
217 | // Assert.That(gotControlFile, Is.True, "No control file in archive"); | ||
218 | Assert.That(gotf1, Is.True); | ||
219 | Assert.That(gotf1_1, Is.True); | ||
220 | Assert.That(gotso1, Is.True); | ||
221 | Assert.That(gotso2, Is.True); | ||
222 | |||
223 | // TODO: Test presence of more files and contents of files. | ||
224 | } | ||
124 | 225 | ||
125 | /// <summary> | 226 | /// <summary> |
126 | /// Test saving a single inventory item to an IAR | 227 | /// Test saving a single inventory item to an IAR |
@@ -155,7 +256,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver.Tests | |||
155 | item1.AssetID = asset1.FullID; | 256 | item1.AssetID = asset1.FullID; |
156 | item1.ID = item1Id; | 257 | item1.ID = item1Id; |
157 | InventoryFolderBase objsFolder | 258 | InventoryFolderBase objsFolder |
158 | = InventoryArchiveUtils.FindFolderByPath(m_scene.InventoryService, userId, "Objects")[0]; | 259 | = InventoryArchiveUtils.FindFoldersByPath(m_scene.InventoryService, userId, "Objects")[0]; |
159 | item1.Folder = objsFolder.ID; | 260 | item1.Folder = objsFolder.ID; |
160 | m_scene.AddInventoryItem(item1); | 261 | m_scene.AddInventoryItem(item1); |
161 | 262 | ||
@@ -164,7 +265,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver.Tests | |||
164 | 265 | ||
165 | mre.Reset(); | 266 | mre.Reset(); |
166 | m_archiverModule.ArchiveInventory( | 267 | m_archiverModule.ArchiveInventory( |
167 | Guid.NewGuid(), userFirstName, userLastName, "Objects/" + item1Name, userPassword, archiveWriteStream); | 268 | UUID.Random(), userFirstName, userLastName, "Objects/" + item1Name, userPassword, archiveWriteStream); |
168 | mre.WaitOne(60000, false); | 269 | mre.WaitOne(60000, false); |
169 | 270 | ||
170 | byte[] archive = archiveWriteStream.ToArray(); | 271 | byte[] archive = archiveWriteStream.ToArray(); |
@@ -250,7 +351,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver.Tests | |||
250 | item1.AssetID = asset1.FullID; | 351 | item1.AssetID = asset1.FullID; |
251 | item1.ID = item1Id; | 352 | item1.ID = item1Id; |
252 | InventoryFolderBase objsFolder | 353 | InventoryFolderBase objsFolder |
253 | = InventoryArchiveUtils.FindFolderByPath(m_scene.InventoryService, userId, "Objects")[0]; | 354 | = InventoryArchiveUtils.FindFoldersByPath(m_scene.InventoryService, userId, "Objects")[0]; |
254 | item1.Folder = objsFolder.ID; | 355 | item1.Folder = objsFolder.ID; |
255 | m_scene.AddInventoryItem(item1); | 356 | m_scene.AddInventoryItem(item1); |
256 | 357 | ||
@@ -261,7 +362,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver.Tests | |||
261 | 362 | ||
262 | // When we're not saving assets, archiving is being done synchronously. | 363 | // When we're not saving assets, archiving is being done synchronously. |
263 | m_archiverModule.ArchiveInventory( | 364 | m_archiverModule.ArchiveInventory( |
264 | Guid.NewGuid(), userFirstName, userLastName, "Objects/" + item1Name, userPassword, archiveWriteStream, options); | 365 | UUID.Random(), userFirstName, userLastName, "Objects/" + item1Name, userPassword, archiveWriteStream, options); |
265 | 366 | ||
266 | byte[] archive = archiveWriteStream.ToArray(); | 367 | byte[] archive = archiveWriteStream.ToArray(); |
267 | MemoryStream archiveReadStream = new MemoryStream(archive); | 368 | MemoryStream archiveReadStream = new MemoryStream(archive); |
@@ -317,101 +418,5 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver.Tests | |||
317 | 418 | ||
318 | // TODO: Test presence of more files and contents of files. | 419 | // TODO: Test presence of more files and contents of files. |
319 | } | 420 | } |
320 | |||
321 | /// <summary> | ||
322 | /// Test case where a creator account exists for the creator UUID embedded in item metadata and serialized | ||
323 | /// objects. | ||
324 | /// </summary> | ||
325 | [Test] | ||
326 | public void TestLoadIarCreatorAccountPresent() | ||
327 | { | ||
328 | TestHelpers.InMethod(); | ||
329 | // log4net.Config.XmlConfigurator.Configure(); | ||
330 | |||
331 | UserAccountHelpers.CreateUserWithInventory(m_scene, m_uaLL1, "meowfood"); | ||
332 | |||
333 | m_archiverModule.DearchiveInventory(m_uaLL1.FirstName, m_uaLL1.LastName, "/", "meowfood", m_iarStream); | ||
334 | InventoryItemBase foundItem1 | ||
335 | = InventoryArchiveUtils.FindItemByPath(m_scene.InventoryService, m_uaLL1.PrincipalID, m_item1Name); | ||
336 | |||
337 | Assert.That( | ||
338 | foundItem1.CreatorId, Is.EqualTo(m_uaLL1.PrincipalID.ToString()), | ||
339 | "Loaded item non-uuid creator doesn't match original"); | ||
340 | Assert.That( | ||
341 | foundItem1.CreatorIdAsUuid, Is.EqualTo(m_uaLL1.PrincipalID), | ||
342 | "Loaded item uuid creator doesn't match original"); | ||
343 | Assert.That(foundItem1.Owner, Is.EqualTo(m_uaLL1.PrincipalID), | ||
344 | "Loaded item owner doesn't match inventory reciever"); | ||
345 | |||
346 | AssetBase asset1 = m_scene.AssetService.Get(foundItem1.AssetID.ToString()); | ||
347 | string xmlData = Utils.BytesToString(asset1.Data); | ||
348 | SceneObjectGroup sog1 = SceneObjectSerializer.FromOriginalXmlFormat(xmlData); | ||
349 | |||
350 | Assert.That(sog1.RootPart.CreatorID, Is.EqualTo(m_uaLL1.PrincipalID)); | ||
351 | } | ||
352 | |||
353 | // /// <summary> | ||
354 | // /// Test loading a V0.1 OpenSim Inventory Archive (subject to change since there is no fixed format yet) where | ||
355 | // /// an account exists with the same name as the creator, though not the same id. | ||
356 | // /// </summary> | ||
357 | // [Test] | ||
358 | // public void TestLoadIarV0_1SameNameCreator() | ||
359 | // { | ||
360 | // TestHelpers.InMethod(); | ||
361 | // TestHelpers.EnableLogging(); | ||
362 | // | ||
363 | // UserAccountHelpers.CreateUserWithInventory(m_scene, m_uaMT, "meowfood"); | ||
364 | // UserAccountHelpers.CreateUserWithInventory(m_scene, m_uaLL2, "hampshire"); | ||
365 | // | ||
366 | // m_archiverModule.DearchiveInventory(m_uaMT.FirstName, m_uaMT.LastName, "/", "meowfood", m_iarStream); | ||
367 | // InventoryItemBase foundItem1 | ||
368 | // = InventoryArchiveUtils.FindItemByPath(m_scene.InventoryService, m_uaMT.PrincipalID, m_item1Name); | ||
369 | // | ||
370 | // Assert.That( | ||
371 | // foundItem1.CreatorId, Is.EqualTo(m_uaLL2.PrincipalID.ToString()), | ||
372 | // "Loaded item non-uuid creator doesn't match original"); | ||
373 | // Assert.That( | ||
374 | // foundItem1.CreatorIdAsUuid, Is.EqualTo(m_uaLL2.PrincipalID), | ||
375 | // "Loaded item uuid creator doesn't match original"); | ||
376 | // Assert.That(foundItem1.Owner, Is.EqualTo(m_uaMT.PrincipalID), | ||
377 | // "Loaded item owner doesn't match inventory reciever"); | ||
378 | // | ||
379 | // AssetBase asset1 = m_scene.AssetService.Get(foundItem1.AssetID.ToString()); | ||
380 | // string xmlData = Utils.BytesToString(asset1.Data); | ||
381 | // SceneObjectGroup sog1 = SceneObjectSerializer.FromOriginalXmlFormat(xmlData); | ||
382 | // | ||
383 | // Assert.That(sog1.RootPart.CreatorID, Is.EqualTo(m_uaLL2.PrincipalID)); | ||
384 | // } | ||
385 | |||
386 | /// <summary> | ||
387 | /// Test loading a V0.1 OpenSim Inventory Archive (subject to change since there is no fixed format yet) where | ||
388 | /// the creator or an account with the creator's name does not exist within the system. | ||
389 | /// </summary> | ||
390 | [Test] | ||
391 | public void TestLoadIarV0_1AbsentCreator() | ||
392 | { | ||
393 | TestHelpers.InMethod(); | ||
394 | // log4net.Config.XmlConfigurator.Configure(); | ||
395 | |||
396 | UserAccountHelpers.CreateUserWithInventory(m_scene, m_uaMT, "password"); | ||
397 | m_archiverModule.DearchiveInventory(m_uaMT.FirstName, m_uaMT.LastName, "/", "password", m_iarStream); | ||
398 | |||
399 | InventoryItemBase foundItem1 | ||
400 | = InventoryArchiveUtils.FindItemByPath(m_scene.InventoryService, m_uaMT.PrincipalID, m_item1Name); | ||
401 | |||
402 | Assert.That(foundItem1, Is.Not.Null, "Didn't find loaded item 1"); | ||
403 | Assert.That( | ||
404 | foundItem1.CreatorId, Is.EqualTo(m_uaMT.PrincipalID.ToString()), | ||
405 | "Loaded item non-uuid creator doesn't match that of the loading user"); | ||
406 | Assert.That( | ||
407 | foundItem1.CreatorIdAsUuid, Is.EqualTo(m_uaMT.PrincipalID), | ||
408 | "Loaded item uuid creator doesn't match that of the loading user"); | ||
409 | |||
410 | AssetBase asset1 = m_scene.AssetService.Get(foundItem1.AssetID.ToString()); | ||
411 | string xmlData = Utils.BytesToString(asset1.Data); | ||
412 | SceneObjectGroup sog1 = SceneObjectSerializer.FromOriginalXmlFormat(xmlData); | ||
413 | |||
414 | Assert.That(sog1.RootPart.CreatorID, Is.EqualTo(m_uaMT.PrincipalID)); | ||
415 | } | ||
416 | } | 421 | } |
417 | } \ No newline at end of file | 422 | } \ No newline at end of file |
diff --git a/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/Tests/InventoryArchiveTestCase.cs b/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/Tests/InventoryArchiveTestCase.cs index db78da9..519c697 100644 --- a/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/Tests/InventoryArchiveTestCase.cs +++ b/OpenSim/Region/CoreModules/Avatar/Inventory/Archiver/Tests/InventoryArchiveTestCase.cs | |||
@@ -36,14 +36,12 @@ using OpenSim.Data; | |||
36 | using OpenSim.Framework; | 36 | using OpenSim.Framework; |
37 | using OpenSim.Framework.Serialization; | 37 | using OpenSim.Framework.Serialization; |
38 | using OpenSim.Framework.Serialization.External; | 38 | using OpenSim.Framework.Serialization.External; |
39 | using OpenSim.Framework.Communications; | ||
40 | using OpenSim.Region.CoreModules.Avatar.Inventory.Archiver; | 39 | using OpenSim.Region.CoreModules.Avatar.Inventory.Archiver; |
41 | using OpenSim.Region.CoreModules.World.Serialiser; | 40 | using OpenSim.Region.CoreModules.World.Serialiser; |
42 | using OpenSim.Region.Framework.Scenes; | 41 | using OpenSim.Region.Framework.Scenes; |
43 | using OpenSim.Region.Framework.Scenes.Serialization; | 42 | using OpenSim.Region.Framework.Scenes.Serialization; |
44 | using OpenSim.Services.Interfaces; | 43 | using OpenSim.Services.Interfaces; |
45 | using OpenSim.Tests.Common; | 44 | using OpenSim.Tests.Common; |
46 | using OpenSim.Tests.Common.Mock; | ||
47 | 45 | ||
48 | namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver.Tests | 46 | namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver.Tests |
49 | { | 47 | { |
@@ -163,14 +161,14 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Archiver.Tests | |||
163 | scene.AddInventoryItem(coaItem); | 161 | scene.AddInventoryItem(coaItem); |
164 | 162 | ||
165 | archiverModule.ArchiveInventory( | 163 | archiverModule.ArchiveInventory( |
166 | Guid.NewGuid(), m_uaLL1.FirstName, m_uaLL1.LastName, "/*", "hampshire", archiveWriteStream); | 164 | UUID.Random(), m_uaLL1.FirstName, m_uaLL1.LastName, "/*", "hampshire", archiveWriteStream); |
167 | 165 | ||
168 | m_iarStreamBytes = archiveWriteStream.ToArray(); | 166 | m_iarStreamBytes = archiveWriteStream.ToArray(); |
169 | } | 167 | } |
170 | 168 | ||
171 | protected void SaveCompleted( | 169 | protected void SaveCompleted( |
172 | Guid id, bool succeeded, UserAccount userInfo, string invPath, Stream saveStream, | 170 | UUID id, bool succeeded, UserAccount userInfo, string invPath, Stream saveStream, |
173 | Exception reportedException) | 171 | Exception reportedException, int SaveCount, int FilterCount) |
174 | { | 172 | { |
175 | mre.Set(); | 173 | mre.Set(); |
176 | } | 174 | } |
diff --git a/OpenSim/Region/CoreModules/Avatar/Inventory/Transfer/InventoryTransferModule.cs b/OpenSim/Region/CoreModules/Avatar/Inventory/Transfer/InventoryTransferModule.cs index bcb7f42..bba48cc 100644 --- a/OpenSim/Region/CoreModules/Avatar/Inventory/Transfer/InventoryTransferModule.cs +++ b/OpenSim/Region/CoreModules/Avatar/Inventory/Transfer/InventoryTransferModule.cs | |||
@@ -47,10 +47,8 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Transfer | |||
47 | 47 | ||
48 | /// <summary> | 48 | /// <summary> |
49 | private List<Scene> m_Scenelist = new List<Scene>(); | 49 | private List<Scene> m_Scenelist = new List<Scene>(); |
50 | // private Dictionary<UUID, Scene> m_AgentRegions = | ||
51 | // new Dictionary<UUID, Scene>(); | ||
52 | 50 | ||
53 | private IMessageTransferModule m_TransferModule = null; | 51 | private IMessageTransferModule m_TransferModule; |
54 | private bool m_Enabled = true; | 52 | private bool m_Enabled = true; |
55 | 53 | ||
56 | #region Region Module interface | 54 | #region Region Module interface |
@@ -81,9 +79,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Transfer | |||
81 | // scene.RegisterModuleInterface<IInventoryTransferModule>(this); | 79 | // scene.RegisterModuleInterface<IInventoryTransferModule>(this); |
82 | 80 | ||
83 | scene.EventManager.OnNewClient += OnNewClient; | 81 | scene.EventManager.OnNewClient += OnNewClient; |
84 | // scene.EventManager.OnClientClosed += ClientLoggedOut; | ||
85 | scene.EventManager.OnIncomingInstantMessage += OnGridInstantMessage; | 82 | scene.EventManager.OnIncomingInstantMessage += OnGridInstantMessage; |
86 | // scene.EventManager.OnSetRootAgentScene += OnSetRootAgentScene; | ||
87 | } | 83 | } |
88 | 84 | ||
89 | public void RegionLoaded(Scene scene) | 85 | public void RegionLoaded(Scene scene) |
@@ -96,11 +92,9 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Transfer | |||
96 | m_log.Error("[INVENTORY TRANSFER]: No Message transfer module found, transfers will be local only"); | 92 | m_log.Error("[INVENTORY TRANSFER]: No Message transfer module found, transfers will be local only"); |
97 | m_Enabled = false; | 93 | m_Enabled = false; |
98 | 94 | ||
99 | m_Scenelist.Clear(); | 95 | // m_Scenelist.Clear(); |
100 | scene.EventManager.OnNewClient -= OnNewClient; | 96 | // scene.EventManager.OnNewClient -= OnNewClient; |
101 | // scene.EventManager.OnClientClosed -= ClientLoggedOut; | ||
102 | scene.EventManager.OnIncomingInstantMessage -= OnGridInstantMessage; | 97 | scene.EventManager.OnIncomingInstantMessage -= OnGridInstantMessage; |
103 | // scene.EventManager.OnSetRootAgentScene -= OnSetRootAgentScene; | ||
104 | } | 98 | } |
105 | } | 99 | } |
106 | } | 100 | } |
@@ -108,9 +102,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Transfer | |||
108 | public void RemoveRegion(Scene scene) | 102 | public void RemoveRegion(Scene scene) |
109 | { | 103 | { |
110 | scene.EventManager.OnNewClient -= OnNewClient; | 104 | scene.EventManager.OnNewClient -= OnNewClient; |
111 | // scene.EventManager.OnClientClosed -= ClientLoggedOut; | ||
112 | scene.EventManager.OnIncomingInstantMessage -= OnGridInstantMessage; | 105 | scene.EventManager.OnIncomingInstantMessage -= OnGridInstantMessage; |
113 | // scene.EventManager.OnSetRootAgentScene -= OnSetRootAgentScene; | ||
114 | m_Scenelist.Remove(scene); | 106 | m_Scenelist.Remove(scene); |
115 | } | 107 | } |
116 | 108 | ||
@@ -139,11 +131,6 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Transfer | |||
139 | // Inventory giving is conducted via instant message | 131 | // Inventory giving is conducted via instant message |
140 | client.OnInstantMessage += OnInstantMessage; | 132 | client.OnInstantMessage += OnInstantMessage; |
141 | } | 133 | } |
142 | |||
143 | // protected void OnSetRootAgentScene(UUID id, Scene scene) | ||
144 | // { | ||
145 | // m_AgentRegions[id] = scene; | ||
146 | // } | ||
147 | 134 | ||
148 | private Scene FindClientScene(UUID agentId) | 135 | private Scene FindClientScene(UUID agentId) |
149 | { | 136 | { |
@@ -162,8 +149,9 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Transfer | |||
162 | private void OnInstantMessage(IClientAPI client, GridInstantMessage im) | 149 | private void OnInstantMessage(IClientAPI client, GridInstantMessage im) |
163 | { | 150 | { |
164 | // m_log.DebugFormat( | 151 | // m_log.DebugFormat( |
165 | // "[INVENTORY TRANSFER]: {0} IM type received from {1}", | 152 | // "[INVENTORY TRANSFER]: {0} IM type received from client {1}. From={2} ({3}), To={4}", |
166 | // (InstantMessageDialog)im.dialog, client.Name); | 153 | // (InstantMessageDialog)im.dialog, client.Name, |
154 | // im.fromAgentID, im.fromAgentName, im.toAgentID); | ||
167 | 155 | ||
168 | Scene scene = FindClientScene(client.AgentId); | 156 | Scene scene = FindClientScene(client.AgentId); |
169 | 157 | ||
@@ -188,12 +176,12 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Transfer | |||
188 | { | 176 | { |
189 | UUID folderID = new UUID(im.binaryBucket, 1); | 177 | UUID folderID = new UUID(im.binaryBucket, 1); |
190 | 178 | ||
191 | m_log.DebugFormat("[INVENTORY TRANSFER]: Inserting original folder {0} "+ | 179 | m_log.DebugFormat( |
192 | "into agent {1}'s inventory", | 180 | "[INVENTORY TRANSFER]: Inserting original folder {0} into agent {1}'s inventory", |
193 | folderID, new UUID(im.toAgentID)); | 181 | folderID, new UUID(im.toAgentID)); |
194 | 182 | ||
195 | InventoryFolderBase folderCopy | 183 | InventoryFolderBase folderCopy |
196 | = scene.GiveInventoryFolder(receipientID, client.AgentId, folderID, UUID.Zero); | 184 | = scene.GiveInventoryFolder(client, receipientID, client.AgentId, folderID, UUID.Zero); |
197 | 185 | ||
198 | if (folderCopy == null) | 186 | if (folderCopy == null) |
199 | { | 187 | { |
@@ -213,7 +201,10 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Transfer | |||
213 | user.ControllingClient.SendBulkUpdateInventory(folderCopy); | 201 | user.ControllingClient.SendBulkUpdateInventory(folderCopy); |
214 | 202 | ||
215 | // HACK!! | 203 | // HACK!! |
216 | im.imSessionID = folderID.Guid; | 204 | // Insert the ID of the copied folder into the IM so that we know which item to move to trash if it |
205 | // is rejected. | ||
206 | // XXX: This is probably a misuse of the session ID slot. | ||
207 | im.imSessionID = copyID.Guid; | ||
217 | } | 208 | } |
218 | else | 209 | else |
219 | { | 210 | { |
@@ -226,13 +217,12 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Transfer | |||
226 | "into agent {1}'s inventory", | 217 | "into agent {1}'s inventory", |
227 | itemID, new UUID(im.toAgentID)); | 218 | itemID, new UUID(im.toAgentID)); |
228 | 219 | ||
229 | InventoryItemBase itemCopy = scene.GiveInventoryItem( | 220 | string message; |
230 | new UUID(im.toAgentID), | 221 | InventoryItemBase itemCopy = scene.GiveInventoryItem(new UUID(im.toAgentID), client.AgentId, itemID, out message); |
231 | client.AgentId, itemID); | ||
232 | 222 | ||
233 | if (itemCopy == null) | 223 | if (itemCopy == null) |
234 | { | 224 | { |
235 | client.SendAgentAlertMessage("Can't find item to give. Nothing given.", false); | 225 | client.SendAgentAlertMessage(message, false); |
236 | return; | 226 | return; |
237 | } | 227 | } |
238 | 228 | ||
@@ -243,7 +233,10 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Transfer | |||
243 | user.ControllingClient.SendBulkUpdateInventory(itemCopy); | 233 | user.ControllingClient.SendBulkUpdateInventory(itemCopy); |
244 | 234 | ||
245 | // HACK!! | 235 | // HACK!! |
246 | im.imSessionID = itemID.Guid; | 236 | // Insert the ID of the copied item into the IM so that we know which item to move to trash if it |
237 | // is rejected. | ||
238 | // XXX: This is probably a misuse of the session ID slot. | ||
239 | im.imSessionID = copyID.Guid; | ||
247 | } | 240 | } |
248 | 241 | ||
249 | // Send the IM to the recipient. The item is already | 242 | // Send the IM to the recipient. The item is already |
@@ -379,7 +372,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Transfer | |||
379 | IInventoryService invService = scene.InventoryService; | 372 | IInventoryService invService = scene.InventoryService; |
380 | 373 | ||
381 | InventoryFolderBase trashFolder = | 374 | InventoryFolderBase trashFolder = |
382 | invService.GetFolderForType(client.AgentId, AssetType.TrashFolder); | 375 | invService.GetFolderForType(client.AgentId, FolderType.Trash); |
383 | 376 | ||
384 | UUID inventoryID = new UUID(im.imSessionID); // The inventory item/folder, back from it's trip | 377 | UUID inventoryID = new UUID(im.imSessionID); // The inventory item/folder, back from it's trip |
385 | 378 | ||
@@ -403,7 +396,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Transfer | |||
403 | { | 396 | { |
404 | folder = new InventoryFolderBase(inventoryID, client.AgentId); | 397 | folder = new InventoryFolderBase(inventoryID, client.AgentId); |
405 | folder = invService.GetFolder(folder); | 398 | folder = invService.GetFolder(folder); |
406 | 399 | ||
407 | if (folder != null & trashFolder != null) | 400 | if (folder != null & trashFolder != null) |
408 | { | 401 | { |
409 | previousParentFolderID = folder.ParentID; | 402 | previousParentFolderID = folder.ParentID; |
@@ -454,90 +447,61 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Transfer | |||
454 | } | 447 | } |
455 | } | 448 | } |
456 | 449 | ||
457 | // public bool NeedSceneCacheClear(UUID agentID, Scene scene) | ||
458 | // { | ||
459 | // if (!m_AgentRegions.ContainsKey(agentID)) | ||
460 | // { | ||
461 | // // Since we can get here two ways, we need to scan | ||
462 | // // the scenes here. This is somewhat more expensive | ||
463 | // // but helps avoid a nasty bug | ||
464 | // // | ||
465 | // | ||
466 | // foreach (Scene s in m_Scenelist) | ||
467 | // { | ||
468 | // ScenePresence presence; | ||
469 | // | ||
470 | // if (s.TryGetScenePresence(agentID, out presence)) | ||
471 | // { | ||
472 | // // If the agent is in this scene, then we | ||
473 | // // are being called twice in a single | ||
474 | // // teleport. This is wasteful of cycles | ||
475 | // // but harmless due to this 2nd level check | ||
476 | // // | ||
477 | // // If the agent is found in another scene | ||
478 | // // then the list wasn't current | ||
479 | // // | ||
480 | // // If the agent is totally unknown, then what | ||
481 | // // are we even doing here?? | ||
482 | // // | ||
483 | // if (s == scene) | ||
484 | // { | ||
485 | // //m_log.Debug("[INVTRANSFERMOD]: s == scene. Returning true in " + scene.RegionInfo.RegionName); | ||
486 | // return true; | ||
487 | // } | ||
488 | // else | ||
489 | // { | ||
490 | // //m_log.Debug("[INVTRANSFERMOD]: s != scene. Returning false in " + scene.RegionInfo.RegionName); | ||
491 | // return false; | ||
492 | // } | ||
493 | // } | ||
494 | // } | ||
495 | // //m_log.Debug("[INVTRANSFERMOD]: agent not in scene. Returning true in " + scene.RegionInfo.RegionName); | ||
496 | // return true; | ||
497 | // } | ||
498 | // | ||
499 | // // The agent is left in current Scene, so we must be | ||
500 | // // going to another instance | ||
501 | // // | ||
502 | // if (m_AgentRegions[agentID] == scene) | ||
503 | // { | ||
504 | // //m_log.Debug("[INVTRANSFERMOD]: m_AgentRegions[agentID] == scene. Returning true in " + scene.RegionInfo.RegionName); | ||
505 | // m_AgentRegions.Remove(agentID); | ||
506 | // return true; | ||
507 | // } | ||
508 | // | ||
509 | // // Another region has claimed the agent | ||
510 | // // | ||
511 | // //m_log.Debug("[INVTRANSFERMOD]: last resort. Returning false in " + scene.RegionInfo.RegionName); | ||
512 | // return false; | ||
513 | // } | ||
514 | // | ||
515 | // public void ClientLoggedOut(UUID agentID, Scene scene) | ||
516 | // { | ||
517 | // if (m_AgentRegions.ContainsKey(agentID)) | ||
518 | // m_AgentRegions.Remove(agentID); | ||
519 | // } | ||
520 | |||
521 | /// <summary> | 450 | /// <summary> |
522 | /// | 451 | /// |
523 | /// </summary> | 452 | /// </summary> |
524 | /// <param name="msg"></param> | 453 | /// <param name="im"></param> |
525 | private void OnGridInstantMessage(GridInstantMessage msg) | 454 | private void OnGridInstantMessage(GridInstantMessage im) |
526 | { | 455 | { |
456 | // Check if it's a type of message that we should handle | ||
457 | if (!((im.dialog == (byte) InstantMessageDialog.InventoryOffered) | ||
458 | || (im.dialog == (byte) InstantMessageDialog.TaskInventoryOffered) | ||
459 | || (im.dialog == (byte) InstantMessageDialog.InventoryAccepted) | ||
460 | || (im.dialog == (byte) InstantMessageDialog.InventoryDeclined) | ||
461 | || (im.dialog == (byte) InstantMessageDialog.TaskInventoryDeclined))) | ||
462 | return; | ||
463 | |||
464 | m_log.DebugFormat( | ||
465 | "[INVENTORY TRANSFER]: {0} IM type received from grid. From={1} ({2}), To={3}", | ||
466 | (InstantMessageDialog)im.dialog, im.fromAgentID, im.fromAgentName, im.toAgentID); | ||
467 | |||
527 | // Check if this is ours to handle | 468 | // Check if this is ours to handle |
528 | // | 469 | // |
529 | Scene scene = FindClientScene(new UUID(msg.toAgentID)); | 470 | Scene scene = FindClientScene(new UUID(im.toAgentID)); |
530 | 471 | ||
531 | if (scene == null) | 472 | if (scene == null) |
532 | return; | 473 | return; |
533 | 474 | ||
534 | // Find agent to deliver to | 475 | // Find agent to deliver to |
535 | // | 476 | // |
536 | ScenePresence user = scene.GetScenePresence(new UUID(msg.toAgentID)); | 477 | ScenePresence user = scene.GetScenePresence(new UUID(im.toAgentID)); |
537 | 478 | ||
538 | // Just forward to local handling | 479 | if (user != null) |
539 | OnInstantMessage(user.ControllingClient, msg); | 480 | { |
481 | user.ControllingClient.SendInstantMessage(im); | ||
482 | |||
483 | if (im.dialog == (byte)InstantMessageDialog.InventoryOffered) | ||
484 | { | ||
485 | AssetType assetType = (AssetType)im.binaryBucket[0]; | ||
486 | UUID inventoryID = new UUID(im.binaryBucket, 1); | ||
487 | |||
488 | IInventoryService invService = scene.InventoryService; | ||
489 | InventoryNodeBase node = null; | ||
490 | if (AssetType.Folder == assetType) | ||
491 | { | ||
492 | InventoryFolderBase folder = new InventoryFolderBase(inventoryID, new UUID(im.toAgentID)); | ||
493 | node = invService.GetFolder(folder); | ||
494 | } | ||
495 | else | ||
496 | { | ||
497 | InventoryItemBase item = new InventoryItemBase(inventoryID, new UUID(im.toAgentID)); | ||
498 | node = invService.GetItem(item); | ||
499 | } | ||
540 | 500 | ||
501 | if (node != null) | ||
502 | user.ControllingClient.SendBulkUpdateInventory(node); | ||
503 | } | ||
504 | } | ||
541 | } | 505 | } |
542 | } | 506 | } |
543 | } | 507 | } |
diff --git a/OpenSim/Region/CoreModules/Avatar/Inventory/Transfer/Tests/InventoryTransferModuleTests.cs b/OpenSim/Region/CoreModules/Avatar/Inventory/Transfer/Tests/InventoryTransferModuleTests.cs new file mode 100644 index 0000000..7ddc396 --- /dev/null +++ b/OpenSim/Region/CoreModules/Avatar/Inventory/Transfer/Tests/InventoryTransferModuleTests.cs | |||
@@ -0,0 +1,448 @@ | |||
1 | /* | ||
2 | * Copyright (c) Contributors, http://opensimulator.org/ | ||
3 | * See CONTRIBUTORS.TXT for a full list of copyright holders. | ||
4 | * | ||
5 | * Redistribution and use in source and binary forms, with or without | ||
6 | * modification, are permitted provided that the following conditions are met: | ||
7 | * * Redistributions of source code must retain the above copyright | ||
8 | * notice, this list of conditions and the following disclaimer. | ||
9 | * * Redistributions in binary form must reproduce the above copyright | ||
10 | * notice, this list of conditions and the following disclaimer in the | ||
11 | * documentation and/or other materials provided with the distribution. | ||
12 | * * Neither the name of the OpenSimulator Project nor the | ||
13 | * names of its contributors may be used to endorse or promote products | ||
14 | * derived from this software without specific prior written permission. | ||
15 | * | ||
16 | * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY | ||
17 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||
18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
19 | * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY | ||
20 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||
21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||
22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | ||
23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
26 | */ | ||
27 | |||
28 | using System; | ||
29 | using System.Collections.Generic; | ||
30 | using System.Reflection; | ||
31 | using log4net.Config; | ||
32 | using Nini.Config; | ||
33 | using NUnit.Framework; | ||
34 | using OpenMetaverse; | ||
35 | using OpenMetaverse.Assets; | ||
36 | using OpenSim.Framework; | ||
37 | using OpenSim.Region.CoreModules.Avatar.Inventory.Transfer; | ||
38 | using OpenSim.Region.Framework.Interfaces; | ||
39 | using OpenSim.Region.Framework.Scenes; | ||
40 | using OpenSim.Services.Interfaces; | ||
41 | using OpenSim.Tests.Common; | ||
42 | |||
43 | namespace OpenSim.Region.CoreModules.Avatar.Inventory.Transfer.Tests | ||
44 | { | ||
45 | [TestFixture] | ||
46 | public class InventoryTransferModuleTests : OpenSimTestCase | ||
47 | { | ||
48 | protected TestScene m_scene; | ||
49 | |||
50 | [SetUp] | ||
51 | public override void SetUp() | ||
52 | { | ||
53 | base.SetUp(); | ||
54 | |||
55 | IConfigSource config = new IniConfigSource(); | ||
56 | config.AddConfig("Messaging"); | ||
57 | config.Configs["Messaging"].Set("InventoryTransferModule", "InventoryTransferModule"); | ||
58 | |||
59 | m_scene = new SceneHelpers().SetupScene(); | ||
60 | SceneHelpers.SetupSceneModules(m_scene, config, new InventoryTransferModule()); | ||
61 | } | ||
62 | |||
63 | [Test] | ||
64 | public void TestAcceptGivenItem() | ||
65 | { | ||
66 | // TestHelpers.EnableLogging(); | ||
67 | |||
68 | UUID initialSessionId = TestHelpers.ParseTail(0x10); | ||
69 | UUID itemId = TestHelpers.ParseTail(0x100); | ||
70 | UUID assetId = TestHelpers.ParseTail(0x200); | ||
71 | |||
72 | UserAccount ua1 | ||
73 | = UserAccountHelpers.CreateUserWithInventory(m_scene, "User", "One", TestHelpers.ParseTail(0x1), "pw"); | ||
74 | UserAccount ua2 | ||
75 | = UserAccountHelpers.CreateUserWithInventory(m_scene, "User", "Two", TestHelpers.ParseTail(0x2), "pw"); | ||
76 | |||
77 | ScenePresence giverSp = SceneHelpers.AddScenePresence(m_scene, ua1); | ||
78 | TestClient giverClient = (TestClient)giverSp.ControllingClient; | ||
79 | |||
80 | ScenePresence receiverSp = SceneHelpers.AddScenePresence(m_scene, ua2); | ||
81 | TestClient receiverClient = (TestClient)receiverSp.ControllingClient; | ||
82 | |||
83 | // Create the object to test give | ||
84 | InventoryItemBase originalItem | ||
85 | = UserInventoryHelpers.CreateInventoryItem( | ||
86 | m_scene, "givenObj", itemId, assetId, giverSp.UUID, InventoryType.Object); | ||
87 | |||
88 | byte[] giveImBinaryBucket = new byte[17]; | ||
89 | byte[] itemIdBytes = itemId.GetBytes(); | ||
90 | Array.Copy(itemIdBytes, 0, giveImBinaryBucket, 1, itemIdBytes.Length); | ||
91 | |||
92 | GridInstantMessage giveIm | ||
93 | = new GridInstantMessage( | ||
94 | m_scene, | ||
95 | giverSp.UUID, | ||
96 | giverSp.Name, | ||
97 | receiverSp.UUID, | ||
98 | (byte)InstantMessageDialog.InventoryOffered, | ||
99 | false, | ||
100 | "inventory offered msg", | ||
101 | initialSessionId, | ||
102 | false, | ||
103 | Vector3.Zero, | ||
104 | giveImBinaryBucket, | ||
105 | true); | ||
106 | |||
107 | giverClient.HandleImprovedInstantMessage(giveIm); | ||
108 | |||
109 | // These details might not all be correct. | ||
110 | GridInstantMessage acceptIm | ||
111 | = new GridInstantMessage( | ||
112 | m_scene, | ||
113 | receiverSp.UUID, | ||
114 | receiverSp.Name, | ||
115 | giverSp.UUID, | ||
116 | (byte)InstantMessageDialog.InventoryAccepted, | ||
117 | false, | ||
118 | "inventory accepted msg", | ||
119 | initialSessionId, | ||
120 | false, | ||
121 | Vector3.Zero, | ||
122 | null, | ||
123 | true); | ||
124 | |||
125 | receiverClient.HandleImprovedInstantMessage(acceptIm); | ||
126 | |||
127 | // Test for item remaining in the giver's inventory (here we assume a copy item) | ||
128 | // TODO: Test no-copy items. | ||
129 | InventoryItemBase originalItemAfterGive | ||
130 | = UserInventoryHelpers.GetInventoryItem(m_scene.InventoryService, giverSp.UUID, "Objects/givenObj"); | ||
131 | |||
132 | Assert.That(originalItemAfterGive, Is.Not.Null); | ||
133 | Assert.That(originalItemAfterGive.ID, Is.EqualTo(originalItem.ID)); | ||
134 | |||
135 | // Test for item successfully making it into the receiver's inventory | ||
136 | InventoryItemBase receivedItem | ||
137 | = UserInventoryHelpers.GetInventoryItem(m_scene.InventoryService, receiverSp.UUID, "Objects/givenObj"); | ||
138 | |||
139 | Assert.That(receivedItem, Is.Not.Null); | ||
140 | Assert.That(receivedItem.ID, Is.Not.EqualTo(originalItem.ID)); | ||
141 | |||
142 | // Test that on a delete, item still exists and is accessible for the giver. | ||
143 | m_scene.InventoryService.DeleteItems(receiverSp.UUID, new List<UUID>() { receivedItem.ID }); | ||
144 | |||
145 | InventoryItemBase originalItemAfterDelete | ||
146 | = UserInventoryHelpers.GetInventoryItem(m_scene.InventoryService, giverSp.UUID, "Objects/givenObj"); | ||
147 | |||
148 | Assert.That(originalItemAfterDelete, Is.Not.Null); | ||
149 | |||
150 | // TODO: Test scenario where giver deletes their item first. | ||
151 | } | ||
152 | |||
153 | /// <summary> | ||
154 | /// Test user rejection of a given item. | ||
155 | /// </summary> | ||
156 | /// <remarks> | ||
157 | /// A rejected item still ends up in the user's trash folder. | ||
158 | /// </remarks> | ||
159 | [Test] | ||
160 | public void TestRejectGivenItem() | ||
161 | { | ||
162 | // TestHelpers.EnableLogging(); | ||
163 | |||
164 | UUID initialSessionId = TestHelpers.ParseTail(0x10); | ||
165 | UUID itemId = TestHelpers.ParseTail(0x100); | ||
166 | UUID assetId = TestHelpers.ParseTail(0x200); | ||
167 | |||
168 | UserAccount ua1 | ||
169 | = UserAccountHelpers.CreateUserWithInventory(m_scene, "User", "One", TestHelpers.ParseTail(0x1), "pw"); | ||
170 | UserAccount ua2 | ||
171 | = UserAccountHelpers.CreateUserWithInventory(m_scene, "User", "Two", TestHelpers.ParseTail(0x2), "pw"); | ||
172 | |||
173 | ScenePresence giverSp = SceneHelpers.AddScenePresence(m_scene, ua1); | ||
174 | TestClient giverClient = (TestClient)giverSp.ControllingClient; | ||
175 | |||
176 | ScenePresence receiverSp = SceneHelpers.AddScenePresence(m_scene, ua2); | ||
177 | TestClient receiverClient = (TestClient)receiverSp.ControllingClient; | ||
178 | |||
179 | // Create the object to test give | ||
180 | InventoryItemBase originalItem | ||
181 | = UserInventoryHelpers.CreateInventoryItem( | ||
182 | m_scene, "givenObj", itemId, assetId, giverSp.UUID, InventoryType.Object); | ||
183 | |||
184 | GridInstantMessage receivedIm = null; | ||
185 | receiverClient.OnReceivedInstantMessage += im => receivedIm = im; | ||
186 | |||
187 | byte[] giveImBinaryBucket = new byte[17]; | ||
188 | byte[] itemIdBytes = itemId.GetBytes(); | ||
189 | Array.Copy(itemIdBytes, 0, giveImBinaryBucket, 1, itemIdBytes.Length); | ||
190 | |||
191 | GridInstantMessage giveIm | ||
192 | = new GridInstantMessage( | ||
193 | m_scene, | ||
194 | giverSp.UUID, | ||
195 | giverSp.Name, | ||
196 | receiverSp.UUID, | ||
197 | (byte)InstantMessageDialog.InventoryOffered, | ||
198 | false, | ||
199 | "inventory offered msg", | ||
200 | initialSessionId, | ||
201 | false, | ||
202 | Vector3.Zero, | ||
203 | giveImBinaryBucket, | ||
204 | true); | ||
205 | |||
206 | giverClient.HandleImprovedInstantMessage(giveIm); | ||
207 | |||
208 | // These details might not all be correct. | ||
209 | // Session ID is now the created item ID (!) | ||
210 | GridInstantMessage rejectIm | ||
211 | = new GridInstantMessage( | ||
212 | m_scene, | ||
213 | receiverSp.UUID, | ||
214 | receiverSp.Name, | ||
215 | giverSp.UUID, | ||
216 | (byte)InstantMessageDialog.InventoryDeclined, | ||
217 | false, | ||
218 | "inventory declined msg", | ||
219 | new UUID(receivedIm.imSessionID), | ||
220 | false, | ||
221 | Vector3.Zero, | ||
222 | null, | ||
223 | true); | ||
224 | |||
225 | receiverClient.HandleImprovedInstantMessage(rejectIm); | ||
226 | |||
227 | // Test for item remaining in the giver's inventory (here we assume a copy item) | ||
228 | // TODO: Test no-copy items. | ||
229 | InventoryItemBase originalItemAfterGive | ||
230 | = UserInventoryHelpers.GetInventoryItem(m_scene.InventoryService, giverSp.UUID, "Objects/givenObj"); | ||
231 | |||
232 | Assert.That(originalItemAfterGive, Is.Not.Null); | ||
233 | Assert.That(originalItemAfterGive.ID, Is.EqualTo(originalItem.ID)); | ||
234 | |||
235 | // Test for item successfully making it into the receiver's inventory | ||
236 | InventoryItemBase receivedItem | ||
237 | = UserInventoryHelpers.GetInventoryItem(m_scene.InventoryService, receiverSp.UUID, "Trash/givenObj"); | ||
238 | |||
239 | InventoryFolderBase trashFolder | ||
240 | = m_scene.InventoryService.GetFolderForType(receiverSp.UUID, FolderType.Trash); | ||
241 | |||
242 | Assert.That(receivedItem, Is.Not.Null); | ||
243 | Assert.That(receivedItem.ID, Is.Not.EqualTo(originalItem.ID)); | ||
244 | Assert.That(receivedItem.Folder, Is.EqualTo(trashFolder.ID)); | ||
245 | |||
246 | // Test that on a delete, item still exists and is accessible for the giver. | ||
247 | m_scene.InventoryService.PurgeFolder(trashFolder); | ||
248 | |||
249 | InventoryItemBase originalItemAfterDelete | ||
250 | = UserInventoryHelpers.GetInventoryItem(m_scene.InventoryService, giverSp.UUID, "Objects/givenObj"); | ||
251 | |||
252 | Assert.That(originalItemAfterDelete, Is.Not.Null); | ||
253 | } | ||
254 | |||
255 | [Test] | ||
256 | public void TestAcceptGivenFolder() | ||
257 | { | ||
258 | TestHelpers.InMethod(); | ||
259 | // TestHelpers.EnableLogging(); | ||
260 | |||
261 | UUID initialSessionId = TestHelpers.ParseTail(0x10); | ||
262 | UUID folderId = TestHelpers.ParseTail(0x100); | ||
263 | |||
264 | UserAccount ua1 | ||
265 | = UserAccountHelpers.CreateUserWithInventory(m_scene, "User", "One", TestHelpers.ParseTail(0x1), "pw"); | ||
266 | UserAccount ua2 | ||
267 | = UserAccountHelpers.CreateUserWithInventory(m_scene, "User", "Two", TestHelpers.ParseTail(0x2), "pw"); | ||
268 | |||
269 | ScenePresence giverSp = SceneHelpers.AddScenePresence(m_scene, ua1); | ||
270 | TestClient giverClient = (TestClient)giverSp.ControllingClient; | ||
271 | |||
272 | ScenePresence receiverSp = SceneHelpers.AddScenePresence(m_scene, ua2); | ||
273 | TestClient receiverClient = (TestClient)receiverSp.ControllingClient; | ||
274 | |||
275 | InventoryFolderBase originalFolder | ||
276 | = UserInventoryHelpers.CreateInventoryFolder( | ||
277 | m_scene.InventoryService, giverSp.UUID, folderId, "f1", true); | ||
278 | |||
279 | byte[] giveImBinaryBucket = new byte[17]; | ||
280 | giveImBinaryBucket[0] = (byte)AssetType.Folder; | ||
281 | byte[] itemIdBytes = folderId.GetBytes(); | ||
282 | Array.Copy(itemIdBytes, 0, giveImBinaryBucket, 1, itemIdBytes.Length); | ||
283 | |||
284 | GridInstantMessage giveIm | ||
285 | = new GridInstantMessage( | ||
286 | m_scene, | ||
287 | giverSp.UUID, | ||
288 | giverSp.Name, | ||
289 | receiverSp.UUID, | ||
290 | (byte)InstantMessageDialog.InventoryOffered, | ||
291 | false, | ||
292 | "inventory offered msg", | ||
293 | initialSessionId, | ||
294 | false, | ||
295 | Vector3.Zero, | ||
296 | giveImBinaryBucket, | ||
297 | true); | ||
298 | |||
299 | giverClient.HandleImprovedInstantMessage(giveIm); | ||
300 | |||
301 | // These details might not all be correct. | ||
302 | GridInstantMessage acceptIm | ||
303 | = new GridInstantMessage( | ||
304 | m_scene, | ||
305 | receiverSp.UUID, | ||
306 | receiverSp.Name, | ||
307 | giverSp.UUID, | ||
308 | (byte)InstantMessageDialog.InventoryAccepted, | ||
309 | false, | ||
310 | "inventory accepted msg", | ||
311 | initialSessionId, | ||
312 | false, | ||
313 | Vector3.Zero, | ||
314 | null, | ||
315 | true); | ||
316 | |||
317 | receiverClient.HandleImprovedInstantMessage(acceptIm); | ||
318 | |||
319 | // Test for item remaining in the giver's inventory (here we assume a copy item) | ||
320 | // TODO: Test no-copy items. | ||
321 | InventoryFolderBase originalFolderAfterGive | ||
322 | = UserInventoryHelpers.GetInventoryFolder(m_scene.InventoryService, giverSp.UUID, "f1"); | ||
323 | |||
324 | Assert.That(originalFolderAfterGive, Is.Not.Null); | ||
325 | Assert.That(originalFolderAfterGive.ID, Is.EqualTo(originalFolder.ID)); | ||
326 | |||
327 | // Test for item successfully making it into the receiver's inventory | ||
328 | InventoryFolderBase receivedFolder | ||
329 | = UserInventoryHelpers.GetInventoryFolder(m_scene.InventoryService, receiverSp.UUID, "f1"); | ||
330 | |||
331 | Assert.That(receivedFolder, Is.Not.Null); | ||
332 | Assert.That(receivedFolder.ID, Is.Not.EqualTo(originalFolder.ID)); | ||
333 | |||
334 | // Test that on a delete, item still exists and is accessible for the giver. | ||
335 | m_scene.InventoryService.DeleteFolders(receiverSp.UUID, new List<UUID>() { receivedFolder.ID }); | ||
336 | |||
337 | InventoryFolderBase originalFolderAfterDelete | ||
338 | = UserInventoryHelpers.GetInventoryFolder(m_scene.InventoryService, giverSp.UUID, "f1"); | ||
339 | |||
340 | Assert.That(originalFolderAfterDelete, Is.Not.Null); | ||
341 | |||
342 | // TODO: Test scenario where giver deletes their item first. | ||
343 | } | ||
344 | |||
345 | /// <summary> | ||
346 | /// Test user rejection of a given item. | ||
347 | /// </summary> | ||
348 | /// <remarks> | ||
349 | /// A rejected item still ends up in the user's trash folder. | ||
350 | /// </remarks> | ||
351 | [Test] | ||
352 | public void TestRejectGivenFolder() | ||
353 | { | ||
354 | TestHelpers.InMethod(); | ||
355 | // TestHelpers.EnableLogging(); | ||
356 | |||
357 | UUID initialSessionId = TestHelpers.ParseTail(0x10); | ||
358 | UUID folderId = TestHelpers.ParseTail(0x100); | ||
359 | |||
360 | UserAccount ua1 | ||
361 | = UserAccountHelpers.CreateUserWithInventory(m_scene, "User", "One", TestHelpers.ParseTail(0x1), "pw"); | ||
362 | UserAccount ua2 | ||
363 | = UserAccountHelpers.CreateUserWithInventory(m_scene, "User", "Two", TestHelpers.ParseTail(0x2), "pw"); | ||
364 | |||
365 | ScenePresence giverSp = SceneHelpers.AddScenePresence(m_scene, ua1); | ||
366 | TestClient giverClient = (TestClient)giverSp.ControllingClient; | ||
367 | |||
368 | ScenePresence receiverSp = SceneHelpers.AddScenePresence(m_scene, ua2); | ||
369 | TestClient receiverClient = (TestClient)receiverSp.ControllingClient; | ||
370 | |||
371 | // Create the folder to test give | ||
372 | InventoryFolderBase originalFolder | ||
373 | = UserInventoryHelpers.CreateInventoryFolder( | ||
374 | m_scene.InventoryService, giverSp.UUID, folderId, "f1", true); | ||
375 | |||
376 | GridInstantMessage receivedIm = null; | ||
377 | receiverClient.OnReceivedInstantMessage += im => receivedIm = im; | ||
378 | |||
379 | byte[] giveImBinaryBucket = new byte[17]; | ||
380 | giveImBinaryBucket[0] = (byte)AssetType.Folder; | ||
381 | byte[] itemIdBytes = folderId.GetBytes(); | ||
382 | Array.Copy(itemIdBytes, 0, giveImBinaryBucket, 1, itemIdBytes.Length); | ||
383 | |||
384 | GridInstantMessage giveIm | ||
385 | = new GridInstantMessage( | ||
386 | m_scene, | ||
387 | giverSp.UUID, | ||
388 | giverSp.Name, | ||
389 | receiverSp.UUID, | ||
390 | (byte)InstantMessageDialog.InventoryOffered, | ||
391 | false, | ||
392 | "inventory offered msg", | ||
393 | initialSessionId, | ||
394 | false, | ||
395 | Vector3.Zero, | ||
396 | giveImBinaryBucket, | ||
397 | true); | ||
398 | |||
399 | giverClient.HandleImprovedInstantMessage(giveIm); | ||
400 | |||
401 | // These details might not all be correct. | ||
402 | // Session ID is now the created item ID (!) | ||
403 | GridInstantMessage rejectIm | ||
404 | = new GridInstantMessage( | ||
405 | m_scene, | ||
406 | receiverSp.UUID, | ||
407 | receiverSp.Name, | ||
408 | giverSp.UUID, | ||
409 | (byte)InstantMessageDialog.InventoryDeclined, | ||
410 | false, | ||
411 | "inventory declined msg", | ||
412 | new UUID(receivedIm.imSessionID), | ||
413 | false, | ||
414 | Vector3.Zero, | ||
415 | null, | ||
416 | true); | ||
417 | |||
418 | receiverClient.HandleImprovedInstantMessage(rejectIm); | ||
419 | |||
420 | // Test for item remaining in the giver's inventory (here we assume a copy item) | ||
421 | // TODO: Test no-copy items. | ||
422 | InventoryFolderBase originalFolderAfterGive | ||
423 | = UserInventoryHelpers.GetInventoryFolder(m_scene.InventoryService, giverSp.UUID, "f1"); | ||
424 | |||
425 | Assert.That(originalFolderAfterGive, Is.Not.Null); | ||
426 | Assert.That(originalFolderAfterGive.ID, Is.EqualTo(originalFolder.ID)); | ||
427 | |||
428 | // Test for folder successfully making it into the receiver's inventory | ||
429 | InventoryFolderBase receivedFolder | ||
430 | = UserInventoryHelpers.GetInventoryFolder(m_scene.InventoryService, receiverSp.UUID, "Trash/f1"); | ||
431 | |||
432 | InventoryFolderBase trashFolder | ||
433 | = m_scene.InventoryService.GetFolderForType(receiverSp.UUID, FolderType.Trash); | ||
434 | |||
435 | Assert.That(receivedFolder, Is.Not.Null); | ||
436 | Assert.That(receivedFolder.ID, Is.Not.EqualTo(originalFolder.ID)); | ||
437 | Assert.That(receivedFolder.ParentID, Is.EqualTo(trashFolder.ID)); | ||
438 | |||
439 | // Test that on a delete, item still exists and is accessible for the giver. | ||
440 | m_scene.InventoryService.PurgeFolder(trashFolder); | ||
441 | |||
442 | InventoryFolderBase originalFolderAfterDelete | ||
443 | = UserInventoryHelpers.GetInventoryFolder(m_scene.InventoryService, giverSp.UUID, "f1"); | ||
444 | |||
445 | Assert.That(originalFolderAfterDelete, Is.Not.Null); | ||
446 | } | ||
447 | } | ||
448 | } \ No newline at end of file | ||
diff --git a/OpenSim/Region/CoreModules/Avatar/Lure/HGLureModule.cs b/OpenSim/Region/CoreModules/Avatar/Lure/HGLureModule.cs index 232a4fe..24286a4 100644 --- a/OpenSim/Region/CoreModules/Avatar/Lure/HGLureModule.cs +++ b/OpenSim/Region/CoreModules/Avatar/Lure/HGLureModule.cs | |||
@@ -65,7 +65,10 @@ namespace OpenSim.Region.CoreModules.Avatar.Lure | |||
65 | { | 65 | { |
66 | m_Enabled = true; | 66 | m_Enabled = true; |
67 | 67 | ||
68 | m_ThisGridURL = config.Configs["Messaging"].GetString("Gatekeeper", string.Empty); | 68 | m_ThisGridURL = Util.GetConfigVarFromSections<string>(config, "GatekeeperURI", |
69 | new string[] { "Startup", "Hypergrid", "Messaging" }, String.Empty); | ||
70 | // Legacy. Remove soon! | ||
71 | m_ThisGridURL = config.Configs["Messaging"].GetString("Gatekeeper", m_ThisGridURL); | ||
69 | m_log.DebugFormat("[LURE MODULE]: {0} enabled", Name); | 72 | m_log.DebugFormat("[LURE MODULE]: {0} enabled", Name); |
70 | } | 73 | } |
71 | } | 74 | } |
@@ -151,7 +154,8 @@ namespace OpenSim.Region.CoreModules.Avatar.Lure | |||
151 | 154 | ||
152 | void OnIncomingInstantMessage(GridInstantMessage im) | 155 | void OnIncomingInstantMessage(GridInstantMessage im) |
153 | { | 156 | { |
154 | if (im.dialog == (byte)InstantMessageDialog.RequestTeleport) | 157 | if (im.dialog == (byte)InstantMessageDialog.RequestTeleport |
158 | || im.dialog == (byte)InstantMessageDialog.GodLikeRequestTeleport) | ||
155 | { | 159 | { |
156 | UUID sessionID = new UUID(im.imSessionID); | 160 | UUID sessionID = new UUID(im.imSessionID); |
157 | 161 | ||
@@ -235,16 +239,29 @@ namespace OpenSim.Region.CoreModules.Avatar.Lure | |||
235 | GatekeeperServiceConnector gConn = new GatekeeperServiceConnector(); | 239 | GatekeeperServiceConnector gConn = new GatekeeperServiceConnector(); |
236 | GridRegion gatekeeper = new GridRegion(); | 240 | GridRegion gatekeeper = new GridRegion(); |
237 | gatekeeper.ServerURI = url; | 241 | gatekeeper.ServerURI = url; |
238 | GridRegion finalDestination = gConn.GetHyperlinkRegion(gatekeeper, new UUID(im.RegionID)); | 242 | string homeURI = scene.GetAgentHomeURI(client.AgentId); |
243 | |||
244 | string message; | ||
245 | GridRegion finalDestination = gConn.GetHyperlinkRegion(gatekeeper, new UUID(im.RegionID), client.AgentId, homeURI, out message); | ||
239 | if (finalDestination != null) | 246 | if (finalDestination != null) |
240 | { | 247 | { |
241 | ScenePresence sp = scene.GetScenePresence(client.AgentId); | 248 | ScenePresence sp = scene.GetScenePresence(client.AgentId); |
242 | IEntityTransferModule transferMod = scene.RequestModuleInterface<IEntityTransferModule>(); | 249 | IEntityTransferModule transferMod = scene.RequestModuleInterface<IEntityTransferModule>(); |
243 | 250 | ||
244 | if (transferMod != null && sp != null) | 251 | if (transferMod != null && sp != null) |
252 | { | ||
253 | if (message != null) | ||
254 | sp.ControllingClient.SendAgentAlertMessage(message, true); | ||
255 | |||
245 | transferMod.DoTeleport( | 256 | transferMod.DoTeleport( |
246 | sp, gatekeeper, finalDestination, im.Position + new Vector3(0.5f, 0.5f, 0f), | 257 | sp, gatekeeper, finalDestination, im.Position + new Vector3(0.5f, 0.5f, 0f), |
247 | Vector3.UnitX, teleportflags); | 258 | Vector3.UnitX, teleportflags); |
259 | } | ||
260 | } | ||
261 | else | ||
262 | { | ||
263 | m_log.InfoFormat("[HG LURE MODULE]: Lure failed: {0}", message); | ||
264 | client.SendAgentAlertMessage(message, true); | ||
248 | } | 265 | } |
249 | } | 266 | } |
250 | } | 267 | } |
diff --git a/OpenSim/Region/CoreModules/Avatar/Lure/LureModule.cs b/OpenSim/Region/CoreModules/Avatar/Lure/LureModule.cs index e4b0cfa..465ffbc 100644 --- a/OpenSim/Region/CoreModules/Avatar/Lure/LureModule.cs +++ b/OpenSim/Region/CoreModules/Avatar/Lure/LureModule.cs | |||
@@ -165,7 +165,7 @@ namespace OpenSim.Region.CoreModules.Avatar.Lure | |||
165 | (uint)presence.AbsolutePosition.Y, | 165 | (uint)presence.AbsolutePosition.Y, |
166 | (uint)Math.Ceiling(presence.AbsolutePosition.Z)); | 166 | (uint)Math.Ceiling(presence.AbsolutePosition.Z)); |
167 | 167 | ||
168 | m_log.DebugFormat("TP invite with message {0}", message); | 168 | m_log.DebugFormat("[LURE MODULE]: TP invite with message {0}, type {1}", message, lureType); |
169 | 169 | ||
170 | GridInstantMessage m = new GridInstantMessage(scene, client.AgentId, | 170 | GridInstantMessage m = new GridInstantMessage(scene, client.AgentId, |
171 | client.FirstName+" "+client.LastName, targetid, | 171 | client.FirstName+" "+client.LastName, targetid, |
diff --git a/OpenSim/Region/CoreModules/Avatar/Profile/BasicProfileModule.cs b/OpenSim/Region/CoreModules/Avatar/Profile/BasicProfileModule.cs index bf24030..2bb24ae 100644 --- a/OpenSim/Region/CoreModules/Avatar/Profile/BasicProfileModule.cs +++ b/OpenSim/Region/CoreModules/Avatar/Profile/BasicProfileModule.cs | |||
@@ -57,6 +57,9 @@ namespace OpenSim.Region.CoreModules.Avatar.Profile | |||
57 | 57 | ||
58 | public void Initialise(IConfigSource config) | 58 | public void Initialise(IConfigSource config) |
59 | { | 59 | { |
60 | if(config.Configs["UserProfiles"] != null) | ||
61 | return; | ||
62 | |||
60 | m_log.DebugFormat("[PROFILE MODULE]: Basic Profile Module enabled"); | 63 | m_log.DebugFormat("[PROFILE MODULE]: Basic Profile Module enabled"); |
61 | m_Enabled = true; | 64 | m_Enabled = true; |
62 | } | 65 | } |
diff --git a/OpenSim/Region/CoreModules/Avatar/UserProfiles/UserProfileModule.cs b/OpenSim/Region/CoreModules/Avatar/UserProfiles/UserProfileModule.cs new file mode 100644 index 0000000..c20369c --- /dev/null +++ b/OpenSim/Region/CoreModules/Avatar/UserProfiles/UserProfileModule.cs | |||
@@ -0,0 +1,1406 @@ | |||
1 | /* | ||
2 | * Copyright (c) Contributors, http://opensimulator.org/ | ||
3 | * See CONTRIBUTORS.TXT for a full list of copyright holders. | ||
4 | * | ||
5 | * Redistribution and use in source and binary forms, with or without | ||
6 | * modification, are permitted provided that the following conditions are met: | ||
7 | * * Redistributions of source code must retain the above copyright | ||
8 | * notice, this list of conditions and the following disclaimer. | ||
9 | * * Redistributions in binary form must reproduce the above copyright | ||
10 | * notice, this list of conditions and the following disclaimer in the | ||
11 | * documentation and/or other materials provided with the distribution. | ||
12 | * * Neither the name of the OpenSimulator Project nor the | ||
13 | * names of its contributors may be used to endorse or promote products | ||
14 | * derived from this software without specific prior written permission. | ||
15 | * | ||
16 | * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY | ||
17 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||
18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
19 | * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY | ||
20 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||
21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||
22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | ||
23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
26 | */ | ||
27 | |||
28 | using System; | ||
29 | using System.IO; | ||
30 | using System.Text; | ||
31 | using System.Collections; | ||
32 | using System.Collections.Generic; | ||
33 | using System.Globalization; | ||
34 | using System.Net; | ||
35 | using System.Net.Sockets; | ||
36 | using System.Reflection; | ||
37 | using System.Xml; | ||
38 | using OpenMetaverse; | ||
39 | using OpenMetaverse.StructuredData; | ||
40 | using log4net; | ||
41 | using Nini.Config; | ||
42 | using Nwc.XmlRpc; | ||
43 | using OpenSim.Framework; | ||
44 | using OpenSim.Region.Framework.Interfaces; | ||
45 | using OpenSim.Region.Framework.Scenes; | ||
46 | using OpenSim.Services.Interfaces; | ||
47 | using Mono.Addins; | ||
48 | using OpenSim.Services.Connectors.Hypergrid; | ||
49 | using OpenSim.Framework.Servers.HttpServer; | ||
50 | using OpenSim.Services.UserProfilesService; | ||
51 | using GridRegion = OpenSim.Services.Interfaces.GridRegion; | ||
52 | using Microsoft.CSharp; | ||
53 | |||
54 | namespace OpenSim.Region.CoreModules.Avatar.UserProfiles | ||
55 | { | ||
56 | [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "UserProfilesModule")] | ||
57 | public class UserProfileModule : IProfileModule, INonSharedRegionModule | ||
58 | { | ||
59 | /// <summary> | ||
60 | /// Logging | ||
61 | /// </summary> | ||
62 | static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | ||
63 | |||
64 | // The pair of Dictionaries are used to handle the switching of classified ads | ||
65 | // by maintaining a cache of classified id to creator id mappings and an interest | ||
66 | // count. The entries are removed when the interest count reaches 0. | ||
67 | Dictionary<UUID, UUID> m_classifiedCache = new Dictionary<UUID, UUID>(); | ||
68 | Dictionary<UUID, int> m_classifiedInterest = new Dictionary<UUID, int>(); | ||
69 | |||
70 | private JsonRpcRequestManager rpc = new JsonRpcRequestManager(); | ||
71 | |||
72 | public Scene Scene | ||
73 | { | ||
74 | get; private set; | ||
75 | } | ||
76 | |||
77 | /// <summary> | ||
78 | /// Gets or sets the ConfigSource. | ||
79 | /// </summary> | ||
80 | /// <value> | ||
81 | /// The configuration | ||
82 | /// </value> | ||
83 | public IConfigSource Config | ||
84 | { | ||
85 | get; | ||
86 | set; | ||
87 | } | ||
88 | |||
89 | /// <summary> | ||
90 | /// Gets or sets the URI to the profile server. | ||
91 | /// </summary> | ||
92 | /// <value> | ||
93 | /// The profile server URI. | ||
94 | /// </value> | ||
95 | public string ProfileServerUri | ||
96 | { | ||
97 | get; | ||
98 | set; | ||
99 | } | ||
100 | |||
101 | IProfileModule ProfileModule | ||
102 | { | ||
103 | get; set; | ||
104 | } | ||
105 | |||
106 | IUserManagement UserManagementModule | ||
107 | { | ||
108 | get; set; | ||
109 | } | ||
110 | |||
111 | /// <summary> | ||
112 | /// Gets or sets a value indicating whether this | ||
113 | /// <see cref="OpenSim.Region.Coremodules.UserProfiles.UserProfileModule"/> is enabled. | ||
114 | /// </summary> | ||
115 | /// <value> | ||
116 | /// <c>true</c> if enabled; otherwise, <c>false</c>. | ||
117 | /// </value> | ||
118 | public bool Enabled | ||
119 | { | ||
120 | get; | ||
121 | set; | ||
122 | } | ||
123 | |||
124 | public string MyGatekeeper | ||
125 | { | ||
126 | get; private set; | ||
127 | } | ||
128 | |||
129 | |||
130 | #region IRegionModuleBase implementation | ||
131 | /// <summary> | ||
132 | /// This is called to initialize the region module. For shared modules, this is called exactly once, after | ||
133 | /// creating the single (shared) instance. For non-shared modules, this is called once on each instance, after | ||
134 | /// the instace for the region has been created. | ||
135 | /// </summary> | ||
136 | /// <param name='source'> | ||
137 | /// Source. | ||
138 | /// </param> | ||
139 | public void Initialise(IConfigSource source) | ||
140 | { | ||
141 | Config = source; | ||
142 | ReplaceableInterface = typeof(IProfileModule); | ||
143 | |||
144 | IConfig profileConfig = Config.Configs["UserProfiles"]; | ||
145 | |||
146 | if (profileConfig == null) | ||
147 | { | ||
148 | m_log.Debug("[PROFILES]: UserProfiles disabled, no configuration"); | ||
149 | Enabled = false; | ||
150 | return; | ||
151 | } | ||
152 | |||
153 | // If we find ProfileURL then we configure for FULL support | ||
154 | // else we setup for BASIC support | ||
155 | ProfileServerUri = profileConfig.GetString("ProfileServiceURL", ""); | ||
156 | if (ProfileServerUri == "") | ||
157 | { | ||
158 | Enabled = false; | ||
159 | return; | ||
160 | } | ||
161 | |||
162 | m_log.Debug("[PROFILES]: Full Profiles Enabled"); | ||
163 | ReplaceableInterface = null; | ||
164 | Enabled = true; | ||
165 | |||
166 | MyGatekeeper = Util.GetConfigVarFromSections<string>(source, "GatekeeperURI", | ||
167 | new string[] { "Startup", "Hypergrid", "UserProfiles" }, String.Empty); | ||
168 | } | ||
169 | |||
170 | /// <summary> | ||
171 | /// Adds the region. | ||
172 | /// </summary> | ||
173 | /// <param name='scene'> | ||
174 | /// Scene. | ||
175 | /// </param> | ||
176 | public void AddRegion(Scene scene) | ||
177 | { | ||
178 | if(!Enabled) | ||
179 | return; | ||
180 | |||
181 | Scene = scene; | ||
182 | Scene.RegisterModuleInterface<IProfileModule>(this); | ||
183 | Scene.EventManager.OnNewClient += OnNewClient; | ||
184 | Scene.EventManager.OnMakeRootAgent += HandleOnMakeRootAgent; | ||
185 | |||
186 | UserManagementModule = Scene.RequestModuleInterface<IUserManagement>(); | ||
187 | } | ||
188 | |||
189 | void HandleOnMakeRootAgent (ScenePresence obj) | ||
190 | { | ||
191 | if(obj.PresenceType == PresenceType.Npc) | ||
192 | return; | ||
193 | |||
194 | Util.FireAndForget(delegate | ||
195 | { | ||
196 | GetImageAssets(((IScenePresence)obj).UUID); | ||
197 | }, null, "UserProfileModule.GetImageAssets"); | ||
198 | } | ||
199 | |||
200 | /// <summary> | ||
201 | /// Removes the region. | ||
202 | /// </summary> | ||
203 | /// <param name='scene'> | ||
204 | /// Scene. | ||
205 | /// </param> | ||
206 | public void RemoveRegion(Scene scene) | ||
207 | { | ||
208 | if(!Enabled) | ||
209 | return; | ||
210 | } | ||
211 | |||
212 | /// <summary> | ||
213 | /// This will be called once for every scene loaded. In a shared module this will be multiple times in one | ||
214 | /// instance, while a nonshared module instance will only be called once. This method is called after AddRegion | ||
215 | /// has been called in all modules for that scene, providing an opportunity to request another module's | ||
216 | /// interface, or hook an event from another module. | ||
217 | /// </summary> | ||
218 | /// <param name='scene'> | ||
219 | /// Scene. | ||
220 | /// </param> | ||
221 | public void RegionLoaded(Scene scene) | ||
222 | { | ||
223 | if(!Enabled) | ||
224 | return; | ||
225 | } | ||
226 | |||
227 | /// <summary> | ||
228 | /// If this returns non-null, it is the type of an interface that this module intends to register. This will | ||
229 | /// cause the loader to defer loading of this module until all other modules have been loaded. If no other | ||
230 | /// module has registered the interface by then, this module will be activated, else it will remain inactive, | ||
231 | /// letting the other module take over. This should return non-null ONLY in modules that are intended to be | ||
232 | /// easily replaceable, e.g. stub implementations that the developer expects to be replaced by third party | ||
233 | /// provided modules. | ||
234 | /// </summary> | ||
235 | /// <value> | ||
236 | /// The replaceable interface. | ||
237 | /// </value> | ||
238 | public Type ReplaceableInterface | ||
239 | { | ||
240 | get; private set; | ||
241 | } | ||
242 | |||
243 | /// <summary> | ||
244 | /// Called as the instance is closed. | ||
245 | /// </summary> | ||
246 | public void Close() | ||
247 | { | ||
248 | } | ||
249 | |||
250 | /// <value> | ||
251 | /// The name of the module | ||
252 | /// </value> | ||
253 | /// <summary> | ||
254 | /// Gets the module name. | ||
255 | /// </summary> | ||
256 | public string Name | ||
257 | { | ||
258 | get { return "UserProfileModule"; } | ||
259 | } | ||
260 | #endregion IRegionModuleBase implementation | ||
261 | |||
262 | #region Region Event Handlers | ||
263 | /// <summary> | ||
264 | /// Raises the new client event. | ||
265 | /// </summary> | ||
266 | /// <param name='client'> | ||
267 | /// Client. | ||
268 | /// </param> | ||
269 | void OnNewClient(IClientAPI client) | ||
270 | { | ||
271 | //Profile | ||
272 | client.OnRequestAvatarProperties += RequestAvatarProperties; | ||
273 | client.OnUpdateAvatarProperties += AvatarPropertiesUpdate; | ||
274 | client.OnAvatarInterestUpdate += AvatarInterestsUpdate; | ||
275 | |||
276 | // Classifieds | ||
277 | client.AddGenericPacketHandler("avatarclassifiedsrequest", ClassifiedsRequest); | ||
278 | client.OnClassifiedInfoUpdate += ClassifiedInfoUpdate; | ||
279 | client.OnClassifiedInfoRequest += ClassifiedInfoRequest; | ||
280 | client.OnClassifiedDelete += ClassifiedDelete; | ||
281 | |||
282 | // Picks | ||
283 | client.AddGenericPacketHandler("avatarpicksrequest", PicksRequest); | ||
284 | client.AddGenericPacketHandler("pickinforequest", PickInfoRequest); | ||
285 | client.OnPickInfoUpdate += PickInfoUpdate; | ||
286 | client.OnPickDelete += PickDelete; | ||
287 | |||
288 | // Notes | ||
289 | client.AddGenericPacketHandler("avatarnotesrequest", NotesRequest); | ||
290 | client.OnAvatarNotesUpdate += NotesUpdate; | ||
291 | |||
292 | // Preferences | ||
293 | client.OnUserInfoRequest += UserPreferencesRequest; | ||
294 | client.OnUpdateUserInfo += UpdateUserPreferences; | ||
295 | } | ||
296 | #endregion Region Event Handlers | ||
297 | |||
298 | #region Classified | ||
299 | /// | ||
300 | /// <summary> | ||
301 | /// Handles the avatar classifieds request. | ||
302 | /// </summary> | ||
303 | /// <param name='sender'> | ||
304 | /// Sender. | ||
305 | /// </param> | ||
306 | /// <param name='method'> | ||
307 | /// Method. | ||
308 | /// </param> | ||
309 | /// <param name='args'> | ||
310 | /// Arguments. | ||
311 | /// </param> | ||
312 | public void ClassifiedsRequest(Object sender, string method, List<String> args) | ||
313 | { | ||
314 | if (!(sender is IClientAPI)) | ||
315 | return; | ||
316 | |||
317 | IClientAPI remoteClient = (IClientAPI)sender; | ||
318 | |||
319 | UUID targetID; | ||
320 | UUID.TryParse(args[0], out targetID); | ||
321 | |||
322 | // Can't handle NPC yet... | ||
323 | ScenePresence p = FindPresence(targetID); | ||
324 | |||
325 | if (null != p) | ||
326 | { | ||
327 | if (p.PresenceType == PresenceType.Npc) | ||
328 | return; | ||
329 | } | ||
330 | |||
331 | string serverURI = string.Empty; | ||
332 | GetUserProfileServerURI(targetID, out serverURI); | ||
333 | UUID creatorId = UUID.Zero; | ||
334 | Dictionary<UUID, string> classifieds = new Dictionary<UUID, string>(); | ||
335 | |||
336 | OSDMap parameters= new OSDMap(); | ||
337 | UUID.TryParse(args[0], out creatorId); | ||
338 | parameters.Add("creatorId", OSD.FromUUID(creatorId)); | ||
339 | OSD Params = (OSD)parameters; | ||
340 | if(!rpc.JsonRpcRequest(ref Params, "avatarclassifiedsrequest", serverURI, UUID.Random().ToString())) | ||
341 | { | ||
342 | remoteClient.SendAvatarClassifiedReply(new UUID(args[0]), classifieds); | ||
343 | return; | ||
344 | } | ||
345 | |||
346 | parameters = (OSDMap)Params; | ||
347 | |||
348 | OSDArray list = (OSDArray)parameters["result"]; | ||
349 | |||
350 | |||
351 | foreach(OSD map in list) | ||
352 | { | ||
353 | OSDMap m = (OSDMap)map; | ||
354 | UUID cid = m["classifieduuid"].AsUUID(); | ||
355 | string name = m["name"].AsString(); | ||
356 | |||
357 | classifieds[cid] = name; | ||
358 | |||
359 | lock (m_classifiedCache) | ||
360 | { | ||
361 | if (!m_classifiedCache.ContainsKey(cid)) | ||
362 | { | ||
363 | m_classifiedCache.Add(cid,creatorId); | ||
364 | m_classifiedInterest.Add(cid, 0); | ||
365 | } | ||
366 | |||
367 | m_classifiedInterest[cid]++; | ||
368 | } | ||
369 | } | ||
370 | |||
371 | remoteClient.SendAvatarClassifiedReply(new UUID(args[0]), classifieds); | ||
372 | } | ||
373 | |||
374 | public void ClassifiedInfoRequest(UUID queryClassifiedID, IClientAPI remoteClient) | ||
375 | { | ||
376 | UUID target = remoteClient.AgentId; | ||
377 | UserClassifiedAdd ad = new UserClassifiedAdd(); | ||
378 | ad.ClassifiedId = queryClassifiedID; | ||
379 | |||
380 | lock (m_classifiedCache) | ||
381 | { | ||
382 | if (m_classifiedCache.ContainsKey(queryClassifiedID)) | ||
383 | { | ||
384 | target = m_classifiedCache[queryClassifiedID]; | ||
385 | |||
386 | m_classifiedInterest[queryClassifiedID] --; | ||
387 | |||
388 | if (m_classifiedInterest[queryClassifiedID] == 0) | ||
389 | { | ||
390 | m_classifiedInterest.Remove(queryClassifiedID); | ||
391 | m_classifiedCache.Remove(queryClassifiedID); | ||
392 | } | ||
393 | } | ||
394 | } | ||
395 | |||
396 | string serverURI = string.Empty; | ||
397 | GetUserProfileServerURI(target, out serverURI); | ||
398 | |||
399 | object Ad = (object)ad; | ||
400 | if(!rpc.JsonRpcRequest(ref Ad, "classifieds_info_query", serverURI, UUID.Random().ToString())) | ||
401 | { | ||
402 | remoteClient.SendAgentAlertMessage( | ||
403 | "Error getting classified info", false); | ||
404 | return; | ||
405 | } | ||
406 | ad = (UserClassifiedAdd) Ad; | ||
407 | |||
408 | if(ad.CreatorId == UUID.Zero) | ||
409 | return; | ||
410 | |||
411 | Vector3 globalPos = new Vector3(); | ||
412 | Vector3.TryParse(ad.GlobalPos, out globalPos); | ||
413 | |||
414 | remoteClient.SendClassifiedInfoReply(ad.ClassifiedId, ad.CreatorId, (uint)ad.CreationDate, (uint)ad.ExpirationDate, | ||
415 | (uint)ad.Category, ad.Name, ad.Description, ad.ParcelId, (uint)ad.ParentEstate, | ||
416 | ad.SnapshotId, ad.SimName, globalPos, ad.ParcelName, ad.Flags, ad.Price); | ||
417 | |||
418 | } | ||
419 | |||
420 | /// <summary> | ||
421 | /// Classifieds info update. | ||
422 | /// </summary> | ||
423 | /// <param name='queryclassifiedID'> | ||
424 | /// Queryclassified I. | ||
425 | /// </param> | ||
426 | /// <param name='queryCategory'> | ||
427 | /// Query category. | ||
428 | /// </param> | ||
429 | /// <param name='queryName'> | ||
430 | /// Query name. | ||
431 | /// </param> | ||
432 | /// <param name='queryDescription'> | ||
433 | /// Query description. | ||
434 | /// </param> | ||
435 | /// <param name='queryParcelID'> | ||
436 | /// Query parcel I. | ||
437 | /// </param> | ||
438 | /// <param name='queryParentEstate'> | ||
439 | /// Query parent estate. | ||
440 | /// </param> | ||
441 | /// <param name='querySnapshotID'> | ||
442 | /// Query snapshot I. | ||
443 | /// </param> | ||
444 | /// <param name='queryGlobalPos'> | ||
445 | /// Query global position. | ||
446 | /// </param> | ||
447 | /// <param name='queryclassifiedFlags'> | ||
448 | /// Queryclassified flags. | ||
449 | /// </param> | ||
450 | /// <param name='queryclassifiedPrice'> | ||
451 | /// Queryclassified price. | ||
452 | /// </param> | ||
453 | /// <param name='remoteClient'> | ||
454 | /// Remote client. | ||
455 | /// </param> | ||
456 | public void ClassifiedInfoUpdate(UUID queryclassifiedID, uint queryCategory, string queryName, string queryDescription, UUID queryParcelID, | ||
457 | uint queryParentEstate, UUID querySnapshotID, Vector3 queryGlobalPos, byte queryclassifiedFlags, | ||
458 | int queryclassifiedPrice, IClientAPI remoteClient) | ||
459 | { | ||
460 | Scene s = (Scene)remoteClient.Scene; | ||
461 | IMoneyModule money = s.RequestModuleInterface<IMoneyModule>(); | ||
462 | |||
463 | if (money != null) | ||
464 | { | ||
465 | if (!money.AmountCovered(remoteClient.AgentId, queryclassifiedPrice)) | ||
466 | { | ||
467 | remoteClient.SendAgentAlertMessage("You do not have enough money to create requested classified.", false); | ||
468 | return; | ||
469 | } | ||
470 | money.ApplyCharge(remoteClient.AgentId, queryclassifiedPrice, MoneyTransactionType.ClassifiedCharge); | ||
471 | } | ||
472 | |||
473 | UserClassifiedAdd ad = new UserClassifiedAdd(); | ||
474 | |||
475 | Vector3 pos = remoteClient.SceneAgent.AbsolutePosition; | ||
476 | ILandObject land = s.LandChannel.GetLandObject(pos.X, pos.Y); | ||
477 | ScenePresence p = FindPresence(remoteClient.AgentId); | ||
478 | |||
479 | string serverURI = string.Empty; | ||
480 | GetUserProfileServerURI(remoteClient.AgentId, out serverURI); | ||
481 | |||
482 | if (land == null) | ||
483 | { | ||
484 | ad.ParcelName = string.Empty; | ||
485 | } | ||
486 | else | ||
487 | { | ||
488 | ad.ParcelName = land.LandData.Name; | ||
489 | } | ||
490 | |||
491 | ad.CreatorId = remoteClient.AgentId; | ||
492 | ad.ClassifiedId = queryclassifiedID; | ||
493 | ad.Category = Convert.ToInt32(queryCategory); | ||
494 | ad.Name = queryName; | ||
495 | ad.Description = queryDescription; | ||
496 | ad.ParentEstate = Convert.ToInt32(queryParentEstate); | ||
497 | ad.SnapshotId = querySnapshotID; | ||
498 | ad.SimName = remoteClient.Scene.RegionInfo.RegionName; | ||
499 | ad.GlobalPos = queryGlobalPos.ToString (); | ||
500 | ad.Flags = queryclassifiedFlags; | ||
501 | ad.Price = queryclassifiedPrice; | ||
502 | ad.ParcelId = p.currentParcelUUID; | ||
503 | |||
504 | object Ad = ad; | ||
505 | |||
506 | OSD.SerializeMembers(Ad); | ||
507 | |||
508 | if(!rpc.JsonRpcRequest(ref Ad, "classified_update", serverURI, UUID.Random().ToString())) | ||
509 | { | ||
510 | remoteClient.SendAgentAlertMessage( | ||
511 | "Error updating classified", false); | ||
512 | return; | ||
513 | } | ||
514 | } | ||
515 | |||
516 | /// <summary> | ||
517 | /// Classifieds delete. | ||
518 | /// </summary> | ||
519 | /// <param name='queryClassifiedID'> | ||
520 | /// Query classified I. | ||
521 | /// </param> | ||
522 | /// <param name='remoteClient'> | ||
523 | /// Remote client. | ||
524 | /// </param> | ||
525 | public void ClassifiedDelete(UUID queryClassifiedID, IClientAPI remoteClient) | ||
526 | { | ||
527 | string serverURI = string.Empty; | ||
528 | GetUserProfileServerURI(remoteClient.AgentId, out serverURI); | ||
529 | |||
530 | UUID classifiedId; | ||
531 | OSDMap parameters= new OSDMap(); | ||
532 | UUID.TryParse(queryClassifiedID.ToString(), out classifiedId); | ||
533 | parameters.Add("classifiedId", OSD.FromUUID(classifiedId)); | ||
534 | OSD Params = (OSD)parameters; | ||
535 | if(!rpc.JsonRpcRequest(ref Params, "classified_delete", serverURI, UUID.Random().ToString())) | ||
536 | { | ||
537 | remoteClient.SendAgentAlertMessage( | ||
538 | "Error classified delete", false); | ||
539 | return; | ||
540 | } | ||
541 | |||
542 | parameters = (OSDMap)Params; | ||
543 | } | ||
544 | #endregion Classified | ||
545 | |||
546 | #region Picks | ||
547 | /// <summary> | ||
548 | /// Handles the avatar picks request. | ||
549 | /// </summary> | ||
550 | /// <param name='sender'> | ||
551 | /// Sender. | ||
552 | /// </param> | ||
553 | /// <param name='method'> | ||
554 | /// Method. | ||
555 | /// </param> | ||
556 | /// <param name='args'> | ||
557 | /// Arguments. | ||
558 | /// </param> | ||
559 | public void PicksRequest(Object sender, string method, List<String> args) | ||
560 | { | ||
561 | if (!(sender is IClientAPI)) | ||
562 | return; | ||
563 | |||
564 | IClientAPI remoteClient = (IClientAPI)sender; | ||
565 | |||
566 | UUID targetId; | ||
567 | UUID.TryParse(args[0], out targetId); | ||
568 | |||
569 | // Can't handle NPC yet... | ||
570 | ScenePresence p = FindPresence(targetId); | ||
571 | |||
572 | if (null != p) | ||
573 | { | ||
574 | if (p.PresenceType == PresenceType.Npc) | ||
575 | return; | ||
576 | } | ||
577 | |||
578 | string serverURI = string.Empty; | ||
579 | GetUserProfileServerURI(targetId, out serverURI); | ||
580 | |||
581 | Dictionary<UUID, string> picks = new Dictionary<UUID, string>(); | ||
582 | |||
583 | OSDMap parameters= new OSDMap(); | ||
584 | parameters.Add("creatorId", OSD.FromUUID(targetId)); | ||
585 | OSD Params = (OSD)parameters; | ||
586 | if(!rpc.JsonRpcRequest(ref Params, "avatarpicksrequest", serverURI, UUID.Random().ToString())) | ||
587 | { | ||
588 | remoteClient.SendAvatarPicksReply(new UUID(args[0]), picks); | ||
589 | return; | ||
590 | } | ||
591 | |||
592 | parameters = (OSDMap)Params; | ||
593 | |||
594 | OSDArray list = (OSDArray)parameters["result"]; | ||
595 | |||
596 | foreach(OSD map in list) | ||
597 | { | ||
598 | OSDMap m = (OSDMap)map; | ||
599 | UUID cid = m["pickuuid"].AsUUID(); | ||
600 | string name = m["name"].AsString(); | ||
601 | |||
602 | m_log.DebugFormat("[PROFILES]: PicksRequest {0}", name); | ||
603 | |||
604 | picks[cid] = name; | ||
605 | } | ||
606 | remoteClient.SendAvatarPicksReply(new UUID(args[0]), picks); | ||
607 | } | ||
608 | |||
609 | /// <summary> | ||
610 | /// Handles the pick info request. | ||
611 | /// </summary> | ||
612 | /// <param name='sender'> | ||
613 | /// Sender. | ||
614 | /// </param> | ||
615 | /// <param name='method'> | ||
616 | /// Method. | ||
617 | /// </param> | ||
618 | /// <param name='args'> | ||
619 | /// Arguments. | ||
620 | /// </param> | ||
621 | public void PickInfoRequest(Object sender, string method, List<String> args) | ||
622 | { | ||
623 | if (!(sender is IClientAPI)) | ||
624 | return; | ||
625 | |||
626 | UUID targetID; | ||
627 | UUID.TryParse (args [0], out targetID); | ||
628 | string serverURI = string.Empty; | ||
629 | GetUserProfileServerURI (targetID, out serverURI); | ||
630 | |||
631 | string theirGatekeeperURI; | ||
632 | GetUserGatekeeperURI (targetID, out theirGatekeeperURI); | ||
633 | |||
634 | IClientAPI remoteClient = (IClientAPI)sender; | ||
635 | |||
636 | UserProfilePick pick = new UserProfilePick (); | ||
637 | UUID.TryParse (args [0], out pick.CreatorId); | ||
638 | UUID.TryParse (args [1], out pick.PickId); | ||
639 | |||
640 | |||
641 | object Pick = (object)pick; | ||
642 | if (!rpc.JsonRpcRequest (ref Pick, "pickinforequest", serverURI, UUID.Random ().ToString ())) { | ||
643 | remoteClient.SendAgentAlertMessage ( | ||
644 | "Error selecting pick", false); | ||
645 | return; | ||
646 | } | ||
647 | pick = (UserProfilePick)Pick; | ||
648 | |||
649 | Vector3 globalPos = new Vector3(Vector3.Zero); | ||
650 | |||
651 | // Smoke and mirrors | ||
652 | if (pick.Gatekeeper == MyGatekeeper) | ||
653 | { | ||
654 | Vector3.TryParse(pick.GlobalPos,out globalPos); | ||
655 | } | ||
656 | else | ||
657 | { | ||
658 | // Setup the illusion | ||
659 | string region = string.Format("{0} {1}",pick.Gatekeeper,pick.SimName); | ||
660 | GridRegion target = Scene.GridService.GetRegionByName(Scene.RegionInfo.ScopeID, region); | ||
661 | |||
662 | if(target == null) | ||
663 | { | ||
664 | // This is a dead or unreachable region | ||
665 | } | ||
666 | else | ||
667 | { | ||
668 | // Work our slight of hand | ||
669 | int x = target.RegionLocX; | ||
670 | int y = target.RegionLocY; | ||
671 | |||
672 | dynamic synthX = globalPos.X - (globalPos.X/Constants.RegionSize) * Constants.RegionSize; | ||
673 | synthX += x; | ||
674 | globalPos.X = synthX; | ||
675 | |||
676 | dynamic synthY = globalPos.Y - (globalPos.Y/Constants.RegionSize) * Constants.RegionSize; | ||
677 | synthY += y; | ||
678 | globalPos.Y = synthY; | ||
679 | } | ||
680 | } | ||
681 | |||
682 | m_log.DebugFormat("[PROFILES]: PickInfoRequest: {0} : {1}", pick.Name.ToString(), pick.SnapshotId.ToString()); | ||
683 | |||
684 | // Pull the rabbit out of the hat | ||
685 | remoteClient.SendPickInfoReply(pick.PickId,pick.CreatorId,pick.TopPick,pick.ParcelId,pick.Name, | ||
686 | pick.Desc,pick.SnapshotId,pick.ParcelName,pick.OriginalName,pick.SimName, | ||
687 | globalPos,pick.SortOrder,pick.Enabled); | ||
688 | } | ||
689 | |||
690 | /// <summary> | ||
691 | /// Updates the userpicks | ||
692 | /// </summary> | ||
693 | /// <param name='remoteClient'> | ||
694 | /// Remote client. | ||
695 | /// </param> | ||
696 | /// <param name='pickID'> | ||
697 | /// Pick I. | ||
698 | /// </param> | ||
699 | /// <param name='creatorID'> | ||
700 | /// the creator of the pick | ||
701 | /// </param> | ||
702 | /// <param name='topPick'> | ||
703 | /// Top pick. | ||
704 | /// </param> | ||
705 | /// <param name='name'> | ||
706 | /// Name. | ||
707 | /// </param> | ||
708 | /// <param name='desc'> | ||
709 | /// Desc. | ||
710 | /// </param> | ||
711 | /// <param name='snapshotID'> | ||
712 | /// Snapshot I. | ||
713 | /// </param> | ||
714 | /// <param name='sortOrder'> | ||
715 | /// Sort order. | ||
716 | /// </param> | ||
717 | /// <param name='enabled'> | ||
718 | /// Enabled. | ||
719 | /// </param> | ||
720 | public void PickInfoUpdate(IClientAPI remoteClient, UUID pickID, UUID creatorID, bool topPick, string name, string desc, UUID snapshotID, int sortOrder, bool enabled) | ||
721 | { | ||
722 | //TODO: See how this works with NPC, May need to test | ||
723 | m_log.DebugFormat("[PROFILES]: Start PickInfoUpdate Name: {0} PickId: {1} SnapshotId: {2}", name, pickID.ToString(), snapshotID.ToString()); | ||
724 | |||
725 | UserProfilePick pick = new UserProfilePick(); | ||
726 | string serverURI = string.Empty; | ||
727 | GetUserProfileServerURI(remoteClient.AgentId, out serverURI); | ||
728 | ScenePresence p = FindPresence(remoteClient.AgentId); | ||
729 | |||
730 | Vector3 avaPos = p.AbsolutePosition; | ||
731 | // Getting the global position for the Avatar | ||
732 | Vector3 posGlobal = new Vector3(remoteClient.Scene.RegionInfo.WorldLocX + avaPos.X, | ||
733 | remoteClient.Scene.RegionInfo.WorldLocY + avaPos.Y, | ||
734 | avaPos.Z); | ||
735 | |||
736 | string landParcelName = "My Parcel"; | ||
737 | UUID landParcelID = p.currentParcelUUID; | ||
738 | |||
739 | ILandObject land = p.Scene.LandChannel.GetLandObject(avaPos.X, avaPos.Y); | ||
740 | |||
741 | if (land != null) | ||
742 | { | ||
743 | // If land found, use parcel uuid from here because the value from SP will be blank if the avatar hasnt moved | ||
744 | landParcelName = land.LandData.Name; | ||
745 | landParcelID = land.LandData.GlobalID; | ||
746 | } | ||
747 | else | ||
748 | { | ||
749 | m_log.WarnFormat( | ||
750 | "[PROFILES]: PickInfoUpdate found no parcel info at {0},{1} in {2}", | ||
751 | avaPos.X, avaPos.Y, p.Scene.Name); | ||
752 | } | ||
753 | |||
754 | |||
755 | pick.PickId = pickID; | ||
756 | pick.CreatorId = creatorID; | ||
757 | pick.TopPick = topPick; | ||
758 | pick.Name = name; | ||
759 | pick.Desc = desc; | ||
760 | pick.ParcelId = landParcelID; | ||
761 | pick.SnapshotId = snapshotID; | ||
762 | pick.ParcelName = landParcelName; | ||
763 | pick.SimName = remoteClient.Scene.RegionInfo.RegionName; | ||
764 | pick.Gatekeeper = MyGatekeeper; | ||
765 | pick.GlobalPos = posGlobal.ToString(); | ||
766 | pick.SortOrder = sortOrder; | ||
767 | pick.Enabled = enabled; | ||
768 | |||
769 | object Pick = (object)pick; | ||
770 | if(!rpc.JsonRpcRequest(ref Pick, "picks_update", serverURI, UUID.Random().ToString())) | ||
771 | { | ||
772 | remoteClient.SendAgentAlertMessage( | ||
773 | "Error updating pick", false); | ||
774 | return; | ||
775 | } | ||
776 | |||
777 | m_log.DebugFormat("[PROFILES]: Finish PickInfoUpdate {0} {1}", pick.Name, pick.PickId.ToString()); | ||
778 | } | ||
779 | |||
780 | /// <summary> | ||
781 | /// Delete a Pick | ||
782 | /// </summary> | ||
783 | /// <param name='remoteClient'> | ||
784 | /// Remote client. | ||
785 | /// </param> | ||
786 | /// <param name='queryPickID'> | ||
787 | /// Query pick I. | ||
788 | /// </param> | ||
789 | public void PickDelete(IClientAPI remoteClient, UUID queryPickID) | ||
790 | { | ||
791 | string serverURI = string.Empty; | ||
792 | GetUserProfileServerURI(remoteClient.AgentId, out serverURI); | ||
793 | |||
794 | OSDMap parameters= new OSDMap(); | ||
795 | parameters.Add("pickId", OSD.FromUUID(queryPickID)); | ||
796 | OSD Params = (OSD)parameters; | ||
797 | if(!rpc.JsonRpcRequest(ref Params, "picks_delete", serverURI, UUID.Random().ToString())) | ||
798 | { | ||
799 | remoteClient.SendAgentAlertMessage( | ||
800 | "Error picks delete", false); | ||
801 | return; | ||
802 | } | ||
803 | } | ||
804 | #endregion Picks | ||
805 | |||
806 | #region Notes | ||
807 | /// <summary> | ||
808 | /// Handles the avatar notes request. | ||
809 | /// </summary> | ||
810 | /// <param name='sender'> | ||
811 | /// Sender. | ||
812 | /// </param> | ||
813 | /// <param name='method'> | ||
814 | /// Method. | ||
815 | /// </param> | ||
816 | /// <param name='args'> | ||
817 | /// Arguments. | ||
818 | /// </param> | ||
819 | public void NotesRequest(Object sender, string method, List<String> args) | ||
820 | { | ||
821 | UserProfileNotes note = new UserProfileNotes(); | ||
822 | |||
823 | if (!(sender is IClientAPI)) | ||
824 | return; | ||
825 | |||
826 | IClientAPI remoteClient = (IClientAPI)sender; | ||
827 | string serverURI = string.Empty; | ||
828 | GetUserProfileServerURI(remoteClient.AgentId, out serverURI); | ||
829 | note.UserId = remoteClient.AgentId; | ||
830 | UUID.TryParse(args[0], out note.TargetId); | ||
831 | |||
832 | object Note = (object)note; | ||
833 | if(!rpc.JsonRpcRequest(ref Note, "avatarnotesrequest", serverURI, UUID.Random().ToString())) | ||
834 | { | ||
835 | remoteClient.SendAvatarNotesReply(note.TargetId, note.Notes); | ||
836 | return; | ||
837 | } | ||
838 | note = (UserProfileNotes) Note; | ||
839 | |||
840 | remoteClient.SendAvatarNotesReply(note.TargetId, note.Notes); | ||
841 | } | ||
842 | |||
843 | /// <summary> | ||
844 | /// Avatars the notes update. | ||
845 | /// </summary> | ||
846 | /// <param name='remoteClient'> | ||
847 | /// Remote client. | ||
848 | /// </param> | ||
849 | /// <param name='queryTargetID'> | ||
850 | /// Query target I. | ||
851 | /// </param> | ||
852 | /// <param name='queryNotes'> | ||
853 | /// Query notes. | ||
854 | /// </param> | ||
855 | public void NotesUpdate(IClientAPI remoteClient, UUID queryTargetID, string queryNotes) | ||
856 | { | ||
857 | UserProfileNotes note = new UserProfileNotes(); | ||
858 | |||
859 | note.UserId = remoteClient.AgentId; | ||
860 | note.TargetId = queryTargetID; | ||
861 | note.Notes = queryNotes; | ||
862 | |||
863 | string serverURI = string.Empty; | ||
864 | GetUserProfileServerURI(remoteClient.AgentId, out serverURI); | ||
865 | |||
866 | object Note = note; | ||
867 | if(!rpc.JsonRpcRequest(ref Note, "avatar_notes_update", serverURI, UUID.Random().ToString())) | ||
868 | { | ||
869 | remoteClient.SendAgentAlertMessage( | ||
870 | "Error updating note", false); | ||
871 | return; | ||
872 | } | ||
873 | } | ||
874 | #endregion Notes | ||
875 | |||
876 | #region User Preferences | ||
877 | /// <summary> | ||
878 | /// Updates the user preferences. | ||
879 | /// </summary> | ||
880 | /// <param name='imViaEmail'> | ||
881 | /// Im via email. | ||
882 | /// </param> | ||
883 | /// <param name='visible'> | ||
884 | /// Visible. | ||
885 | /// </param> | ||
886 | /// <param name='remoteClient'> | ||
887 | /// Remote client. | ||
888 | /// </param> | ||
889 | public void UpdateUserPreferences(bool imViaEmail, bool visible, IClientAPI remoteClient) | ||
890 | { | ||
891 | UserPreferences pref = new UserPreferences(); | ||
892 | |||
893 | pref.UserId = remoteClient.AgentId; | ||
894 | pref.IMViaEmail = imViaEmail; | ||
895 | pref.Visible = visible; | ||
896 | |||
897 | string serverURI = string.Empty; | ||
898 | bool foreign = GetUserProfileServerURI(remoteClient.AgentId, out serverURI); | ||
899 | |||
900 | object Pref = pref; | ||
901 | if(!rpc.JsonRpcRequest(ref Pref, "user_preferences_update", serverURI, UUID.Random().ToString())) | ||
902 | { | ||
903 | m_log.InfoFormat("[PROFILES]: UserPreferences update error"); | ||
904 | remoteClient.SendAgentAlertMessage("Error updating preferences", false); | ||
905 | return; | ||
906 | } | ||
907 | } | ||
908 | |||
909 | /// <summary> | ||
910 | /// Users the preferences request. | ||
911 | /// </summary> | ||
912 | /// <param name='remoteClient'> | ||
913 | /// Remote client. | ||
914 | /// </param> | ||
915 | public void UserPreferencesRequest(IClientAPI remoteClient) | ||
916 | { | ||
917 | UserPreferences pref = new UserPreferences(); | ||
918 | |||
919 | pref.UserId = remoteClient.AgentId; | ||
920 | |||
921 | string serverURI = string.Empty; | ||
922 | bool foreign = GetUserProfileServerURI(remoteClient.AgentId, out serverURI); | ||
923 | |||
924 | |||
925 | object Pref = (object)pref; | ||
926 | if(!rpc.JsonRpcRequest(ref Pref, "user_preferences_request", serverURI, UUID.Random().ToString())) | ||
927 | { | ||
928 | // m_log.InfoFormat("[PROFILES]: UserPreferences request error"); | ||
929 | // remoteClient.SendAgentAlertMessage("Error requesting preferences", false); | ||
930 | return; | ||
931 | } | ||
932 | pref = (UserPreferences) Pref; | ||
933 | |||
934 | remoteClient.SendUserInfoReply(pref.IMViaEmail, pref.Visible, pref.EMail); | ||
935 | |||
936 | } | ||
937 | #endregion User Preferences | ||
938 | |||
939 | #region Avatar Properties | ||
940 | /// <summary> | ||
941 | /// Update the avatars interests . | ||
942 | /// </summary> | ||
943 | /// <param name='remoteClient'> | ||
944 | /// Remote client. | ||
945 | /// </param> | ||
946 | /// <param name='wantmask'> | ||
947 | /// Wantmask. | ||
948 | /// </param> | ||
949 | /// <param name='wanttext'> | ||
950 | /// Wanttext. | ||
951 | /// </param> | ||
952 | /// <param name='skillsmask'> | ||
953 | /// Skillsmask. | ||
954 | /// </param> | ||
955 | /// <param name='skillstext'> | ||
956 | /// Skillstext. | ||
957 | /// </param> | ||
958 | /// <param name='languages'> | ||
959 | /// Languages. | ||
960 | /// </param> | ||
961 | public void AvatarInterestsUpdate(IClientAPI remoteClient, uint wantmask, string wanttext, uint skillsmask, string skillstext, string languages) | ||
962 | { | ||
963 | UserProfileProperties prop = new UserProfileProperties(); | ||
964 | |||
965 | prop.UserId = remoteClient.AgentId; | ||
966 | prop.WantToMask = (int)wantmask; | ||
967 | prop.WantToText = wanttext; | ||
968 | prop.SkillsMask = (int)skillsmask; | ||
969 | prop.SkillsText = skillstext; | ||
970 | prop.Language = languages; | ||
971 | |||
972 | string serverURI = string.Empty; | ||
973 | GetUserProfileServerURI(remoteClient.AgentId, out serverURI); | ||
974 | |||
975 | object Param = prop; | ||
976 | if(!rpc.JsonRpcRequest(ref Param, "avatar_interests_update", serverURI, UUID.Random().ToString())) | ||
977 | { | ||
978 | remoteClient.SendAgentAlertMessage( | ||
979 | "Error updating interests", false); | ||
980 | return; | ||
981 | } | ||
982 | } | ||
983 | |||
984 | public void RequestAvatarProperties(IClientAPI remoteClient, UUID avatarID) | ||
985 | { | ||
986 | if (String.IsNullOrEmpty(avatarID.ToString()) || String.IsNullOrEmpty(remoteClient.AgentId.ToString())) | ||
987 | { | ||
988 | // Looking for a reason that some viewers are sending null Id's | ||
989 | m_log.DebugFormat("[PROFILES]: This should not happen remoteClient.AgentId {0} - avatarID {1}", remoteClient.AgentId, avatarID); | ||
990 | return; | ||
991 | } | ||
992 | |||
993 | // Can't handle NPC yet... | ||
994 | ScenePresence p = FindPresence(avatarID); | ||
995 | |||
996 | if (null != p) | ||
997 | { | ||
998 | if (p.PresenceType == PresenceType.Npc) | ||
999 | return; | ||
1000 | } | ||
1001 | |||
1002 | string serverURI = string.Empty; | ||
1003 | bool foreign = GetUserProfileServerURI(avatarID, out serverURI); | ||
1004 | |||
1005 | UserAccount account = null; | ||
1006 | Dictionary<string,object> userInfo; | ||
1007 | |||
1008 | if (!foreign) | ||
1009 | { | ||
1010 | account = Scene.UserAccountService.GetUserAccount(Scene.RegionInfo.ScopeID, avatarID); | ||
1011 | } | ||
1012 | else | ||
1013 | { | ||
1014 | userInfo = new Dictionary<string, object>(); | ||
1015 | } | ||
1016 | |||
1017 | Byte[] charterMember = new Byte[1]; | ||
1018 | string born = String.Empty; | ||
1019 | uint flags = 0x00; | ||
1020 | |||
1021 | if (null != account) | ||
1022 | { | ||
1023 | if (account.UserTitle == "") | ||
1024 | { | ||
1025 | charterMember[0] = (Byte)((account.UserFlags & 0xf00) >> 8); | ||
1026 | } | ||
1027 | else | ||
1028 | { | ||
1029 | charterMember = Utils.StringToBytes(account.UserTitle); | ||
1030 | } | ||
1031 | |||
1032 | born = Util.ToDateTime(account.Created).ToString( | ||
1033 | "M/d/yyyy", CultureInfo.InvariantCulture); | ||
1034 | flags = (uint)(account.UserFlags & 0xff); | ||
1035 | } | ||
1036 | else | ||
1037 | { | ||
1038 | if (GetUserAccountData(avatarID, out userInfo) == true) | ||
1039 | { | ||
1040 | if ((string)userInfo["user_title"] == "") | ||
1041 | { | ||
1042 | charterMember[0] = (Byte)(((Byte)userInfo["user_flags"] & 0xf00) >> 8); | ||
1043 | } | ||
1044 | else | ||
1045 | { | ||
1046 | charterMember = Utils.StringToBytes((string)userInfo["user_title"]); | ||
1047 | } | ||
1048 | |||
1049 | int val_born = (int)userInfo["user_created"]; | ||
1050 | born = Util.ToDateTime(val_born).ToString( | ||
1051 | "M/d/yyyy", CultureInfo.InvariantCulture); | ||
1052 | |||
1053 | // picky, picky | ||
1054 | int val_flags = (int)userInfo["user_flags"]; | ||
1055 | flags = (uint)(val_flags & 0xff); | ||
1056 | } | ||
1057 | } | ||
1058 | |||
1059 | UserProfileProperties props = new UserProfileProperties(); | ||
1060 | string result = string.Empty; | ||
1061 | |||
1062 | props.UserId = avatarID; | ||
1063 | |||
1064 | if (!GetProfileData(ref props, foreign, out result)) | ||
1065 | { | ||
1066 | // m_log.DebugFormat("Error getting profile for {0}: {1}", avatarID, result); | ||
1067 | return; | ||
1068 | } | ||
1069 | |||
1070 | remoteClient.SendAvatarProperties(props.UserId, props.AboutText, born, charterMember , props.FirstLifeText, flags, | ||
1071 | props.FirstLifeImageId, props.ImageId, props.WebUrl, props.PartnerId); | ||
1072 | |||
1073 | |||
1074 | remoteClient.SendAvatarInterestsReply(props.UserId, (uint)props.WantToMask, props.WantToText, (uint)props.SkillsMask, | ||
1075 | props.SkillsText, props.Language); | ||
1076 | } | ||
1077 | |||
1078 | /// <summary> | ||
1079 | /// Updates the avatar properties. | ||
1080 | /// </summary> | ||
1081 | /// <param name='remoteClient'> | ||
1082 | /// Remote client. | ||
1083 | /// </param> | ||
1084 | /// <param name='newProfile'> | ||
1085 | /// New profile. | ||
1086 | /// </param> | ||
1087 | public void AvatarPropertiesUpdate(IClientAPI remoteClient, UserProfileData newProfile) | ||
1088 | { | ||
1089 | if (remoteClient.AgentId == newProfile.ID) | ||
1090 | { | ||
1091 | UserProfileProperties prop = new UserProfileProperties(); | ||
1092 | |||
1093 | prop.UserId = remoteClient.AgentId; | ||
1094 | prop.WebUrl = newProfile.ProfileUrl; | ||
1095 | prop.ImageId = newProfile.Image; | ||
1096 | prop.AboutText = newProfile.AboutText; | ||
1097 | prop.FirstLifeImageId = newProfile.FirstLifeImage; | ||
1098 | prop.FirstLifeText = newProfile.FirstLifeAboutText; | ||
1099 | |||
1100 | string serverURI = string.Empty; | ||
1101 | GetUserProfileServerURI(remoteClient.AgentId, out serverURI); | ||
1102 | |||
1103 | object Prop = prop; | ||
1104 | |||
1105 | if(!rpc.JsonRpcRequest(ref Prop, "avatar_properties_update", serverURI, UUID.Random().ToString())) | ||
1106 | { | ||
1107 | remoteClient.SendAgentAlertMessage( | ||
1108 | "Error updating properties", false); | ||
1109 | return; | ||
1110 | } | ||
1111 | |||
1112 | RequestAvatarProperties(remoteClient, newProfile.ID); | ||
1113 | } | ||
1114 | } | ||
1115 | |||
1116 | /// <summary> | ||
1117 | /// Gets the profile data. | ||
1118 | /// </summary> | ||
1119 | /// <returns> | ||
1120 | /// The profile data. | ||
1121 | /// </returns> | ||
1122 | bool GetProfileData(ref UserProfileProperties properties, bool foreign, out string message) | ||
1123 | { | ||
1124 | // Can't handle NPC yet... | ||
1125 | ScenePresence p = FindPresence(properties.UserId); | ||
1126 | |||
1127 | if (null != p) | ||
1128 | { | ||
1129 | if (p.PresenceType == PresenceType.Npc) | ||
1130 | { | ||
1131 | message = "Id points to NPC"; | ||
1132 | return false; | ||
1133 | } | ||
1134 | } | ||
1135 | |||
1136 | string serverURI = string.Empty; | ||
1137 | GetUserProfileServerURI(properties.UserId, out serverURI); | ||
1138 | |||
1139 | // This is checking a friend on the home grid | ||
1140 | // Not HG friend | ||
1141 | if (String.IsNullOrEmpty(serverURI)) | ||
1142 | { | ||
1143 | message = "No Presence - foreign friend"; | ||
1144 | return false; | ||
1145 | } | ||
1146 | |||
1147 | object Prop = (object)properties; | ||
1148 | if (!rpc.JsonRpcRequest(ref Prop, "avatar_properties_request", serverURI, UUID.Random().ToString())) | ||
1149 | { | ||
1150 | // If it's a foreign user then try again using OpenProfile, in case that's what the grid is using | ||
1151 | bool secondChanceSuccess = false; | ||
1152 | if (foreign) | ||
1153 | { | ||
1154 | try | ||
1155 | { | ||
1156 | OpenProfileClient client = new OpenProfileClient(serverURI); | ||
1157 | if (client.RequestAvatarPropertiesUsingOpenProfile(ref properties)) | ||
1158 | secondChanceSuccess = true; | ||
1159 | } | ||
1160 | catch (Exception e) | ||
1161 | { | ||
1162 | m_log.Debug( | ||
1163 | string.Format( | ||
1164 | "[PROFILES]: Request using the OpenProfile API for user {0} to {1} failed", | ||
1165 | properties.UserId, serverURI), | ||
1166 | e); | ||
1167 | |||
1168 | // Allow the return 'message' to say "JsonRpcRequest" and not "OpenProfile", because | ||
1169 | // the most likely reason that OpenProfile failed is that the remote server | ||
1170 | // doesn't support OpenProfile, and that's not very interesting. | ||
1171 | } | ||
1172 | } | ||
1173 | |||
1174 | if (!secondChanceSuccess) | ||
1175 | { | ||
1176 | message = string.Format("JsonRpcRequest for user {0} to {1} failed", properties.UserId, serverURI); | ||
1177 | m_log.DebugFormat("[PROFILES]: {0}", message); | ||
1178 | |||
1179 | return false; | ||
1180 | } | ||
1181 | // else, continue below | ||
1182 | } | ||
1183 | |||
1184 | properties = (UserProfileProperties)Prop; | ||
1185 | |||
1186 | message = "Success"; | ||
1187 | return true; | ||
1188 | } | ||
1189 | #endregion Avatar Properties | ||
1190 | |||
1191 | #region Utils | ||
1192 | bool GetImageAssets(UUID avatarId) | ||
1193 | { | ||
1194 | string profileServerURI = string.Empty; | ||
1195 | string assetServerURI = string.Empty; | ||
1196 | |||
1197 | bool foreign = GetUserProfileServerURI(avatarId, out profileServerURI); | ||
1198 | |||
1199 | if(!foreign) | ||
1200 | return true; | ||
1201 | |||
1202 | assetServerURI = UserManagementModule.GetUserServerURL(avatarId, "AssetServerURI"); | ||
1203 | |||
1204 | if(string.IsNullOrEmpty(profileServerURI) || string.IsNullOrEmpty(assetServerURI)) | ||
1205 | return false; | ||
1206 | |||
1207 | OSDMap parameters= new OSDMap(); | ||
1208 | parameters.Add("avatarId", OSD.FromUUID(avatarId)); | ||
1209 | OSD Params = (OSD)parameters; | ||
1210 | if(!rpc.JsonRpcRequest(ref Params, "image_assets_request", profileServerURI, UUID.Random().ToString())) | ||
1211 | { | ||
1212 | return false; | ||
1213 | } | ||
1214 | |||
1215 | parameters = (OSDMap)Params; | ||
1216 | |||
1217 | if (parameters.ContainsKey("result")) | ||
1218 | { | ||
1219 | OSDArray list = (OSDArray)parameters["result"]; | ||
1220 | |||
1221 | foreach (OSD asset in list) | ||
1222 | { | ||
1223 | OSDString assetId = (OSDString)asset; | ||
1224 | |||
1225 | Scene.AssetService.Get(string.Format("{0}/{1}", assetServerURI, assetId.AsString())); | ||
1226 | } | ||
1227 | return true; | ||
1228 | } | ||
1229 | else | ||
1230 | { | ||
1231 | m_log.ErrorFormat("[PROFILES]: Problematic response for image_assets_request from {0}", profileServerURI); | ||
1232 | return false; | ||
1233 | } | ||
1234 | } | ||
1235 | |||
1236 | /// <summary> | ||
1237 | /// Gets the user account data. | ||
1238 | /// </summary> | ||
1239 | /// <returns> | ||
1240 | /// The user profile data. | ||
1241 | /// </returns> | ||
1242 | /// <param name='userID'> | ||
1243 | /// If set to <c>true</c> user I. | ||
1244 | /// </param> | ||
1245 | /// <param name='userInfo'> | ||
1246 | /// If set to <c>true</c> user info. | ||
1247 | /// </param> | ||
1248 | bool GetUserAccountData(UUID userID, out Dictionary<string, object> userInfo) | ||
1249 | { | ||
1250 | Dictionary<string,object> info = new Dictionary<string, object>(); | ||
1251 | |||
1252 | if (UserManagementModule.IsLocalGridUser(userID)) | ||
1253 | { | ||
1254 | // Is local | ||
1255 | IUserAccountService uas = Scene.UserAccountService; | ||
1256 | UserAccount account = uas.GetUserAccount(Scene.RegionInfo.ScopeID, userID); | ||
1257 | |||
1258 | info["user_flags"] = account.UserFlags; | ||
1259 | info["user_created"] = account.Created; | ||
1260 | |||
1261 | if (!String.IsNullOrEmpty(account.UserTitle)) | ||
1262 | info["user_title"] = account.UserTitle; | ||
1263 | else | ||
1264 | info["user_title"] = ""; | ||
1265 | |||
1266 | userInfo = info; | ||
1267 | |||
1268 | return false; | ||
1269 | } | ||
1270 | else | ||
1271 | { | ||
1272 | // Is Foreign | ||
1273 | string home_url = UserManagementModule.GetUserServerURL(userID, "HomeURI"); | ||
1274 | |||
1275 | if (String.IsNullOrEmpty(home_url)) | ||
1276 | { | ||
1277 | info["user_flags"] = 0; | ||
1278 | info["user_created"] = 0; | ||
1279 | info["user_title"] = "Unavailable"; | ||
1280 | |||
1281 | userInfo = info; | ||
1282 | return true; | ||
1283 | } | ||
1284 | |||
1285 | UserAgentServiceConnector uConn = new UserAgentServiceConnector(home_url); | ||
1286 | |||
1287 | Dictionary<string, object> account; | ||
1288 | try | ||
1289 | { | ||
1290 | account = uConn.GetUserInfo(userID); | ||
1291 | } | ||
1292 | catch (Exception e) | ||
1293 | { | ||
1294 | m_log.Debug("[PROFILES]: GetUserInfo call failed ", e); | ||
1295 | account = new Dictionary<string, object>(); | ||
1296 | } | ||
1297 | |||
1298 | if (account.Count > 0) | ||
1299 | { | ||
1300 | if (account.ContainsKey("user_flags")) | ||
1301 | info["user_flags"] = account["user_flags"]; | ||
1302 | else | ||
1303 | info["user_flags"] = ""; | ||
1304 | |||
1305 | if (account.ContainsKey("user_created")) | ||
1306 | info["user_created"] = account["user_created"]; | ||
1307 | else | ||
1308 | info["user_created"] = ""; | ||
1309 | |||
1310 | info["user_title"] = "HG Visitor"; | ||
1311 | } | ||
1312 | else | ||
1313 | { | ||
1314 | info["user_flags"] = 0; | ||
1315 | info["user_created"] = 0; | ||
1316 | info["user_title"] = "HG Visitor"; | ||
1317 | } | ||
1318 | userInfo = info; | ||
1319 | return true; | ||
1320 | } | ||
1321 | } | ||
1322 | |||
1323 | /// <summary> | ||
1324 | /// Gets the user gatekeeper server URI. | ||
1325 | /// </summary> | ||
1326 | /// <returns> | ||
1327 | /// The user gatekeeper server URI. | ||
1328 | /// </returns> | ||
1329 | /// <param name='userID'> | ||
1330 | /// If set to <c>true</c> user URI. | ||
1331 | /// </param> | ||
1332 | /// <param name='serverURI'> | ||
1333 | /// If set to <c>true</c> server URI. | ||
1334 | /// </param> | ||
1335 | bool GetUserGatekeeperURI(UUID userID, out string serverURI) | ||
1336 | { | ||
1337 | bool local; | ||
1338 | local = UserManagementModule.IsLocalGridUser(userID); | ||
1339 | |||
1340 | if (!local) | ||
1341 | { | ||
1342 | serverURI = UserManagementModule.GetUserServerURL(userID, "GatekeeperURI"); | ||
1343 | // Is Foreign | ||
1344 | return true; | ||
1345 | } | ||
1346 | else | ||
1347 | { | ||
1348 | serverURI = MyGatekeeper; | ||
1349 | // Is local | ||
1350 | return false; | ||
1351 | } | ||
1352 | } | ||
1353 | |||
1354 | /// <summary> | ||
1355 | /// Gets the user profile server UR. | ||
1356 | /// </summary> | ||
1357 | /// <returns> | ||
1358 | /// The user profile server UR. | ||
1359 | /// </returns> | ||
1360 | /// <param name='userID'> | ||
1361 | /// If set to <c>true</c> user I. | ||
1362 | /// </param> | ||
1363 | /// <param name='serverURI'> | ||
1364 | /// If set to <c>true</c> server UR. | ||
1365 | /// </param> | ||
1366 | bool GetUserProfileServerURI(UUID userID, out string serverURI) | ||
1367 | { | ||
1368 | bool local; | ||
1369 | local = UserManagementModule.IsLocalGridUser(userID); | ||
1370 | |||
1371 | if (!local) | ||
1372 | { | ||
1373 | serverURI = UserManagementModule.GetUserServerURL(userID, "ProfileServerURI"); | ||
1374 | // Is Foreign | ||
1375 | return true; | ||
1376 | } | ||
1377 | else | ||
1378 | { | ||
1379 | serverURI = ProfileServerUri; | ||
1380 | // Is local | ||
1381 | return false; | ||
1382 | } | ||
1383 | } | ||
1384 | |||
1385 | /// <summary> | ||
1386 | /// Finds the presence. | ||
1387 | /// </summary> | ||
1388 | /// <returns> | ||
1389 | /// The presence. | ||
1390 | /// </returns> | ||
1391 | /// <param name='clientID'> | ||
1392 | /// Client I. | ||
1393 | /// </param> | ||
1394 | ScenePresence FindPresence(UUID clientID) | ||
1395 | { | ||
1396 | ScenePresence p; | ||
1397 | |||
1398 | p = Scene.GetScenePresence(clientID); | ||
1399 | if (p != null && !p.IsChildAgent) | ||
1400 | return p; | ||
1401 | |||
1402 | return null; | ||
1403 | } | ||
1404 | #endregion Util | ||
1405 | } | ||
1406 | } | ||