opensim – Blame information for rev 1
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
1 | eva | 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.Threading; |
||
31 | using OpenMetaverse; |
||
32 | using OpenMetaverse.Packets; |
||
33 | using OpenSim.Framework; |
||
34 | using OpenSim.Framework.Communications; |
||
35 | using OpenSim.Services.Interfaces; |
||
36 | |||
37 | namespace OpenSim.Region.Framework.Scenes |
||
38 | { |
||
39 | public partial class Scene |
||
40 | { |
||
41 | /// <summary> |
||
42 | /// Send chat to listeners. |
||
43 | /// </summary> |
||
44 | /// <param name='message'></param> |
||
45 | /// <param name='type'>/param> |
||
46 | /// <param name='channel'></param> |
||
47 | /// <param name='fromPos'></param> |
||
48 | /// <param name='fromName'></param> |
||
49 | /// <param name='fromID'></param> |
||
50 | /// <param name='targetID'></param> |
||
51 | /// <param name='fromAgent'></param> |
||
52 | /// <param name='broadcast'></param> |
||
53 | protected void SimChat(byte[] message, ChatTypeEnum type, int channel, Vector3 fromPos, string fromName, |
||
54 | UUID fromID, UUID targetID, bool fromAgent, bool broadcast) |
||
55 | { |
||
56 | OSChatMessage args = new OSChatMessage(); |
||
57 | |||
58 | args.Message = Utils.BytesToString(message); |
||
59 | args.Channel = channel; |
||
60 | args.Type = type; |
||
61 | args.Position = fromPos; |
||
62 | args.SenderUUID = fromID; |
||
63 | args.Scene = this; |
||
64 | |||
65 | if (fromAgent) |
||
66 | { |
||
67 | ScenePresence user = GetScenePresence(fromID); |
||
68 | if (user != null) |
||
69 | args.Sender = user.ControllingClient; |
||
70 | } |
||
71 | else |
||
72 | { |
||
73 | SceneObjectPart obj = GetSceneObjectPart(fromID); |
||
74 | args.SenderObject = obj; |
||
75 | } |
||
76 | |||
77 | args.From = fromName; |
||
78 | args.TargetUUID = targetID; |
||
79 | |||
80 | // m_log.DebugFormat( |
||
81 | // "[SCENE]: Sending message {0} on channel {1}, type {2} from {3}, broadcast {4}", |
||
82 | // args.Message.Replace("\n", "\\n"), args.Channel, args.Type, fromName, broadcast); |
||
83 | |||
84 | if (broadcast) |
||
85 | EventManager.TriggerOnChatBroadcast(this, args); |
||
86 | else |
||
87 | EventManager.TriggerOnChatFromWorld(this, args); |
||
88 | } |
||
89 | |||
90 | protected void SimChat(byte[] message, ChatTypeEnum type, int channel, Vector3 fromPos, string fromName, |
||
91 | UUID fromID, bool fromAgent, bool broadcast) |
||
92 | { |
||
93 | SimChat(message, type, channel, fromPos, fromName, fromID, UUID.Zero, fromAgent, broadcast); |
||
94 | } |
||
95 | |||
96 | /// <summary> |
||
97 | /// |
||
98 | /// </summary> |
||
99 | /// <param name="message"></param> |
||
100 | /// <param name="type"></param> |
||
101 | /// <param name="fromPos"></param> |
||
102 | /// <param name="fromName"></param> |
||
103 | /// <param name="fromAgentID"></param> |
||
104 | public void SimChat(byte[] message, ChatTypeEnum type, int channel, Vector3 fromPos, string fromName, |
||
105 | UUID fromID, bool fromAgent) |
||
106 | { |
||
107 | SimChat(message, type, channel, fromPos, fromName, fromID, fromAgent, false); |
||
108 | } |
||
109 | |||
110 | public void SimChat(string message, ChatTypeEnum type, Vector3 fromPos, string fromName, UUID fromID, bool fromAgent) |
||
111 | { |
||
112 | SimChat(Utils.StringToBytes(message), type, 0, fromPos, fromName, fromID, fromAgent); |
||
113 | } |
||
114 | |||
115 | public void SimChat(string message, string fromName) |
||
116 | { |
||
117 | SimChat(message, ChatTypeEnum.Broadcast, Vector3.Zero, fromName, UUID.Zero, false); |
||
118 | } |
||
119 | |||
120 | /// <summary> |
||
121 | /// |
||
122 | /// </summary> |
||
123 | /// <param name="message"></param> |
||
124 | /// <param name="type"></param> |
||
125 | /// <param name="fromPos"></param> |
||
126 | /// <param name="fromName"></param> |
||
127 | /// <param name="fromAgentID"></param> |
||
128 | public void SimChatBroadcast(byte[] message, ChatTypeEnum type, int channel, Vector3 fromPos, string fromName, |
||
129 | UUID fromID, bool fromAgent) |
||
130 | { |
||
131 | SimChat(message, type, channel, fromPos, fromName, fromID, fromAgent, true); |
||
132 | } |
||
133 | /// <summary> |
||
134 | /// |
||
135 | /// </summary> |
||
136 | /// <param name="message"></param> |
||
137 | /// <param name="type"></param> |
||
138 | /// <param name="fromPos"></param> |
||
139 | /// <param name="fromName"></param> |
||
140 | /// <param name="fromAgentID"></param> |
||
141 | /// <param name="targetID"></param> |
||
142 | public void SimChatToAgent(UUID targetID, byte[] message, Vector3 fromPos, string fromName, UUID fromID, bool fromAgent) |
||
143 | { |
||
144 | SimChat(message, ChatTypeEnum.Say, 0, fromPos, fromName, fromID, targetID, fromAgent, false); |
||
145 | } |
||
146 | |||
147 | /// <summary> |
||
148 | /// Invoked when the client requests a prim. |
||
149 | /// </summary> |
||
150 | /// <param name="primLocalID"></param> |
||
151 | /// <param name="remoteClient"></param> |
||
152 | public void RequestPrim(uint primLocalID, IClientAPI remoteClient) |
||
153 | { |
||
154 | SceneObjectGroup sog = GetGroupByPrim(primLocalID); |
||
155 | |||
156 | if (sog != null) |
||
157 | sog.SendFullUpdateToClient(remoteClient); |
||
158 | } |
||
159 | |||
160 | /// <summary> |
||
161 | /// Invoked when the client selects a prim. |
||
162 | /// </summary> |
||
163 | /// <param name="primLocalID"></param> |
||
164 | /// <param name="remoteClient"></param> |
||
165 | public void SelectPrim(uint primLocalID, IClientAPI remoteClient) |
||
166 | { |
||
167 | SceneObjectPart part = GetSceneObjectPart(primLocalID); |
||
168 | |||
169 | if (null == part) |
||
170 | return; |
||
171 | |||
172 | if (part.IsRoot) |
||
173 | { |
||
174 | SceneObjectGroup sog = part.ParentGroup; |
||
175 | sog.SendPropertiesToClient(remoteClient); |
||
176 | sog.IsSelected = true; |
||
177 | |||
178 | // A prim is only tainted if it's allowed to be edited by the person clicking it. |
||
179 | if (Permissions.CanEditObject(sog.UUID, remoteClient.AgentId) |
||
180 | || Permissions.CanMoveObject(sog.UUID, remoteClient.AgentId)) |
||
181 | { |
||
182 | EventManager.TriggerParcelPrimCountTainted(); |
||
183 | } |
||
184 | } |
||
185 | else |
||
186 | { |
||
187 | part.SendPropertiesToClient(remoteClient); |
||
188 | } |
||
189 | } |
||
190 | |||
191 | /// <summary> |
||
192 | /// Handle the update of an object's user group. |
||
193 | /// </summary> |
||
194 | /// <param name="remoteClient"></param> |
||
195 | /// <param name="groupID"></param> |
||
196 | /// <param name="objectLocalID"></param> |
||
197 | /// <param name="Garbage"></param> |
||
198 | private void HandleObjectGroupUpdate( |
||
199 | IClientAPI remoteClient, UUID groupID, uint objectLocalID, UUID Garbage) |
||
200 | { |
||
201 | if (m_groupsModule == null) |
||
202 | return; |
||
203 | |||
204 | // XXX: Might be better to get rid of this special casing and have GetMembershipData return something |
||
205 | // reasonable for a UUID.Zero group. |
||
206 | if (groupID != UUID.Zero) |
||
207 | { |
||
208 | GroupMembershipData gmd = m_groupsModule.GetMembershipData(groupID, remoteClient.AgentId); |
||
209 | |||
210 | if (gmd == null) |
||
211 | { |
||
212 | // m_log.WarnFormat( |
||
213 | // "[GROUPS]: User {0} is not a member of group {1} so they can't update {2} to this group", |
||
214 | // remoteClient.Name, GroupID, objectLocalID); |
||
215 | |||
216 | return; |
||
217 | } |
||
218 | } |
||
219 | |||
220 | SceneObjectGroup so = ((Scene)remoteClient.Scene).GetGroupByPrim(objectLocalID); |
||
221 | if (so != null) |
||
222 | { |
||
223 | if (so.OwnerID == remoteClient.AgentId) |
||
224 | { |
||
225 | so.SetGroup(groupID, remoteClient); |
||
226 | } |
||
227 | } |
||
228 | } |
||
229 | |||
230 | /// <summary> |
||
231 | /// Handle the deselection of a prim from the client. |
||
232 | /// </summary> |
||
233 | /// <param name="primLocalID"></param> |
||
234 | /// <param name="remoteClient"></param> |
||
235 | public void DeselectPrim(uint primLocalID, IClientAPI remoteClient) |
||
236 | { |
||
237 | SceneObjectPart part = GetSceneObjectPart(primLocalID); |
||
238 | if (part == null) |
||
239 | return; |
||
240 | |||
241 | // A deselect packet contains all the local prims being deselected. However, since selection is still |
||
242 | // group based we only want the root prim to trigger a full update - otherwise on objects with many prims |
||
243 | // we end up sending many duplicate ObjectUpdates |
||
244 | if (part.ParentGroup.RootPart.LocalId != part.LocalId) |
||
245 | return; |
||
246 | |||
247 | // This is wrong, wrong, wrong. Selection should not be |
||
248 | // handled by group, but by prim. Legacy cruft. |
||
249 | // TODO: Make selection flagging per prim! |
||
250 | // |
||
251 | part.ParentGroup.IsSelected = false; |
||
252 | |||
253 | part.ParentGroup.ScheduleGroupForFullUpdate(); |
||
254 | |||
255 | // If it's not an attachment, and we are allowed to move it, |
||
256 | // then we might have done so. If we moved across a parcel |
||
257 | // boundary, we will need to recount prims on the parcels. |
||
258 | // For attachments, that makes no sense. |
||
259 | // |
||
260 | if (!part.ParentGroup.IsAttachment) |
||
261 | { |
||
262 | if (Permissions.CanEditObject( |
||
263 | part.UUID, remoteClient.AgentId) |
||
264 | || Permissions.CanMoveObject( |
||
265 | part.UUID, remoteClient.AgentId)) |
||
266 | EventManager.TriggerParcelPrimCountTainted(); |
||
267 | } |
||
268 | } |
||
269 | |||
270 | public virtual void ProcessMoneyTransferRequest(UUID source, UUID destination, int amount, |
||
271 | int transactiontype, string description) |
||
272 | { |
||
273 | EventManager.MoneyTransferArgs args = new EventManager.MoneyTransferArgs(source, destination, amount, |
||
274 | transactiontype, description); |
||
275 | |||
276 | EventManager.TriggerMoneyTransfer(this, args); |
||
277 | } |
||
278 | |||
279 | public virtual void ProcessParcelBuy(UUID agentId, UUID groupId, bool final, bool groupOwned, |
||
280 | bool removeContribution, int parcelLocalID, int parcelArea, int parcelPrice, bool authenticated) |
||
281 | { |
||
282 | EventManager.LandBuyArgs args = new EventManager.LandBuyArgs(agentId, groupId, final, groupOwned, |
||
283 | removeContribution, parcelLocalID, parcelArea, |
||
284 | parcelPrice, authenticated); |
||
285 | |||
286 | // First, allow all validators a stab at it |
||
287 | m_eventManager.TriggerValidateLandBuy(this, args); |
||
288 | |||
289 | // Then, check validation and transfer |
||
290 | m_eventManager.TriggerLandBuy(this, args); |
||
291 | } |
||
292 | |||
293 | public virtual void ProcessObjectGrab(uint localID, Vector3 offsetPos, IClientAPI remoteClient, List<SurfaceTouchEventArgs> surfaceArgs) |
||
294 | { |
||
295 | SceneObjectPart part = GetSceneObjectPart(localID); |
||
296 | |||
297 | if (part == null) |
||
298 | return; |
||
299 | |||
300 | SceneObjectGroup obj = part.ParentGroup; |
||
301 | |||
302 | SurfaceTouchEventArgs surfaceArg = null; |
||
303 | if (surfaceArgs != null && surfaceArgs.Count > 0) |
||
304 | surfaceArg = surfaceArgs[0]; |
||
305 | |||
306 | // Currently only grab/touch for the single prim |
||
307 | // the client handles rez correctly |
||
308 | obj.ObjectGrabHandler(localID, offsetPos, remoteClient); |
||
309 | |||
310 | // If the touched prim handles touches, deliver it |
||
311 | // If not, deliver to root prim |
||
312 | if ((part.ScriptEvents & scriptEvents.touch_start) != 0) |
||
313 | EventManager.TriggerObjectGrab(part.LocalId, 0, part.OffsetPosition, remoteClient, surfaceArg); |
||
314 | |||
315 | // Deliver to the root prim if the touched prim doesn't handle touches |
||
316 | // or if we're meant to pass on touches anyway. Don't send to root prim |
||
317 | // if prim touched is the root prim as we just did it |
||
318 | if (((part.ScriptEvents & scriptEvents.touch_start) == 0) || |
||
319 | (part.PassTouches && (part.LocalId != obj.RootPart.LocalId))) |
||
320 | { |
||
321 | EventManager.TriggerObjectGrab(obj.RootPart.LocalId, part.LocalId, part.OffsetPosition, remoteClient, surfaceArg); |
||
322 | } |
||
323 | } |
||
324 | |||
325 | public virtual void ProcessObjectGrabUpdate( |
||
326 | UUID objectID, Vector3 offset, Vector3 pos, IClientAPI remoteClient, List<SurfaceTouchEventArgs> surfaceArgs) |
||
327 | { |
||
328 | SceneObjectPart part = GetSceneObjectPart(objectID); |
||
329 | if (part == null) |
||
330 | return; |
||
331 | |||
332 | SceneObjectGroup obj = part.ParentGroup; |
||
333 | |||
334 | SurfaceTouchEventArgs surfaceArg = null; |
||
335 | if (surfaceArgs != null && surfaceArgs.Count > 0) |
||
336 | surfaceArg = surfaceArgs[0]; |
||
337 | |||
338 | // If the touched prim handles touches, deliver it |
||
339 | // If not, deliver to root prim |
||
340 | if ((part.ScriptEvents & scriptEvents.touch) != 0) |
||
341 | EventManager.TriggerObjectGrabbing(part.LocalId, 0, part.OffsetPosition, remoteClient, surfaceArg); |
||
342 | // Deliver to the root prim if the touched prim doesn't handle touches |
||
343 | // or if we're meant to pass on touches anyway. Don't send to root prim |
||
344 | // if prim touched is the root prim as we just did it |
||
345 | if (((part.ScriptEvents & scriptEvents.touch) == 0) || |
||
346 | (part.PassTouches && (part.LocalId != obj.RootPart.LocalId))) |
||
347 | { |
||
348 | EventManager.TriggerObjectGrabbing(obj.RootPart.LocalId, part.LocalId, part.OffsetPosition, remoteClient, surfaceArg); |
||
349 | } |
||
350 | } |
||
351 | |||
352 | public virtual void ProcessObjectDeGrab(uint localID, IClientAPI remoteClient, List<SurfaceTouchEventArgs> surfaceArgs) |
||
353 | { |
||
354 | SceneObjectPart part = GetSceneObjectPart(localID); |
||
355 | if (part == null) |
||
356 | return; |
||
357 | |||
358 | SceneObjectGroup obj = part.ParentGroup; |
||
359 | |||
360 | SurfaceTouchEventArgs surfaceArg = null; |
||
361 | if (surfaceArgs != null && surfaceArgs.Count > 0) |
||
362 | surfaceArg = surfaceArgs[0]; |
||
363 | |||
364 | // If the touched prim handles touches, deliver it |
||
365 | // If not, deliver to root prim |
||
366 | if ((part.ScriptEvents & scriptEvents.touch_end) != 0) |
||
367 | EventManager.TriggerObjectDeGrab(part.LocalId, 0, remoteClient, surfaceArg); |
||
368 | else |
||
369 | EventManager.TriggerObjectDeGrab(obj.RootPart.LocalId, part.LocalId, remoteClient, surfaceArg); |
||
370 | } |
||
371 | |||
372 | public void ProcessScriptReset(IClientAPI remoteClient, UUID objectID, |
||
373 | UUID itemID) |
||
374 | { |
||
375 | SceneObjectPart part=GetSceneObjectPart(objectID); |
||
376 | if (part == null) |
||
377 | return; |
||
378 | |||
379 | if (Permissions.CanResetScript(objectID, itemID, remoteClient.AgentId)) |
||
380 | { |
||
381 | EventManager.TriggerScriptReset(part.LocalId, itemID); |
||
382 | } |
||
383 | } |
||
384 | |||
385 | void ProcessViewerEffect(IClientAPI remoteClient, List<ViewerEffectEventHandlerArg> args) |
||
386 | { |
||
387 | // TODO: don't create new blocks if recycling an old packet |
||
388 | bool discardableEffects = true; |
||
389 | ViewerEffectPacket.EffectBlock[] effectBlockArray = new ViewerEffectPacket.EffectBlock[args.Count]; |
||
390 | for (int i = 0; i < args.Count; i++) |
||
391 | { |
||
392 | ViewerEffectPacket.EffectBlock effect = new ViewerEffectPacket.EffectBlock(); |
||
393 | effect.AgentID = args[i].AgentID; |
||
394 | effect.Color = args[i].Color; |
||
395 | effect.Duration = args[i].Duration; |
||
396 | effect.ID = args[i].ID; |
||
397 | effect.Type = args[i].Type; |
||
398 | effect.TypeData = args[i].TypeData; |
||
399 | effectBlockArray[i] = effect; |
||
400 | |||
401 | if ((EffectType)effect.Type != EffectType.LookAt && (EffectType)effect.Type != EffectType.Beam) |
||
402 | discardableEffects = false; |
||
403 | |||
404 | //m_log.DebugFormat("[YYY]: VE {0} {1} {2}", effect.AgentID, effect.Duration, (EffectType)effect.Type); |
||
405 | } |
||
406 | |||
407 | ForEachScenePresence(sp => |
||
408 | { |
||
409 | if (sp.ControllingClient.AgentId != remoteClient.AgentId) |
||
410 | { |
||
411 | if (!discardableEffects || |
||
412 | (discardableEffects && ShouldSendDiscardableEffect(remoteClient, sp))) |
||
413 | { |
||
414 | //m_log.DebugFormat("[YYY]: Sending to {0}", sp.UUID); |
||
415 | sp.ControllingClient.SendViewerEffect(effectBlockArray); |
||
416 | } |
||
417 | //else |
||
418 | // m_log.DebugFormat("[YYY]: Not sending to {0}", sp.UUID); |
||
419 | } |
||
420 | }); |
||
421 | } |
||
422 | |||
423 | private bool ShouldSendDiscardableEffect(IClientAPI thisClient, ScenePresence other) |
||
424 | { |
||
425 | return Vector3.Distance(other.CameraPosition, thisClient.SceneAgent.AbsolutePosition) < 10; |
||
426 | } |
||
427 | |||
428 | /// <summary> |
||
429 | /// Tell the client about the various child items and folders contained in the requested folder. |
||
430 | /// </summary> |
||
431 | /// <param name="remoteClient"></param> |
||
432 | /// <param name="folderID"></param> |
||
433 | /// <param name="ownerID"></param> |
||
434 | /// <param name="fetchFolders"></param> |
||
435 | /// <param name="fetchItems"></param> |
||
436 | /// <param name="sortOrder"></param> |
||
437 | public void HandleFetchInventoryDescendents(IClientAPI remoteClient, UUID folderID, UUID ownerID, |
||
438 | bool fetchFolders, bool fetchItems, int sortOrder) |
||
439 | { |
||
440 | // m_log.DebugFormat( |
||
441 | // "[USER INVENTORY]: HandleFetchInventoryDescendents() for {0}, folder={1}, fetchFolders={2}, fetchItems={3}, sortOrder={4}", |
||
442 | // remoteClient.Name, folderID, fetchFolders, fetchItems, sortOrder); |
||
443 | |||
444 | if (folderID == UUID.Zero) |
||
445 | return; |
||
446 | |||
447 | // FIXME MAYBE: We're not handling sortOrder! |
||
448 | |||
449 | // TODO: This code for looking in the folder for the library should be folded somewhere else |
||
450 | // so that this class doesn't have to know the details (and so that multiple libraries, etc. |
||
451 | // can be handled transparently). |
||
452 | InventoryFolderImpl fold = null; |
||
453 | if (LibraryService != null && LibraryService.LibraryRootFolder != null) |
||
454 | { |
||
455 | if ((fold = LibraryService.LibraryRootFolder.FindFolder(folderID)) != null) |
||
456 | { |
||
457 | remoteClient.SendInventoryFolderDetails( |
||
458 | fold.Owner, folderID, fold.RequestListOfItems(), |
||
459 | fold.RequestListOfFolders(), fold.Version, fetchFolders, fetchItems); |
||
460 | return; |
||
461 | } |
||
462 | } |
||
463 | |||
464 | // We're going to send the reply async, because there may be |
||
465 | // an enormous quantity of packets -- basically the entire inventory! |
||
466 | // We don't want to block the client thread while all that is happening. |
||
467 | SendInventoryDelegate d = SendInventoryAsync; |
||
468 | d.BeginInvoke(remoteClient, folderID, ownerID, fetchFolders, fetchItems, sortOrder, SendInventoryComplete, d); |
||
469 | } |
||
470 | |||
471 | delegate void SendInventoryDelegate(IClientAPI remoteClient, UUID folderID, UUID ownerID, bool fetchFolders, bool fetchItems, int sortOrder); |
||
472 | |||
473 | void SendInventoryAsync(IClientAPI remoteClient, UUID folderID, UUID ownerID, bool fetchFolders, bool fetchItems, int sortOrder) |
||
474 | { |
||
475 | SendInventoryUpdate(remoteClient, new InventoryFolderBase(folderID), fetchFolders, fetchItems); |
||
476 | } |
||
477 | |||
478 | void SendInventoryComplete(IAsyncResult iar) |
||
479 | { |
||
480 | SendInventoryDelegate d = (SendInventoryDelegate)iar.AsyncState; |
||
481 | d.EndInvoke(iar); |
||
482 | } |
||
483 | |||
484 | /// <summary> |
||
485 | /// Handle an inventory folder creation request from the client. |
||
486 | /// </summary> |
||
487 | /// <param name="remoteClient"></param> |
||
488 | /// <param name="folderID"></param> |
||
489 | /// <param name="folderType"></param> |
||
490 | /// <param name="folderName"></param> |
||
491 | /// <param name="parentID"></param> |
||
492 | public void HandleCreateInventoryFolder(IClientAPI remoteClient, UUID folderID, ushort folderType, |
||
493 | string folderName, UUID parentID) |
||
494 | { |
||
495 | InventoryFolderBase folder = new InventoryFolderBase(folderID, folderName, remoteClient.AgentId, (short)folderType, parentID, 1); |
||
496 | if (!InventoryService.AddFolder(folder)) |
||
497 | { |
||
498 | m_log.WarnFormat( |
||
499 | "[AGENT INVENTORY]: Failed to create folder for user {0} {1}", |
||
500 | remoteClient.Name, remoteClient.AgentId); |
||
501 | } |
||
502 | } |
||
503 | |||
504 | /// <summary> |
||
505 | /// Handle a client request to update the inventory folder |
||
506 | /// </summary> |
||
507 | /// |
||
508 | /// FIXME: We call add new inventory folder because in the data layer, we happen to use an SQL REPLACE |
||
509 | /// so this will work to rename an existing folder. Needless to say, to rely on this is very confusing, |
||
510 | /// and needs to be changed. |
||
511 | /// |
||
512 | /// <param name="remoteClient"></param> |
||
513 | /// <param name="folderID"></param> |
||
514 | /// <param name="type"></param> |
||
515 | /// <param name="name"></param> |
||
516 | /// <param name="parentID"></param> |
||
517 | public void HandleUpdateInventoryFolder(IClientAPI remoteClient, UUID folderID, ushort type, string name, |
||
518 | UUID parentID) |
||
519 | { |
||
520 | // m_log.DebugFormat( |
||
521 | // "[AGENT INVENTORY]: Updating inventory folder {0} {1} for {2} {3}", folderID, name, remoteClient.Name, remoteClient.AgentId); |
||
522 | |||
523 | InventoryFolderBase folder = new InventoryFolderBase(folderID, remoteClient.AgentId); |
||
524 | folder = InventoryService.GetFolder(folder); |
||
525 | if (folder != null) |
||
526 | { |
||
527 | folder.Name = name; |
||
528 | folder.Type = (short)type; |
||
529 | folder.ParentID = parentID; |
||
530 | if (!InventoryService.UpdateFolder(folder)) |
||
531 | { |
||
532 | m_log.ErrorFormat( |
||
533 | "[AGENT INVENTORY]: Failed to update folder for user {0} {1}", |
||
534 | remoteClient.Name, remoteClient.AgentId); |
||
535 | } |
||
536 | } |
||
537 | } |
||
538 | |||
539 | public void HandleMoveInventoryFolder(IClientAPI remoteClient, UUID folderID, UUID parentID) |
||
540 | { |
||
541 | InventoryFolderBase folder = new InventoryFolderBase(folderID, remoteClient.AgentId); |
||
542 | folder = InventoryService.GetFolder(folder); |
||
543 | if (folder != null) |
||
544 | { |
||
545 | folder.ParentID = parentID; |
||
546 | if (!InventoryService.MoveFolder(folder)) |
||
547 | m_log.WarnFormat("[AGENT INVENTORY]: could not move folder {0}", folderID); |
||
548 | else |
||
549 | m_log.DebugFormat("[AGENT INVENTORY]: folder {0} moved to parent {1}", folderID, parentID); |
||
550 | } |
||
551 | else |
||
552 | { |
||
553 | m_log.WarnFormat("[AGENT INVENTORY]: request to move folder {0} but folder not found", folderID); |
||
554 | } |
||
555 | } |
||
556 | |||
557 | delegate void PurgeFolderDelegate(UUID userID, UUID folder); |
||
558 | |||
559 | /// <summary> |
||
560 | /// This should delete all the items and folders in the given directory. |
||
561 | /// </summary> |
||
562 | /// <param name="remoteClient"></param> |
||
563 | /// <param name="folderID"></param> |
||
564 | public void HandlePurgeInventoryDescendents(IClientAPI remoteClient, UUID folderID) |
||
565 | { |
||
566 | PurgeFolderDelegate d = PurgeFolderAsync; |
||
567 | try |
||
568 | { |
||
569 | d.BeginInvoke(remoteClient.AgentId, folderID, PurgeFolderCompleted, d); |
||
570 | } |
||
571 | catch (Exception e) |
||
572 | { |
||
573 | m_log.WarnFormat("[AGENT INVENTORY]: Exception on purge folder for user {0}: {1}", remoteClient.AgentId, e.Message); |
||
574 | } |
||
575 | } |
||
576 | |||
577 | private void PurgeFolderAsync(UUID userID, UUID folderID) |
||
578 | { |
||
579 | InventoryFolderBase folder = new InventoryFolderBase(folderID, userID); |
||
580 | |||
581 | if (InventoryService.PurgeFolder(folder)) |
||
582 | m_log.DebugFormat("[AGENT INVENTORY]: folder {0} purged successfully", folderID); |
||
583 | else |
||
584 | m_log.WarnFormat("[AGENT INVENTORY]: could not purge folder {0}", folderID); |
||
585 | } |
||
586 | |||
587 | private void PurgeFolderCompleted(IAsyncResult iar) |
||
588 | { |
||
589 | PurgeFolderDelegate d = (PurgeFolderDelegate)iar.AsyncState; |
||
590 | d.EndInvoke(iar); |
||
591 | } |
||
592 | } |
||
593 | } |