corrade-vassal – Blame information for rev 1
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
1 | vero | 1 | /* |
2 | * Copyright (c) 2006-2014, openmetaverse.org |
||
3 | * All rights reserved. |
||
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 | * |
||
8 | * - Redistributions of source code must retain the above copyright notice, this |
||
9 | * list of conditions and the following disclaimer. |
||
10 | * - Neither the name of the openmetaverse.org nor the names |
||
11 | * of its contributors may be used to endorse or promote products derived from |
||
12 | * this software without specific prior written permission. |
||
13 | * |
||
14 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
||
15 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
||
16 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
||
17 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE |
||
18 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
||
19 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
||
20 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
||
21 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
||
22 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
||
23 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
||
24 | * POSSIBILITY OF SUCH DAMAGE. |
||
25 | */ |
||
26 | |||
27 | using System; |
||
28 | using System.Collections.Generic; |
||
29 | using System.Net; |
||
30 | using System.Text.RegularExpressions; |
||
31 | using System.Threading; |
||
32 | using System.Text; |
||
33 | using System.Runtime.Serialization; |
||
34 | using OpenMetaverse.Http; |
||
35 | using OpenMetaverse.Messages.Linden; |
||
36 | using OpenMetaverse.StructuredData; |
||
37 | using OpenMetaverse.Packets; |
||
38 | |||
39 | namespace OpenMetaverse |
||
40 | { |
||
41 | #region Enums |
||
42 | |||
43 | [Flags] |
||
44 | public enum InventorySortOrder : int |
||
45 | { |
||
46 | /// <summary>Sort by name</summary> |
||
47 | ByName = 0, |
||
48 | /// <summary>Sort by date</summary> |
||
49 | ByDate = 1, |
||
50 | /// <summary>Sort folders by name, regardless of whether items are |
||
51 | /// sorted by name or date</summary> |
||
52 | FoldersByName = 2, |
||
53 | /// <summary>Place system folders at the top</summary> |
||
54 | SystemFoldersToTop = 4 |
||
55 | } |
||
56 | |||
57 | /// <summary> |
||
58 | /// Possible destinations for DeRezObject request |
||
59 | /// </summary> |
||
60 | public enum DeRezDestination : byte |
||
61 | { |
||
62 | /// <summary></summary> |
||
63 | AgentInventorySave = 0, |
||
64 | /// <summary>Copy from in-world to agent inventory</summary> |
||
65 | AgentInventoryCopy = 1, |
||
66 | /// <summary>Derez to TaskInventory</summary> |
||
67 | TaskInventory = 2, |
||
68 | /// <summary></summary> |
||
69 | Attachment = 3, |
||
70 | /// <summary>Take Object</summary> |
||
71 | AgentInventoryTake = 4, |
||
72 | /// <summary></summary> |
||
73 | ForceToGodInventory = 5, |
||
74 | /// <summary>Delete Object</summary> |
||
75 | TrashFolder = 6, |
||
76 | /// <summary>Put an avatar attachment into agent inventory</summary> |
||
77 | AttachmentToInventory = 7, |
||
78 | /// <summary></summary> |
||
79 | AttachmentExists = 8, |
||
80 | /// <summary>Return an object back to the owner's inventory</summary> |
||
81 | ReturnToOwner = 9, |
||
82 | /// <summary>Return a deeded object back to the last owner's inventory</summary> |
||
83 | ReturnToLastOwner = 10 |
||
84 | } |
||
85 | |||
86 | /// <summary> |
||
87 | /// Upper half of the Flags field for inventory items |
||
88 | /// </summary> |
||
89 | [Flags] |
||
90 | public enum InventoryItemFlags : uint |
||
91 | { |
||
92 | None = 0, |
||
93 | /// <summary>Indicates that the NextOwner permission will be set to the |
||
94 | /// most restrictive set of permissions found in the object set |
||
95 | /// (including linkset items and object inventory items) on next rez</summary> |
||
96 | ObjectSlamPerm = 0x100, |
||
97 | /// <summary>Indicates that the object sale information has been |
||
98 | /// changed</summary> |
||
99 | ObjectSlamSale = 0x1000, |
||
100 | /// <summary>If set, and a slam bit is set, indicates BaseMask will be overwritten on Rez</summary> |
||
101 | ObjectOverwriteBase = 0x010000, |
||
102 | /// <summary>If set, and a slam bit is set, indicates OwnerMask will be overwritten on Rez</summary> |
||
103 | ObjectOverwriteOwner = 0x020000, |
||
104 | /// <summary>If set, and a slam bit is set, indicates GroupMask will be overwritten on Rez</summary> |
||
105 | ObjectOverwriteGroup = 0x040000, |
||
106 | /// <summary>If set, and a slam bit is set, indicates EveryoneMask will be overwritten on Rez</summary> |
||
107 | ObjectOverwriteEveryone = 0x080000, |
||
108 | /// <summary>If set, and a slam bit is set, indicates NextOwnerMask will be overwritten on Rez</summary> |
||
109 | ObjectOverwriteNextOwner = 0x100000, |
||
110 | /// <summary>Indicates whether this object is composed of multiple |
||
111 | /// items or not</summary> |
||
112 | ObjectHasMultipleItems = 0x200000, |
||
113 | /// <summary>Indicates that the asset is only referenced by this |
||
114 | /// inventory item. If this item is deleted or updated to reference a |
||
115 | /// new assetID, the asset can be deleted</summary> |
||
116 | SharedSingleReference = 0x40000000, |
||
117 | } |
||
118 | |||
119 | #endregion Enums |
||
120 | |||
121 | #region Inventory Object Classes |
||
122 | |||
123 | /// <summary> |
||
124 | /// Base Class for Inventory Items |
||
125 | /// </summary> |
||
126 | [Serializable()] |
||
127 | public abstract class InventoryBase : ISerializable |
||
128 | { |
||
129 | /// <summary><seealso cref="OpenMetaverse.UUID"/> of item/folder</summary> |
||
130 | public UUID UUID; |
||
131 | /// <summary><seealso cref="OpenMetaverse.UUID"/> of parent folder</summary> |
||
132 | public UUID ParentUUID; |
||
133 | /// <summary>Name of item/folder</summary> |
||
134 | public string Name; |
||
135 | /// <summary>Item/Folder Owners <seealso cref="OpenMetaverse.UUID"/></summary> |
||
136 | public UUID OwnerID; |
||
137 | |||
138 | /// <summary> |
||
139 | /// Constructor, takes an itemID as a parameter |
||
140 | /// </summary> |
||
141 | /// <param name="itemID">The <seealso cref="OpenMetaverse.UUID"/> of the item</param> |
||
142 | public InventoryBase(UUID itemID) |
||
143 | { |
||
144 | if (itemID == UUID.Zero) |
||
145 | Logger.Log("Initializing an InventoryBase with UUID.Zero", Helpers.LogLevel.Warning); |
||
146 | UUID = itemID; |
||
147 | } |
||
148 | |||
149 | /// <summary> |
||
150 | /// |
||
151 | /// </summary> |
||
152 | /// <returns></returns> |
||
153 | public virtual void GetObjectData(SerializationInfo info, StreamingContext ctxt) |
||
154 | { |
||
155 | info.AddValue("UUID", UUID); |
||
156 | info.AddValue("ParentUUID", ParentUUID); |
||
157 | info.AddValue("Name", Name); |
||
158 | info.AddValue("OwnerID", OwnerID); |
||
159 | } |
||
160 | |||
161 | /// <summary> |
||
162 | /// |
||
163 | /// </summary> |
||
164 | /// <returns></returns> |
||
165 | public InventoryBase(SerializationInfo info, StreamingContext ctxt) |
||
166 | { |
||
167 | UUID = (UUID)info.GetValue("UUID", typeof(UUID)); |
||
168 | ParentUUID = (UUID)info.GetValue("ParentUUID", typeof(UUID)); |
||
169 | Name = (string)info.GetValue("Name", typeof(string)); |
||
170 | OwnerID = (UUID)info.GetValue("OwnerID", typeof(UUID)); |
||
171 | } |
||
172 | |||
173 | /// <summary> |
||
174 | /// Generates a number corresponding to the value of the object to support the use of a hash table, |
||
175 | /// suitable for use in hashing algorithms and data structures such as a hash table |
||
176 | /// </summary> |
||
177 | /// <returns>A Hashcode of all the combined InventoryBase fields</returns> |
||
178 | public override int GetHashCode() |
||
179 | { |
||
180 | return UUID.GetHashCode() ^ ParentUUID.GetHashCode() ^ Name.GetHashCode() ^ OwnerID.GetHashCode(); |
||
181 | } |
||
182 | |||
183 | /// <summary> |
||
184 | /// Determine whether the specified <seealso cref="OpenMetaverse.InventoryBase"/> object is equal to the current object |
||
185 | /// </summary> |
||
186 | /// <param name="o">InventoryBase object to compare against</param> |
||
187 | /// <returns>true if objects are the same</returns> |
||
188 | public override bool Equals(object o) |
||
189 | { |
||
190 | InventoryBase inv = o as InventoryBase; |
||
191 | return inv != null && Equals(inv); |
||
192 | } |
||
193 | |||
194 | /// <summary> |
||
195 | /// Determine whether the specified <seealso cref="OpenMetaverse.InventoryBase"/> object is equal to the current object |
||
196 | /// </summary> |
||
197 | /// <param name="o">InventoryBase object to compare against</param> |
||
198 | /// <returns>true if objects are the same</returns> |
||
199 | public virtual bool Equals(InventoryBase o) |
||
200 | { |
||
201 | return o.UUID == UUID |
||
202 | && o.ParentUUID == ParentUUID |
||
203 | && o.Name == Name |
||
204 | && o.OwnerID == OwnerID; |
||
205 | } |
||
206 | |||
207 | /// <summary> |
||
208 | /// Convert inventory to OSD |
||
209 | /// </summary> |
||
210 | /// <returns>OSD representation</returns> |
||
211 | public abstract OSD GetOSD(); |
||
212 | } |
||
213 | |||
214 | /// <summary> |
||
215 | /// An Item in Inventory |
||
216 | /// </summary> |
||
217 | [Serializable()] |
||
218 | public class InventoryItem : InventoryBase |
||
219 | { |
||
220 | public override string ToString() |
||
221 | { |
||
222 | return AssetType + " " + AssetUUID + " (" + InventoryType + " " + UUID + ") '" + Name + "'/'" + |
||
223 | Description + "' " + Permissions; |
||
224 | } |
||
225 | /// <summary>The <seealso cref="OpenMetaverse.UUID"/> of this item</summary> |
||
226 | public UUID AssetUUID; |
||
227 | /// <summary>The combined <seealso cref="OpenMetaverse.Permissions"/> of this item</summary> |
||
228 | public Permissions Permissions; |
||
229 | /// <summary>The type of item from <seealso cref="OpenMetaverse.AssetType"/></summary> |
||
230 | public AssetType AssetType; |
||
231 | /// <summary>The type of item from the <seealso cref="OpenMetaverse.InventoryType"/> enum</summary> |
||
232 | public InventoryType InventoryType; |
||
233 | /// <summary>The <seealso cref="OpenMetaverse.UUID"/> of the creator of this item</summary> |
||
234 | public UUID CreatorID; |
||
235 | /// <summary>A Description of this item</summary> |
||
236 | public string Description; |
||
237 | /// <summary>The <seealso cref="OpenMetaverse.Group"/>s <seealso cref="OpenMetaverse.UUID"/> this item is set to or owned by</summary> |
||
238 | public UUID GroupID; |
||
239 | /// <summary>If true, item is owned by a group</summary> |
||
240 | public bool GroupOwned; |
||
241 | /// <summary>The price this item can be purchased for</summary> |
||
242 | public int SalePrice; |
||
243 | /// <summary>The type of sale from the <seealso cref="OpenMetaverse.SaleType"/> enum</summary> |
||
244 | public SaleType SaleType; |
||
245 | /// <summary>Combined flags from <seealso cref="OpenMetaverse.InventoryItemFlags"/></summary> |
||
246 | public uint Flags; |
||
247 | /// <summary>Time and date this inventory item was created, stored as |
||
248 | /// UTC (Coordinated Universal Time)</summary> |
||
249 | public DateTime CreationDate; |
||
250 | /// <summary>Used to update the AssetID in requests sent to the server</summary> |
||
251 | public UUID TransactionID; |
||
252 | /// <summary>The <seealso cref="OpenMetaverse.UUID"/> of the previous owner of the item</summary> |
||
253 | public UUID LastOwnerID; |
||
254 | |||
255 | /// <summary> |
||
256 | /// Construct a new InventoryItem object |
||
257 | /// </summary> |
||
258 | /// <param name="itemID">The <seealso cref="OpenMetaverse.UUID"/> of the item</param> |
||
259 | public InventoryItem(UUID itemID) |
||
260 | : base(itemID) { } |
||
261 | |||
262 | /// <summary> |
||
263 | /// Construct a new InventoryItem object of a specific Type |
||
264 | /// </summary> |
||
265 | /// <param name="type">The type of item from <seealso cref="OpenMetaverse.InventoryType"/></param> |
||
266 | /// <param name="itemID"><seealso cref="OpenMetaverse.UUID"/> of the item</param> |
||
267 | public InventoryItem(InventoryType type, UUID itemID) : base(itemID) { InventoryType = type; } |
||
268 | |||
269 | /// <summary> |
||
270 | /// Indicates inventory item is a link |
||
271 | /// </summary> |
||
272 | /// <returns>True if inventory item is a link to another inventory item</returns> |
||
273 | public bool IsLink() |
||
274 | { |
||
275 | return AssetType == AssetType.Link || AssetType == AssetType.LinkFolder; |
||
276 | } |
||
277 | |||
278 | /// <summary> |
||
279 | /// |
||
280 | /// </summary> |
||
281 | /// <returns></returns> |
||
282 | override public void GetObjectData(SerializationInfo info, StreamingContext ctxt) |
||
283 | { |
||
284 | base.GetObjectData(info, ctxt); |
||
285 | info.AddValue("AssetUUID", AssetUUID, typeof(UUID)); |
||
286 | info.AddValue("Permissions", Permissions, typeof(Permissions)); |
||
287 | info.AddValue("AssetType", AssetType); |
||
288 | info.AddValue("InventoryType", InventoryType); |
||
289 | info.AddValue("CreatorID", CreatorID); |
||
290 | info.AddValue("Description", Description); |
||
291 | info.AddValue("GroupID", GroupID); |
||
292 | info.AddValue("GroupOwned", GroupOwned); |
||
293 | info.AddValue("SalePrice", SalePrice); |
||
294 | info.AddValue("SaleType", SaleType); |
||
295 | info.AddValue("Flags", Flags); |
||
296 | info.AddValue("CreationDate", CreationDate); |
||
297 | info.AddValue("LastOwnerID", LastOwnerID); |
||
298 | } |
||
299 | |||
300 | /// <summary> |
||
301 | /// |
||
302 | /// </summary> |
||
303 | /// <returns></returns> |
||
304 | public InventoryItem(SerializationInfo info, StreamingContext ctxt) |
||
305 | : base(info, ctxt) |
||
306 | { |
||
307 | AssetUUID = (UUID)info.GetValue("AssetUUID", typeof(UUID)); |
||
308 | Permissions = (Permissions)info.GetValue("Permissions", typeof(Permissions)); |
||
309 | AssetType = (AssetType)info.GetValue("AssetType", typeof(AssetType)); |
||
310 | InventoryType = (InventoryType)info.GetValue("InventoryType", typeof(InventoryType)); |
||
311 | CreatorID = (UUID)info.GetValue("CreatorID", typeof(UUID)); |
||
312 | Description = (string)info.GetValue("Description", typeof(string)); |
||
313 | GroupID = (UUID)info.GetValue("GroupID", typeof(UUID)); |
||
314 | GroupOwned = (bool)info.GetValue("GroupOwned", typeof(bool)); |
||
315 | SalePrice = (int)info.GetValue("SalePrice", typeof(int)); |
||
316 | SaleType = (SaleType)info.GetValue("SaleType", typeof(SaleType)); |
||
317 | Flags = (uint)info.GetValue("Flags", typeof(uint)); |
||
318 | CreationDate = (DateTime)info.GetValue("CreationDate", typeof(DateTime)); |
||
319 | LastOwnerID = (UUID)info.GetValue("LastOwnerID", typeof(UUID)); |
||
320 | } |
||
321 | |||
322 | /// <summary> |
||
323 | /// Generates a number corresponding to the value of the object to support the use of a hash table. |
||
324 | /// Suitable for use in hashing algorithms and data structures such as a hash table |
||
325 | /// </summary> |
||
326 | /// <returns>A Hashcode of all the combined InventoryItem fields</returns> |
||
327 | public override int GetHashCode() |
||
328 | { |
||
329 | return AssetUUID.GetHashCode() ^ Permissions.GetHashCode() ^ AssetType.GetHashCode() ^ |
||
330 | InventoryType.GetHashCode() ^ Description.GetHashCode() ^ GroupID.GetHashCode() ^ |
||
331 | GroupOwned.GetHashCode() ^ SalePrice.GetHashCode() ^ SaleType.GetHashCode() ^ |
||
332 | Flags.GetHashCode() ^ CreationDate.GetHashCode() ^ LastOwnerID.GetHashCode(); |
||
333 | } |
||
334 | |||
335 | /// <summary> |
||
336 | /// Compares an object |
||
337 | /// </summary> |
||
338 | /// <param name="o">The object to compare</param> |
||
339 | /// <returns>true if comparison object matches</returns> |
||
340 | public override bool Equals(object o) |
||
341 | { |
||
342 | InventoryItem item = o as InventoryItem; |
||
343 | return item != null && Equals(item); |
||
344 | } |
||
345 | |||
346 | /// <summary> |
||
347 | /// Determine whether the specified <seealso cref="OpenMetaverse.InventoryBase"/> object is equal to the current object |
||
348 | /// </summary> |
||
349 | /// <param name="o">The <seealso cref="OpenMetaverse.InventoryBase"/> object to compare against</param> |
||
350 | /// <returns>true if objects are the same</returns> |
||
351 | public override bool Equals(InventoryBase o) |
||
352 | { |
||
353 | InventoryItem item = o as InventoryItem; |
||
354 | return item != null && Equals(item); |
||
355 | } |
||
356 | |||
357 | /// <summary> |
||
358 | /// Determine whether the specified <seealso cref="OpenMetaverse.InventoryItem"/> object is equal to the current object |
||
359 | /// </summary> |
||
360 | /// <param name="o">The <seealso cref="OpenMetaverse.InventoryItem"/> object to compare against</param> |
||
361 | /// <returns>true if objects are the same</returns> |
||
362 | public bool Equals(InventoryItem o) |
||
363 | { |
||
364 | return base.Equals(o as InventoryBase) |
||
365 | && o.AssetType == AssetType |
||
366 | && o.AssetUUID == AssetUUID |
||
367 | && o.CreationDate == CreationDate |
||
368 | && o.Description == Description |
||
369 | && o.Flags == Flags |
||
370 | && o.GroupID == GroupID |
||
371 | && o.GroupOwned == GroupOwned |
||
372 | && o.InventoryType == InventoryType |
||
373 | && o.Permissions.Equals(Permissions) |
||
374 | && o.SalePrice == SalePrice |
||
375 | && o.SaleType == SaleType |
||
376 | && o.LastOwnerID == LastOwnerID; |
||
377 | } |
||
378 | |||
379 | /// <summary> |
||
380 | /// Create InventoryItem from OSD |
||
381 | /// </summary> |
||
382 | /// <param name="data">OSD Data that makes up InventoryItem</param> |
||
383 | /// <returns>Inventory item created</returns> |
||
384 | public static InventoryItem FromOSD(OSD data) |
||
385 | { |
||
386 | OSDMap descItem = (OSDMap)data; |
||
387 | |||
388 | InventoryType type = (InventoryType)descItem["inv_type"].AsInteger(); |
||
389 | if (type == InventoryType.Texture && (AssetType)descItem["type"].AsInteger() == AssetType.Object) |
||
390 | { |
||
391 | type = InventoryType.Attachment; |
||
392 | } |
||
393 | InventoryItem item = InventoryManager.CreateInventoryItem(type, descItem["item_id"]); |
||
394 | |||
395 | item.ParentUUID = descItem["parent_id"]; |
||
396 | item.Name = descItem["name"]; |
||
397 | item.Description = descItem["desc"]; |
||
398 | item.OwnerID = descItem["agent_id"]; |
||
399 | item.ParentUUID = descItem["parent_id"]; |
||
400 | item.AssetUUID = descItem["asset_id"]; |
||
401 | item.AssetType = (AssetType)descItem["type"].AsInteger(); |
||
402 | item.CreationDate = Utils.UnixTimeToDateTime(descItem["created_at"]); |
||
403 | item.Flags = descItem["flags"]; |
||
404 | |||
405 | OSDMap perms = (OSDMap)descItem["permissions"]; |
||
406 | item.CreatorID = perms["creator_id"]; |
||
407 | item.LastOwnerID = perms["last_owner_id"]; |
||
408 | item.Permissions = new Permissions(perms["base_mask"], perms["everyone_mask"], perms["group_mask"], perms["next_owner_mask"], perms["owner_mask"]); |
||
409 | item.GroupOwned = perms["is_owner_group"]; |
||
410 | item.GroupID = perms["group_id"]; |
||
411 | |||
412 | OSDMap sale = (OSDMap)descItem["sale_info"]; |
||
413 | item.SalePrice = sale["sale_price"]; |
||
414 | item.SaleType = (SaleType)sale["sale_type"].AsInteger(); |
||
415 | |||
416 | return item; |
||
417 | } |
||
418 | |||
419 | /// <summary> |
||
420 | /// Convert InventoryItem to OSD |
||
421 | /// </summary> |
||
422 | /// <returns>OSD representation of InventoryItem</returns> |
||
423 | public override OSD GetOSD() |
||
424 | { |
||
425 | OSDMap map = new OSDMap(); |
||
426 | map["inv_type"] = (int)InventoryType; |
||
427 | map["parent_id"] = ParentUUID; |
||
428 | map["name"] = Name; |
||
429 | map["desc"] = Description; |
||
430 | map["agent_id"] = OwnerID; |
||
431 | map["parent_id"] = ParentUUID; |
||
432 | map["asset_id"] = AssetUUID; |
||
433 | map["type"] = (int)AssetType; |
||
434 | map["created_at"] = CreationDate; |
||
435 | map["flags"] = Flags; |
||
436 | |||
437 | OSDMap perms = (OSDMap)Permissions.GetOSD(); |
||
438 | perms["creator_id"] = CreatorID; |
||
439 | perms["last_owner_id"] = LastOwnerID; |
||
440 | perms["is_owner_group"] = GroupOwned; |
||
441 | perms["group_id"] = GroupID; |
||
442 | map["permissions"] = perms; |
||
443 | |||
444 | OSDMap sale = new OSDMap(); |
||
445 | sale["sale_price"] = SalePrice; |
||
446 | sale["sale_type"] = (int)SaleType; |
||
447 | map["sale_info"] = sale; |
||
448 | |||
449 | return map; |
||
450 | } |
||
451 | } |
||
452 | |||
453 | /// <summary> |
||
454 | /// InventoryTexture Class representing a graphical image |
||
455 | /// </summary> |
||
456 | /// <seealso cref="ManagedImage"/> |
||
457 | [Serializable()] |
||
458 | public class InventoryTexture : InventoryItem |
||
459 | { |
||
460 | /// <summary> |
||
461 | /// Construct an InventoryTexture object |
||
462 | /// </summary> |
||
463 | /// <param name="itemID">A <seealso cref="OpenMetaverse.UUID"/> which becomes the |
||
464 | /// <seealso cref="OpenMetaverse.InventoryItem"/> objects AssetUUID</param> |
||
465 | public InventoryTexture(UUID itemID) |
||
466 | : base(itemID) |
||
467 | { |
||
468 | InventoryType = InventoryType.Texture; |
||
469 | } |
||
470 | |||
471 | /// <summary> |
||
472 | /// Construct an InventoryTexture object from a serialization stream |
||
473 | /// </summary> |
||
474 | public InventoryTexture(SerializationInfo info, StreamingContext ctxt) |
||
475 | : base(info, ctxt) |
||
476 | { |
||
477 | InventoryType = InventoryType.Texture; |
||
478 | } |
||
479 | } |
||
480 | |||
481 | /// <summary> |
||
482 | /// InventorySound Class representing a playable sound |
||
483 | /// </summary> |
||
484 | [Serializable()] |
||
485 | public class InventorySound : InventoryItem |
||
486 | { |
||
487 | /// <summary> |
||
488 | /// Construct an InventorySound object |
||
489 | /// </summary> |
||
490 | /// <param name="itemID">A <seealso cref="OpenMetaverse.UUID"/> which becomes the |
||
491 | /// <seealso cref="OpenMetaverse.InventoryItem"/> objects AssetUUID</param> |
||
492 | public InventorySound(UUID itemID) |
||
493 | : base(itemID) |
||
494 | { |
||
495 | InventoryType = InventoryType.Sound; |
||
496 | } |
||
497 | |||
498 | /// <summary> |
||
499 | /// Construct an InventorySound object from a serialization stream |
||
500 | /// </summary> |
||
501 | public InventorySound(SerializationInfo info, StreamingContext ctxt) |
||
502 | : base(info, ctxt) |
||
503 | { |
||
504 | InventoryType = InventoryType.Sound; |
||
505 | } |
||
506 | } |
||
507 | |||
508 | /// <summary> |
||
509 | /// InventoryCallingCard Class, contains information on another avatar |
||
510 | /// </summary> |
||
511 | [Serializable()] |
||
512 | public class InventoryCallingCard : InventoryItem |
||
513 | { |
||
514 | /// <summary> |
||
515 | /// Construct an InventoryCallingCard object |
||
516 | /// </summary> |
||
517 | /// <param name="itemID">A <seealso cref="OpenMetaverse.UUID"/> which becomes the |
||
518 | /// <seealso cref="OpenMetaverse.InventoryItem"/> objects AssetUUID</param> |
||
519 | public InventoryCallingCard(UUID itemID) |
||
520 | : base(itemID) |
||
521 | { |
||
522 | InventoryType = InventoryType.CallingCard; |
||
523 | } |
||
524 | |||
525 | /// <summary> |
||
526 | /// Construct an InventoryCallingCard object from a serialization stream |
||
527 | /// </summary> |
||
528 | public InventoryCallingCard(SerializationInfo info, StreamingContext ctxt) |
||
529 | : base(info, ctxt) |
||
530 | { |
||
531 | InventoryType = InventoryType.CallingCard; |
||
532 | } |
||
533 | } |
||
534 | |||
535 | /// <summary> |
||
536 | /// InventoryLandmark Class, contains details on a specific location |
||
537 | /// </summary> |
||
538 | [Serializable()] |
||
539 | public class InventoryLandmark : InventoryItem |
||
540 | { |
||
541 | /// <summary> |
||
542 | /// Construct an InventoryLandmark object |
||
543 | /// </summary> |
||
544 | /// <param name="itemID">A <seealso cref="OpenMetaverse.UUID"/> which becomes the |
||
545 | /// <seealso cref="OpenMetaverse.InventoryItem"/> objects AssetUUID</param> |
||
546 | public InventoryLandmark(UUID itemID) |
||
547 | : base(itemID) |
||
548 | { |
||
549 | InventoryType = InventoryType.Landmark; |
||
550 | } |
||
551 | |||
552 | /// <summary> |
||
553 | /// Construct an InventoryLandmark object from a serialization stream |
||
554 | /// </summary> |
||
555 | public InventoryLandmark(SerializationInfo info, StreamingContext ctxt) |
||
556 | : base(info, ctxt) |
||
557 | { |
||
558 | InventoryType = InventoryType.Landmark; |
||
559 | |||
560 | } |
||
561 | |||
562 | /// <summary> |
||
563 | /// Landmarks use the InventoryItemFlags struct and will have a flag of 1 set if they have been visited |
||
564 | /// </summary> |
||
565 | public bool LandmarkVisited |
||
566 | { |
||
567 | get { return (Flags & 1) != 0; } |
||
568 | set |
||
569 | { |
||
570 | if (value) Flags |= 1; |
||
571 | else Flags &= ~1u; |
||
572 | } |
||
573 | } |
||
574 | } |
||
575 | |||
576 | /// <summary> |
||
577 | /// InventoryObject Class contains details on a primitive or coalesced set of primitives |
||
578 | /// </summary> |
||
579 | [Serializable()] |
||
580 | public class InventoryObject : InventoryItem |
||
581 | { |
||
582 | /// <summary> |
||
583 | /// Construct an InventoryObject object |
||
584 | /// </summary> |
||
585 | /// <param name="itemID">A <seealso cref="OpenMetaverse.UUID"/> which becomes the |
||
586 | /// <seealso cref="OpenMetaverse.InventoryItem"/> objects AssetUUID</param> |
||
587 | public InventoryObject(UUID itemID) |
||
588 | : base(itemID) |
||
589 | { |
||
590 | InventoryType = InventoryType.Object; |
||
591 | } |
||
592 | |||
593 | /// <summary> |
||
594 | /// Construct an InventoryObject object from a serialization stream |
||
595 | /// </summary> |
||
596 | public InventoryObject(SerializationInfo info, StreamingContext ctxt) |
||
597 | : base(info, ctxt) |
||
598 | { |
||
599 | InventoryType = InventoryType.Object; |
||
600 | } |
||
601 | |||
602 | /// <summary> |
||
603 | /// Gets or sets the upper byte of the Flags value |
||
604 | /// </summary> |
||
605 | public InventoryItemFlags ItemFlags |
||
606 | { |
||
607 | get { return (InventoryItemFlags)(Flags & ~0xFF); } |
||
608 | set { Flags = (uint)value | (Flags & 0xFF); } |
||
609 | } |
||
610 | |||
611 | /// <summary> |
||
612 | /// Gets or sets the object attachment point, the lower byte of the Flags value |
||
613 | /// </summary> |
||
614 | public AttachmentPoint AttachPoint |
||
615 | { |
||
616 | get { return (AttachmentPoint)(Flags & 0xFF); } |
||
617 | set { Flags = (uint)value | (Flags & 0xFFFFFF00); } |
||
618 | } |
||
619 | } |
||
620 | |||
621 | /// <summary> |
||
622 | /// InventoryNotecard Class, contains details on an encoded text document |
||
623 | /// </summary> |
||
624 | [Serializable()] |
||
625 | public class InventoryNotecard : InventoryItem |
||
626 | { |
||
627 | /// <summary> |
||
628 | /// Construct an InventoryNotecard object |
||
629 | /// </summary> |
||
630 | /// <param name="itemID">A <seealso cref="OpenMetaverse.UUID"/> which becomes the |
||
631 | /// <seealso cref="OpenMetaverse.InventoryItem"/> objects AssetUUID</param> |
||
632 | public InventoryNotecard(UUID itemID) |
||
633 | : base(itemID) |
||
634 | { |
||
635 | InventoryType = InventoryType.Notecard; |
||
636 | } |
||
637 | |||
638 | /// <summary> |
||
639 | /// Construct an InventoryNotecard object from a serialization stream |
||
640 | /// </summary> |
||
641 | public InventoryNotecard(SerializationInfo info, StreamingContext ctxt) |
||
642 | : base(info, ctxt) |
||
643 | { |
||
644 | InventoryType = InventoryType.Notecard; |
||
645 | } |
||
646 | } |
||
647 | |||
648 | /// <summary> |
||
649 | /// InventoryCategory Class |
||
650 | /// </summary> |
||
651 | /// <remarks>TODO: Is this even used for anything?</remarks> |
||
652 | [Serializable()] |
||
653 | public class InventoryCategory : InventoryItem |
||
654 | { |
||
655 | /// <summary> |
||
656 | /// Construct an InventoryCategory object |
||
657 | /// </summary> |
||
658 | /// <param name="itemID">A <seealso cref="OpenMetaverse.UUID"/> which becomes the |
||
659 | /// <seealso cref="OpenMetaverse.InventoryItem"/> objects AssetUUID</param> |
||
660 | public InventoryCategory(UUID itemID) |
||
661 | : base(itemID) |
||
662 | { |
||
663 | InventoryType = InventoryType.Category; |
||
664 | } |
||
665 | |||
666 | /// <summary> |
||
667 | /// Construct an InventoryCategory object from a serialization stream |
||
668 | /// </summary> |
||
669 | public InventoryCategory(SerializationInfo info, StreamingContext ctxt) |
||
670 | : base(info, ctxt) |
||
671 | { |
||
672 | InventoryType = InventoryType.Category; |
||
673 | } |
||
674 | } |
||
675 | |||
676 | /// <summary> |
||
677 | /// InventoryLSL Class, represents a Linden Scripting Language object |
||
678 | /// </summary> |
||
679 | [Serializable()] |
||
680 | public class InventoryLSL : InventoryItem |
||
681 | { |
||
682 | /// <summary> |
||
683 | /// Construct an InventoryLSL object |
||
684 | /// </summary> |
||
685 | /// <param name="itemID">A <seealso cref="OpenMetaverse.UUID"/> which becomes the |
||
686 | /// <seealso cref="OpenMetaverse.InventoryItem"/> objects AssetUUID</param> |
||
687 | public InventoryLSL(UUID itemID) |
||
688 | : base(itemID) |
||
689 | { |
||
690 | InventoryType = InventoryType.LSL; |
||
691 | } |
||
692 | |||
693 | /// <summary> |
||
694 | /// Construct an InventoryLSL object from a serialization stream |
||
695 | /// </summary> |
||
696 | public InventoryLSL(SerializationInfo info, StreamingContext ctxt) |
||
697 | : base(info, ctxt) |
||
698 | { |
||
699 | InventoryType = InventoryType.LSL; |
||
700 | } |
||
701 | } |
||
702 | |||
703 | /// <summary> |
||
704 | /// InventorySnapshot Class, an image taken with the viewer |
||
705 | /// </summary> |
||
706 | [Serializable()] |
||
707 | public class InventorySnapshot : InventoryItem |
||
708 | { |
||
709 | /// <summary> |
||
710 | /// Construct an InventorySnapshot object |
||
711 | /// </summary> |
||
712 | /// <param name="itemID">A <seealso cref="OpenMetaverse.UUID"/> which becomes the |
||
713 | /// <seealso cref="OpenMetaverse.InventoryItem"/> objects AssetUUID</param> |
||
714 | public InventorySnapshot(UUID itemID) |
||
715 | : base(itemID) |
||
716 | { |
||
717 | InventoryType = InventoryType.Snapshot; |
||
718 | } |
||
719 | |||
720 | /// <summary> |
||
721 | /// Construct an InventorySnapshot object from a serialization stream |
||
722 | /// </summary> |
||
723 | public InventorySnapshot(SerializationInfo info, StreamingContext ctxt) |
||
724 | : base(info, ctxt) |
||
725 | { |
||
726 | InventoryType = InventoryType.Snapshot; |
||
727 | } |
||
728 | } |
||
729 | |||
730 | /// <summary> |
||
731 | /// InventoryAttachment Class, contains details on an attachable object |
||
732 | /// </summary> |
||
733 | [Serializable()] |
||
734 | public class InventoryAttachment : InventoryItem |
||
735 | { |
||
736 | /// <summary> |
||
737 | /// Construct an InventoryAttachment object |
||
738 | /// </summary> |
||
739 | /// <param name="itemID">A <seealso cref="OpenMetaverse.UUID"/> which becomes the |
||
740 | /// <seealso cref="OpenMetaverse.InventoryItem"/> objects AssetUUID</param> |
||
741 | public InventoryAttachment(UUID itemID) |
||
742 | : base(itemID) |
||
743 | { |
||
744 | InventoryType = InventoryType.Attachment; |
||
745 | } |
||
746 | |||
747 | /// <summary> |
||
748 | /// Construct an InventoryAttachment object from a serialization stream |
||
749 | /// </summary> |
||
750 | public InventoryAttachment(SerializationInfo info, StreamingContext ctxt) |
||
751 | : base(info, ctxt) |
||
752 | { |
||
753 | InventoryType = InventoryType.Attachment; |
||
754 | } |
||
755 | |||
756 | /// <summary> |
||
757 | /// Get the last AttachmentPoint this object was attached to |
||
758 | /// </summary> |
||
759 | public AttachmentPoint AttachmentPoint |
||
760 | { |
||
761 | get { return (AttachmentPoint)Flags; } |
||
762 | set { Flags = (uint)value; } |
||
763 | } |
||
764 | } |
||
765 | |||
766 | /// <summary> |
||
767 | /// InventoryWearable Class, details on a clothing item or body part |
||
768 | /// </summary> |
||
769 | [Serializable()] |
||
770 | public class InventoryWearable : InventoryItem |
||
771 | { |
||
772 | /// <summary> |
||
773 | /// Construct an InventoryWearable object |
||
774 | /// </summary> |
||
775 | /// <param name="itemID">A <seealso cref="OpenMetaverse.UUID"/> which becomes the |
||
776 | /// <seealso cref="OpenMetaverse.InventoryItem"/> objects AssetUUID</param> |
||
777 | public InventoryWearable(UUID itemID) : base(itemID) { InventoryType = InventoryType.Wearable; } |
||
778 | |||
779 | /// <summary> |
||
780 | /// Construct an InventoryWearable object from a serialization stream |
||
781 | /// </summary> |
||
782 | public InventoryWearable(SerializationInfo info, StreamingContext ctxt) |
||
783 | : base(info, ctxt) |
||
784 | { |
||
785 | InventoryType = InventoryType.Wearable; |
||
786 | } |
||
787 | |||
788 | /// <summary> |
||
789 | /// The <seealso cref="OpenMetaverse.WearableType"/>, Skin, Shape, Skirt, Etc |
||
790 | /// </summary> |
||
791 | public WearableType WearableType |
||
792 | { |
||
793 | get { return (WearableType)Flags; } |
||
794 | set { Flags = (uint)value; } |
||
795 | } |
||
796 | } |
||
797 | |||
798 | /// <summary> |
||
799 | /// InventoryAnimation Class, A bvh encoded object which animates an avatar |
||
800 | /// </summary> |
||
801 | [Serializable()] |
||
802 | public class InventoryAnimation : InventoryItem |
||
803 | { |
||
804 | /// <summary> |
||
805 | /// Construct an InventoryAnimation object |
||
806 | /// </summary> |
||
807 | /// <param name="itemID">A <seealso cref="OpenMetaverse.UUID"/> which becomes the |
||
808 | /// <seealso cref="OpenMetaverse.InventoryItem"/> objects AssetUUID</param> |
||
809 | public InventoryAnimation(UUID itemID) |
||
810 | : base(itemID) |
||
811 | { |
||
812 | InventoryType = InventoryType.Animation; |
||
813 | } |
||
814 | |||
815 | /// <summary> |
||
816 | /// Construct an InventoryAnimation object from a serialization stream |
||
817 | /// </summary> |
||
818 | public InventoryAnimation(SerializationInfo info, StreamingContext ctxt) |
||
819 | : base(info, ctxt) |
||
820 | { |
||
821 | InventoryType = InventoryType.Animation; |
||
822 | } |
||
823 | |||
824 | |||
825 | } |
||
826 | |||
827 | /// <summary> |
||
828 | /// InventoryGesture Class, details on a series of animations, sounds, and actions |
||
829 | /// </summary> |
||
830 | [Serializable()] |
||
831 | public class InventoryGesture : InventoryItem |
||
832 | { |
||
833 | /// <summary> |
||
834 | /// Construct an InventoryGesture object |
||
835 | /// </summary> |
||
836 | /// <param name="itemID">A <seealso cref="OpenMetaverse.UUID"/> which becomes the |
||
837 | /// <seealso cref="OpenMetaverse.InventoryItem"/> objects AssetUUID</param> |
||
838 | public InventoryGesture(UUID itemID) |
||
839 | : base(itemID) |
||
840 | { |
||
841 | InventoryType = InventoryType.Gesture; |
||
842 | } |
||
843 | |||
844 | /// <summary> |
||
845 | /// Construct an InventoryGesture object from a serialization stream |
||
846 | /// </summary> |
||
847 | public InventoryGesture(SerializationInfo info, StreamingContext ctxt) |
||
848 | : base(info, ctxt) |
||
849 | { |
||
850 | InventoryType = InventoryType.Gesture; |
||
851 | } |
||
852 | } |
||
853 | |||
854 | /// <summary> |
||
855 | /// A folder contains <seealso cref="T:OpenMetaverse.InventoryItem"/>s and has certain attributes specific |
||
856 | /// to itself |
||
857 | /// </summary> |
||
858 | [Serializable()] |
||
859 | public class InventoryFolder : InventoryBase |
||
860 | { |
||
861 | /// <summary>The Preferred <seealso cref="T:OpenMetaverse.AssetType"/> for a folder.</summary> |
||
862 | public AssetType PreferredType; |
||
863 | /// <summary>The Version of this folder</summary> |
||
864 | public int Version; |
||
865 | /// <summary>Number of child items this folder contains.</summary> |
||
866 | public int DescendentCount; |
||
867 | |||
868 | /// <summary> |
||
869 | /// Constructor |
||
870 | /// </summary> |
||
871 | /// <param name="itemID">UUID of the folder</param> |
||
872 | public InventoryFolder(UUID itemID) |
||
873 | : base(itemID) |
||
874 | { |
||
875 | PreferredType = AssetType.Unknown; |
||
876 | Version = 1; |
||
877 | DescendentCount = 0; |
||
878 | } |
||
879 | |||
880 | /// <summary> |
||
881 | /// |
||
882 | /// </summary> |
||
883 | /// <returns></returns> |
||
884 | public override string ToString() |
||
885 | { |
||
886 | return Name; |
||
887 | } |
||
888 | |||
889 | /// <summary> |
||
890 | /// Get Serilization data for this InventoryFolder object |
||
891 | /// </summary> |
||
892 | override public void GetObjectData(SerializationInfo info, StreamingContext ctxt) |
||
893 | { |
||
894 | base.GetObjectData(info, ctxt); |
||
895 | info.AddValue("PreferredType", PreferredType, typeof(AssetType)); |
||
896 | info.AddValue("Version", Version); |
||
897 | info.AddValue("DescendentCount", DescendentCount); |
||
898 | } |
||
899 | |||
900 | /// <summary> |
||
901 | /// Construct an InventoryFolder object from a serialization stream |
||
902 | /// </summary> |
||
903 | public InventoryFolder(SerializationInfo info, StreamingContext ctxt) |
||
904 | : base(info, ctxt) |
||
905 | { |
||
906 | PreferredType = (AssetType)info.GetValue("PreferredType", typeof(AssetType)); |
||
907 | Version = (int)info.GetValue("Version", typeof(int)); |
||
908 | DescendentCount = (int)info.GetValue("DescendentCount", typeof(int)); |
||
909 | } |
||
910 | |||
911 | /// <summary> |
||
912 | /// |
||
913 | /// </summary> |
||
914 | /// <returns></returns> |
||
915 | public override int GetHashCode() |
||
916 | { |
||
917 | return PreferredType.GetHashCode() ^ Version.GetHashCode() ^ DescendentCount.GetHashCode(); |
||
918 | } |
||
919 | |||
920 | /// <summary> |
||
921 | /// |
||
922 | /// </summary> |
||
923 | /// <param name="o"></param> |
||
924 | /// <returns></returns> |
||
925 | public override bool Equals(object o) |
||
926 | { |
||
927 | InventoryFolder folder = o as InventoryFolder; |
||
928 | return folder != null && Equals(folder); |
||
929 | } |
||
930 | |||
931 | /// <summary> |
||
932 | /// |
||
933 | /// </summary> |
||
934 | /// <param name="o"></param> |
||
935 | /// <returns></returns> |
||
936 | public override bool Equals(InventoryBase o) |
||
937 | { |
||
938 | InventoryFolder folder = o as InventoryFolder; |
||
939 | return folder != null && Equals(folder); |
||
940 | } |
||
941 | |||
942 | /// <summary> |
||
943 | /// |
||
944 | /// </summary> |
||
945 | /// <param name="o"></param> |
||
946 | /// <returns></returns> |
||
947 | public bool Equals(InventoryFolder o) |
||
948 | { |
||
949 | return base.Equals(o as InventoryBase) |
||
950 | && o.DescendentCount == DescendentCount |
||
951 | && o.PreferredType == PreferredType |
||
952 | && o.Version == Version; |
||
953 | } |
||
954 | |||
955 | /// <summary> |
||
956 | /// Create InventoryFolder from OSD |
||
957 | /// </summary> |
||
958 | /// <param name="data">OSD Data that makes up InventoryFolder</param> |
||
959 | /// <returns>Inventory folder created</returns> |
||
960 | public static InventoryFolder FromOSD(OSD data) |
||
961 | { |
||
962 | OSDMap res = (OSDMap)data; |
||
963 | UUID folderID = res.ContainsKey("category_id") ? res["category_id"] : res["folder_id"]; |
||
964 | InventoryFolder folder = new InventoryFolder(folderID); |
||
965 | folder.Name = res["name"]; |
||
966 | folder.DescendentCount = res["descendents"]; |
||
967 | folder.Version = res["version"]; |
||
968 | folder.OwnerID = res.ContainsKey("agent_id") ? res["agent_id"] : res["owner_id"]; |
||
969 | folder.ParentUUID = res["parent_id"]; |
||
970 | folder.PreferredType = (AssetType)(int)res["type_default"]; |
||
971 | return folder; |
||
972 | } |
||
973 | |||
974 | /// <summary> |
||
975 | /// Convert InventoryItem to OSD |
||
976 | /// </summary> |
||
977 | /// <returns>OSD representation of InventoryItem</returns> |
||
978 | public override OSD GetOSD() |
||
979 | { |
||
980 | OSDMap res = new OSDMap(4); |
||
981 | res["name"] = Name; |
||
982 | res["type_default"] = (int)PreferredType; |
||
983 | res["folder_id"] = UUID; |
||
984 | res["descendents"] = DescendentCount; |
||
985 | res["version"] = Version; |
||
986 | res["owner_id"] = OwnerID; |
||
987 | res["parent_id"] = ParentUUID; |
||
988 | return res; |
||
989 | } |
||
990 | |||
991 | } |
||
992 | |||
993 | #endregion Inventory Object Classes |
||
994 | |||
995 | /// <summary> |
||
996 | /// Tools for dealing with agents inventory |
||
997 | /// </summary> |
||
998 | [Serializable()] |
||
999 | public class InventoryManager |
||
1000 | { |
||
1001 | /// <summary>Used for converting shadow_id to asset_id</summary> |
||
1002 | public static readonly UUID MAGIC_ID = new UUID("3c115e51-04f4-523c-9fa6-98aff1034730"); |
||
1003 | |||
1004 | protected struct InventorySearch |
||
1005 | { |
||
1006 | public UUID Folder; |
||
1007 | public UUID Owner; |
||
1008 | public string[] Path; |
||
1009 | public int Level; |
||
1010 | } |
||
1011 | |||
1012 | #region Delegates |
||
1013 | |||
1014 | /// <summary> |
||
1015 | /// Callback for inventory item creation finishing |
||
1016 | /// </summary> |
||
1017 | /// <param name="success">Whether the request to create an inventory |
||
1018 | /// item succeeded or not</param> |
||
1019 | /// <param name="item">Inventory item being created. If success is |
||
1020 | /// false this will be null</param> |
||
1021 | public delegate void ItemCreatedCallback(bool success, InventoryItem item); |
||
1022 | |||
1023 | /// <summary> |
||
1024 | /// Callback for an inventory item being create from an uploaded asset |
||
1025 | /// </summary> |
||
1026 | /// <param name="success">true if inventory item creation was successful</param> |
||
1027 | /// <param name="status"></param> |
||
1028 | /// <param name="itemID"></param> |
||
1029 | /// <param name="assetID"></param> |
||
1030 | public delegate void ItemCreatedFromAssetCallback(bool success, string status, UUID itemID, UUID assetID); |
||
1031 | |||
1032 | /// <summary> |
||
1033 | /// |
||
1034 | /// </summary> |
||
1035 | /// <param name="item"></param> |
||
1036 | public delegate void ItemCopiedCallback(InventoryBase item); |
||
1037 | |||
1038 | /// <summary>The event subscribers, null of no subscribers</summary> |
||
1039 | private EventHandler<ItemReceivedEventArgs> m_ItemReceived; |
||
1040 | |||
1041 | ///<summary>Raises the ItemReceived Event</summary> |
||
1042 | /// <param name="e">A ItemReceivedEventArgs object containing |
||
1043 | /// the data sent from the simulator</param> |
||
1044 | protected virtual void OnItemReceived(ItemReceivedEventArgs e) |
||
1045 | { |
||
1046 | EventHandler<ItemReceivedEventArgs> handler = m_ItemReceived; |
||
1047 | if (handler != null) |
||
1048 | handler(this, e); |
||
1049 | } |
||
1050 | |||
1051 | /// <summary>Thread sync lock object</summary> |
||
1052 | private readonly object m_ItemReceivedLock = new object(); |
||
1053 | |||
1054 | /// <summary>Raised when the simulator sends us data containing |
||
1055 | /// ...</summary> |
||
1056 | public event EventHandler<ItemReceivedEventArgs> ItemReceived |
||
1057 | { |
||
1058 | add { lock (m_ItemReceivedLock) { m_ItemReceived += value; } } |
||
1059 | remove { lock (m_ItemReceivedLock) { m_ItemReceived -= value; } } |
||
1060 | } |
||
1061 | |||
1062 | |||
1063 | /// <summary>The event subscribers, null of no subscribers</summary> |
||
1064 | private EventHandler<FolderUpdatedEventArgs> m_FolderUpdated; |
||
1065 | |||
1066 | ///<summary>Raises the FolderUpdated Event</summary> |
||
1067 | /// <param name="e">A FolderUpdatedEventArgs object containing |
||
1068 | /// the data sent from the simulator</param> |
||
1069 | protected virtual void OnFolderUpdated(FolderUpdatedEventArgs e) |
||
1070 | { |
||
1071 | EventHandler<FolderUpdatedEventArgs> handler = m_FolderUpdated; |
||
1072 | if (handler != null) |
||
1073 | handler(this, e); |
||
1074 | } |
||
1075 | |||
1076 | /// <summary>Thread sync lock object</summary> |
||
1077 | private readonly object m_FolderUpdatedLock = new object(); |
||
1078 | |||
1079 | /// <summary>Raised when the simulator sends us data containing |
||
1080 | /// ...</summary> |
||
1081 | public event EventHandler<FolderUpdatedEventArgs> FolderUpdated |
||
1082 | { |
||
1083 | add { lock (m_FolderUpdatedLock) { m_FolderUpdated += value; } } |
||
1084 | remove { lock (m_FolderUpdatedLock) { m_FolderUpdated -= value; } } |
||
1085 | } |
||
1086 | |||
1087 | |||
1088 | /// <summary>The event subscribers, null of no subscribers</summary> |
||
1089 | private EventHandler<InventoryObjectOfferedEventArgs> m_InventoryObjectOffered; |
||
1090 | |||
1091 | ///<summary>Raises the InventoryObjectOffered Event</summary> |
||
1092 | /// <param name="e">A InventoryObjectOfferedEventArgs object containing |
||
1093 | /// the data sent from the simulator</param> |
||
1094 | protected virtual void OnInventoryObjectOffered(InventoryObjectOfferedEventArgs e) |
||
1095 | { |
||
1096 | EventHandler<InventoryObjectOfferedEventArgs> handler = m_InventoryObjectOffered; |
||
1097 | if (handler != null) |
||
1098 | handler(this, e); |
||
1099 | } |
||
1100 | |||
1101 | /// <summary>Thread sync lock object</summary> |
||
1102 | private readonly object m_InventoryObjectOfferedLock = new object(); |
||
1103 | |||
1104 | /// <summary>Raised when the simulator sends us data containing |
||
1105 | /// an inventory object sent by another avatar or primitive</summary> |
||
1106 | public event EventHandler<InventoryObjectOfferedEventArgs> InventoryObjectOffered |
||
1107 | { |
||
1108 | add { lock (m_InventoryObjectOfferedLock) { m_InventoryObjectOffered += value; } } |
||
1109 | remove { lock (m_InventoryObjectOfferedLock) { m_InventoryObjectOffered -= value; } } |
||
1110 | } |
||
1111 | |||
1112 | /// <summary>The event subscribers, null of no subscribers</summary> |
||
1113 | private EventHandler<TaskItemReceivedEventArgs> m_TaskItemReceived; |
||
1114 | |||
1115 | ///<summary>Raises the TaskItemReceived Event</summary> |
||
1116 | /// <param name="e">A TaskItemReceivedEventArgs object containing |
||
1117 | /// the data sent from the simulator</param> |
||
1118 | protected virtual void OnTaskItemReceived(TaskItemReceivedEventArgs e) |
||
1119 | { |
||
1120 | EventHandler<TaskItemReceivedEventArgs> handler = m_TaskItemReceived; |
||
1121 | if (handler != null) |
||
1122 | handler(this, e); |
||
1123 | } |
||
1124 | |||
1125 | /// <summary>Thread sync lock object</summary> |
||
1126 | private readonly object m_TaskItemReceivedLock = new object(); |
||
1127 | |||
1128 | /// <summary>Raised when the simulator sends us data containing |
||
1129 | /// ...</summary> |
||
1130 | public event EventHandler<TaskItemReceivedEventArgs> TaskItemReceived |
||
1131 | { |
||
1132 | add { lock (m_TaskItemReceivedLock) { m_TaskItemReceived += value; } } |
||
1133 | remove { lock (m_TaskItemReceivedLock) { m_TaskItemReceived -= value; } } |
||
1134 | } |
||
1135 | |||
1136 | |||
1137 | /// <summary>The event subscribers, null of no subscribers</summary> |
||
1138 | private EventHandler<FindObjectByPathReplyEventArgs> m_FindObjectByPathReply; |
||
1139 | |||
1140 | ///<summary>Raises the FindObjectByPath Event</summary> |
||
1141 | /// <param name="e">A FindObjectByPathEventArgs object containing |
||
1142 | /// the data sent from the simulator</param> |
||
1143 | protected virtual void OnFindObjectByPathReply(FindObjectByPathReplyEventArgs e) |
||
1144 | { |
||
1145 | EventHandler<FindObjectByPathReplyEventArgs> handler = m_FindObjectByPathReply; |
||
1146 | if (handler != null) |
||
1147 | handler(this, e); |
||
1148 | } |
||
1149 | |||
1150 | /// <summary>Thread sync lock object</summary> |
||
1151 | private readonly object m_FindObjectByPathReplyLock = new object(); |
||
1152 | |||
1153 | /// <summary>Raised when the simulator sends us data containing |
||
1154 | /// ...</summary> |
||
1155 | public event EventHandler<FindObjectByPathReplyEventArgs> FindObjectByPathReply |
||
1156 | { |
||
1157 | add { lock (m_FindObjectByPathReplyLock) { m_FindObjectByPathReply += value; } } |
||
1158 | remove { lock (m_FindObjectByPathReplyLock) { m_FindObjectByPathReply -= value; } } |
||
1159 | } |
||
1160 | |||
1161 | |||
1162 | /// <summary>The event subscribers, null of no subscribers</summary> |
||
1163 | private EventHandler<TaskInventoryReplyEventArgs> m_TaskInventoryReply; |
||
1164 | |||
1165 | ///<summary>Raises the TaskInventoryReply Event</summary> |
||
1166 | /// <param name="e">A TaskInventoryReplyEventArgs object containing |
||
1167 | /// the data sent from the simulator</param> |
||
1168 | protected virtual void OnTaskInventoryReply(TaskInventoryReplyEventArgs e) |
||
1169 | { |
||
1170 | EventHandler<TaskInventoryReplyEventArgs> handler = m_TaskInventoryReply; |
||
1171 | if (handler != null) |
||
1172 | handler(this, e); |
||
1173 | } |
||
1174 | |||
1175 | /// <summary>Thread sync lock object</summary> |
||
1176 | private readonly object m_TaskInventoryReplyLock = new object(); |
||
1177 | |||
1178 | /// <summary>Raised when the simulator sends us data containing |
||
1179 | /// ...</summary> |
||
1180 | public event EventHandler<TaskInventoryReplyEventArgs> TaskInventoryReply |
||
1181 | { |
||
1182 | add { lock (m_TaskInventoryReplyLock) { m_TaskInventoryReply += value; } } |
||
1183 | remove { lock (m_TaskInventoryReplyLock) { m_TaskInventoryReply -= value; } } |
||
1184 | } |
||
1185 | |||
1186 | /// <summary> |
||
1187 | /// Reply received when uploading an inventory asset |
||
1188 | /// </summary> |
||
1189 | /// <param name="success">Has upload been successful</param> |
||
1190 | /// <param name="status">Error message if upload failed</param> |
||
1191 | /// <param name="itemID">Inventory asset UUID</param> |
||
1192 | /// <param name="assetID">New asset UUID</param> |
||
1193 | public delegate void InventoryUploadedAssetCallback(bool success, string status, UUID itemID, UUID assetID); |
||
1194 | |||
1195 | |||
1196 | /// <summary>The event subscribers, null of no subscribers</summary> |
||
1197 | private EventHandler<SaveAssetToInventoryEventArgs> m_SaveAssetToInventory; |
||
1198 | |||
1199 | ///<summary>Raises the SaveAssetToInventory Event</summary> |
||
1200 | /// <param name="e">A SaveAssetToInventoryEventArgs object containing |
||
1201 | /// the data sent from the simulator</param> |
||
1202 | protected virtual void OnSaveAssetToInventory(SaveAssetToInventoryEventArgs e) |
||
1203 | { |
||
1204 | EventHandler<SaveAssetToInventoryEventArgs> handler = m_SaveAssetToInventory; |
||
1205 | if (handler != null) |
||
1206 | handler(this, e); |
||
1207 | } |
||
1208 | |||
1209 | /// <summary>Thread sync lock object</summary> |
||
1210 | private readonly object m_SaveAssetToInventoryLock = new object(); |
||
1211 | |||
1212 | /// <summary>Raised when the simulator sends us data containing |
||
1213 | /// ...</summary> |
||
1214 | public event EventHandler<SaveAssetToInventoryEventArgs> SaveAssetToInventory |
||
1215 | { |
||
1216 | add { lock (m_SaveAssetToInventoryLock) { m_SaveAssetToInventory += value; } } |
||
1217 | remove { lock (m_SaveAssetToInventoryLock) { m_SaveAssetToInventory -= value; } } |
||
1218 | } |
||
1219 | |||
1220 | /// <summary> |
||
1221 | /// Delegate that is invoked when script upload is completed |
||
1222 | /// </summary> |
||
1223 | /// <param name="uploadSuccess">Has upload succeded (note, there still might be compile errors)</param> |
||
1224 | /// <param name="uploadStatus">Upload status message</param> |
||
1225 | /// <param name="compileSuccess">Is compilation successful</param> |
||
1226 | /// <param name="compileMessages">If compilation failed, list of error messages, null on compilation success</param> |
||
1227 | /// <param name="itemID">Script inventory UUID</param> |
||
1228 | /// <param name="assetID">Script's new asset UUID</param> |
||
1229 | public delegate void ScriptUpdatedCallback(bool uploadSuccess, string uploadStatus, bool compileSuccess, List<string> compileMessages, UUID itemID, UUID assetID); |
||
1230 | |||
1231 | /// <summary>The event subscribers, null of no subscribers</summary> |
||
1232 | private EventHandler<ScriptRunningReplyEventArgs> m_ScriptRunningReply; |
||
1233 | |||
1234 | ///<summary>Raises the ScriptRunningReply Event</summary> |
||
1235 | /// <param name="e">A ScriptRunningReplyEventArgs object containing |
||
1236 | /// the data sent from the simulator</param> |
||
1237 | protected virtual void OnScriptRunningReply(ScriptRunningReplyEventArgs e) |
||
1238 | { |
||
1239 | EventHandler<ScriptRunningReplyEventArgs> handler = m_ScriptRunningReply; |
||
1240 | if (handler != null) |
||
1241 | handler(this, e); |
||
1242 | } |
||
1243 | |||
1244 | /// <summary>Thread sync lock object</summary> |
||
1245 | private readonly object m_ScriptRunningReplyLock = new object(); |
||
1246 | |||
1247 | /// <summary>Raised when the simulator sends us data containing |
||
1248 | /// ...</summary> |
||
1249 | public event EventHandler<ScriptRunningReplyEventArgs> ScriptRunningReply |
||
1250 | { |
||
1251 | add { lock (m_ScriptRunningReplyLock) { m_ScriptRunningReply += value; } } |
||
1252 | remove { lock (m_ScriptRunningReplyLock) { m_ScriptRunningReply -= value; } } |
||
1253 | } |
||
1254 | #endregion Delegates |
||
1255 | |||
1256 | #region String Arrays |
||
1257 | |||
1258 | /// <summary>Partial mapping of AssetTypes to folder names</summary> |
||
1259 | private static readonly string[] _NewFolderNames = new string[] |
||
1260 | { |
||
1261 | "Textures", |
||
1262 | "Sounds", |
||
1263 | "Calling Cards", |
||
1264 | "Landmarks", |
||
1265 | "Scripts", |
||
1266 | "Clothing", |
||
1267 | "Objects", |
||
1268 | "Notecards", |
||
1269 | "New Folder", |
||
1270 | "Inventory", |
||
1271 | "Scripts", |
||
1272 | "Scripts", |
||
1273 | "Uncompressed Images", |
||
1274 | "Body Parts", |
||
1275 | "Trash", |
||
1276 | "Photo Album", |
||
1277 | "Lost And Found", |
||
1278 | "Uncompressed Sounds", |
||
1279 | "Uncompressed Images", |
||
1280 | "Uncompressed Images", |
||
1281 | "Animations", |
||
1282 | "Gestures" |
||
1283 | }; |
||
1284 | |||
1285 | #endregion String Arrays |
||
1286 | |||
1287 | private GridClient Client; |
||
1288 | private Inventory _Store; |
||
1289 | //private Random _RandNumbers = new Random(); |
||
1290 | private object _CallbacksLock = new object(); |
||
1291 | private uint _CallbackPos; |
||
1292 | private Dictionary<uint, ItemCreatedCallback> _ItemCreatedCallbacks = new Dictionary<uint, ItemCreatedCallback>(); |
||
1293 | private Dictionary<uint, ItemCopiedCallback> _ItemCopiedCallbacks = new Dictionary<uint, ItemCopiedCallback>(); |
||
1294 | private Dictionary<uint, InventoryType> _ItemInventoryTypeRequest = new Dictionary<uint, InventoryType>(); |
||
1295 | private List<InventorySearch> _Searches = new List<InventorySearch>(); |
||
1296 | |||
1297 | #region Properties |
||
1298 | |||
1299 | /// <summary> |
||
1300 | /// Get this agents Inventory data |
||
1301 | /// </summary> |
||
1302 | public Inventory Store { get { return _Store; } } |
||
1303 | |||
1304 | #endregion Properties |
||
1305 | |||
1306 | /// <summary> |
||
1307 | /// Default constructor |
||
1308 | /// </summary> |
||
1309 | /// <param name="client">Reference to the GridClient object</param> |
||
1310 | public InventoryManager(GridClient client) |
||
1311 | { |
||
1312 | Client = client; |
||
1313 | |||
1314 | Client.Network.RegisterCallback(PacketType.UpdateCreateInventoryItem, UpdateCreateInventoryItemHandler); |
||
1315 | Client.Network.RegisterCallback(PacketType.SaveAssetIntoInventory, SaveAssetIntoInventoryHandler); |
||
1316 | Client.Network.RegisterCallback(PacketType.BulkUpdateInventory, BulkUpdateInventoryHandler); |
||
1317 | Client.Network.RegisterEventCallback("BulkUpdateInventory", new Caps.EventQueueCallback(BulkUpdateInventoryCapHandler)); |
||
1318 | Client.Network.RegisterCallback(PacketType.MoveInventoryItem, MoveInventoryItemHandler); |
||
1319 | Client.Network.RegisterCallback(PacketType.InventoryDescendents, InventoryDescendentsHandler); |
||
1320 | Client.Network.RegisterCallback(PacketType.FetchInventoryReply, FetchInventoryReplyHandler); |
||
1321 | Client.Network.RegisterCallback(PacketType.ReplyTaskInventory, ReplyTaskInventoryHandler); |
||
1322 | Client.Network.RegisterEventCallback("ScriptRunningReply", new Caps.EventQueueCallback(ScriptRunningReplyMessageHandler)); |
||
1323 | |||
1324 | // Watch for inventory given to us through instant message |
||
1325 | Client.Self.IM += Self_IM; |
||
1326 | |||
1327 | // Register extra parameters with login and parse the inventory data that comes back |
||
1328 | Client.Network.RegisterLoginResponseCallback( |
||
1329 | new NetworkManager.LoginResponseCallback(Network_OnLoginResponse), |
||
1330 | new string[] { |
||
1331 | "inventory-root", "inventory-skeleton", "inventory-lib-root", |
||
1332 | "inventory-lib-owner", "inventory-skel-lib"}); |
||
1333 | } |
||
1334 | |||
1335 | |||
1336 | #region Fetch |
||
1337 | |||
1338 | /// <summary> |
||
1339 | /// Fetch an inventory item from the dataserver |
||
1340 | /// </summary> |
||
1341 | /// <param name="itemID">The items <seealso cref="UUID"/></param> |
||
1342 | /// <param name="ownerID">The item Owners <seealso cref="OpenMetaverse.UUID"/></param> |
||
1343 | /// <param name="timeoutMS">a integer representing the number of milliseconds to wait for results</param> |
||
1344 | /// <returns>An <seealso cref="InventoryItem"/> object on success, or null if no item was found</returns> |
||
1345 | /// <remarks>Items will also be sent to the <seealso cref="InventoryManager.OnItemReceived"/> event</remarks> |
||
1346 | public InventoryItem FetchItem(UUID itemID, UUID ownerID, int timeoutMS) |
||
1347 | { |
||
1348 | AutoResetEvent fetchEvent = new AutoResetEvent(false); |
||
1349 | InventoryItem fetchedItem = null; |
||
1350 | |||
1351 | EventHandler<ItemReceivedEventArgs> callback = |
||
1352 | delegate(object sender, ItemReceivedEventArgs e) |
||
1353 | { |
||
1354 | if (e.Item.UUID == itemID) |
||
1355 | { |
||
1356 | fetchedItem = e.Item; |
||
1357 | fetchEvent.Set(); |
||
1358 | } |
||
1359 | }; |
||
1360 | |||
1361 | ItemReceived += callback; |
||
1362 | RequestFetchInventory(itemID, ownerID); |
||
1363 | |||
1364 | fetchEvent.WaitOne(timeoutMS, false); |
||
1365 | ItemReceived -= callback; |
||
1366 | |||
1367 | return fetchedItem; |
||
1368 | } |
||
1369 | |||
1370 | /// <summary> |
||
1371 | /// Request A single inventory item |
||
1372 | /// </summary> |
||
1373 | /// <param name="itemID">The items <seealso cref="OpenMetaverse.UUID"/></param> |
||
1374 | /// <param name="ownerID">The item Owners <seealso cref="OpenMetaverse.UUID"/></param> |
||
1375 | /// <seealso cref="InventoryManager.OnItemReceived"/> |
||
1376 | public void RequestFetchInventory(UUID itemID, UUID ownerID) |
||
1377 | { |
||
1378 | RequestFetchInventory(new List<UUID>(1) { itemID }, new List<UUID>(1) { ownerID }); |
||
1379 | } |
||
1380 | |||
1381 | /// <summary> |
||
1382 | /// Request inventory items |
||
1383 | /// </summary> |
||
1384 | /// <param name="itemIDs">Inventory items to request</param> |
||
1385 | /// <param name="ownerIDs">Owners of the inventory items</param> |
||
1386 | /// <seealso cref="InventoryManager.OnItemReceived"/> |
||
1387 | public void RequestFetchInventory(List<UUID> itemIDs, List<UUID> ownerIDs) |
||
1388 | { |
||
1389 | if (itemIDs.Count != ownerIDs.Count) |
||
1390 | throw new ArgumentException("itemIDs and ownerIDs must contain the same number of entries"); |
||
1391 | |||
1392 | if (Client.Settings.HTTP_INVENTORY && |
||
1393 | Client.Network.CurrentSim.Caps != null && |
||
1394 | Client.Network.CurrentSim.Caps.CapabilityURI("FetchInventory2") != null) |
||
1395 | { |
||
1396 | RequestFetchInventoryCap(itemIDs, ownerIDs); |
||
1397 | return; |
||
1398 | } |
||
1399 | |||
1400 | |||
1401 | FetchInventoryPacket fetch = new FetchInventoryPacket(); |
||
1402 | fetch.AgentData = new FetchInventoryPacket.AgentDataBlock(); |
||
1403 | fetch.AgentData.AgentID = Client.Self.AgentID; |
||
1404 | fetch.AgentData.SessionID = Client.Self.SessionID; |
||
1405 | |||
1406 | fetch.InventoryData = new FetchInventoryPacket.InventoryDataBlock[itemIDs.Count]; |
||
1407 | for (int i = 0; i < itemIDs.Count; i++) |
||
1408 | { |
||
1409 | fetch.InventoryData[i] = new FetchInventoryPacket.InventoryDataBlock(); |
||
1410 | fetch.InventoryData[i].ItemID = itemIDs[i]; |
||
1411 | fetch.InventoryData[i].OwnerID = ownerIDs[i]; |
||
1412 | } |
||
1413 | |||
1414 | Client.Network.SendPacket(fetch); |
||
1415 | } |
||
1416 | |||
1417 | /// <summary> |
||
1418 | /// Request inventory items via Capabilities |
||
1419 | /// </summary> |
||
1420 | /// <param name="itemIDs">Inventory items to request</param> |
||
1421 | /// <param name="ownerIDs">Owners of the inventory items</param> |
||
1422 | /// <seealso cref="InventoryManager.OnItemReceived"/> |
||
1423 | private void RequestFetchInventoryCap(List<UUID> itemIDs, List<UUID> ownerIDs) |
||
1424 | { |
||
1425 | if (itemIDs.Count != ownerIDs.Count) |
||
1426 | throw new ArgumentException("itemIDs and ownerIDs must contain the same number of entries"); |
||
1427 | |||
1428 | if (Client.Settings.HTTP_INVENTORY && |
||
1429 | Client.Network.CurrentSim.Caps != null && |
||
1430 | Client.Network.CurrentSim.Caps.CapabilityURI("FetchInventory2") != null) |
||
1431 | { |
||
1432 | Uri url = Client.Network.CurrentSim.Caps.CapabilityURI("FetchInventory2"); |
||
1433 | CapsClient request = new CapsClient(url); |
||
1434 | |||
1435 | request.OnComplete += (client, result, error) => |
||
1436 | { |
||
1437 | if (error == null) |
||
1438 | { |
||
1439 | try |
||
1440 | { |
||
1441 | OSDMap res = (OSDMap)result; |
||
1442 | OSDArray itemsOSD = (OSDArray)res["items"]; |
||
1443 | |||
1444 | for (int i = 0; i < itemsOSD.Count; i++) |
||
1445 | { |
||
1446 | InventoryItem item = InventoryItem.FromOSD(itemsOSD[i]); |
||
1447 | _Store[item.UUID] = item; |
||
1448 | OnItemReceived(new ItemReceivedEventArgs(item)); |
||
1449 | } |
||
1450 | } |
||
1451 | catch (Exception ex) |
||
1452 | { |
||
1453 | Logger.Log("Failed getting data from FetchInventory2 capability.", Helpers.LogLevel.Error, Client, ex); |
||
1454 | } |
||
1455 | } |
||
1456 | }; |
||
1457 | |||
1458 | OSDMap OSDRequest = new OSDMap(); |
||
1459 | OSDRequest["agent_id"] = Client.Self.AgentID; |
||
1460 | |||
1461 | OSDArray items = new OSDArray(itemIDs.Count); |
||
1462 | for (int i = 0; i < itemIDs.Count; i++) |
||
1463 | { |
||
1464 | OSDMap item = new OSDMap(2); |
||
1465 | item["item_id"] = itemIDs[i]; |
||
1466 | item["owner_id"] = ownerIDs[i]; |
||
1467 | items.Add(item); |
||
1468 | } |
||
1469 | |||
1470 | OSDRequest["items"] = items; |
||
1471 | |||
1472 | request.BeginGetResponse(OSDRequest, OSDFormat.Xml, Client.Settings.CAPS_TIMEOUT); |
||
1473 | } |
||
1474 | } |
||
1475 | |||
1476 | /// <summary> |
||
1477 | /// Get contents of a folder |
||
1478 | /// </summary> |
||
1479 | /// <param name="folder">The <seealso cref="UUID"/> of the folder to search</param> |
||
1480 | /// <param name="owner">The <seealso cref="UUID"/> of the folders owner</param> |
||
1481 | /// <param name="folders">true to retrieve folders</param> |
||
1482 | /// <param name="items">true to retrieve items</param> |
||
1483 | /// <param name="order">sort order to return results in</param> |
||
1484 | /// <param name="timeoutMS">a integer representing the number of milliseconds to wait for results</param> |
||
1485 | /// <returns>A list of inventory items matching search criteria within folder</returns> |
||
1486 | /// <seealso cref="InventoryManager.RequestFolderContents"/> |
||
1487 | /// <remarks>InventoryFolder.DescendentCount will only be accurate if both folders and items are |
||
1488 | /// requested</remarks> |
||
1489 | public List<InventoryBase> FolderContents(UUID folder, UUID owner, bool folders, bool items, |
||
1490 | InventorySortOrder order, int timeoutMS) |
||
1491 | { |
||
1492 | List<InventoryBase> objects = null; |
||
1493 | AutoResetEvent fetchEvent = new AutoResetEvent(false); |
||
1494 | |||
1495 | EventHandler<FolderUpdatedEventArgs> callback = |
||
1496 | delegate(object sender, FolderUpdatedEventArgs e) |
||
1497 | { |
||
1498 | if (e.FolderID == folder |
||
1499 | && _Store[folder] is InventoryFolder) |
||
1500 | { |
||
1501 | // InventoryDescendentsHandler only stores DescendendCount if both folders and items are fetched. |
||
1502 | if (_Store.GetContents(folder).Count >= ((InventoryFolder)_Store[folder]).DescendentCount) |
||
1503 | { |
||
1504 | |||
1505 | fetchEvent.Set(); |
||
1506 | } |
||
1507 | } |
||
1508 | else |
||
1509 | { |
||
1510 | fetchEvent.Set(); |
||
1511 | } |
||
1512 | }; |
||
1513 | |||
1514 | FolderUpdated += callback; |
||
1515 | |||
1516 | RequestFolderContents(folder, owner, folders, items, order); |
||
1517 | if (fetchEvent.WaitOne(timeoutMS, false)) |
||
1518 | objects = _Store.GetContents(folder); |
||
1519 | |||
1520 | FolderUpdated -= callback; |
||
1521 | |||
1522 | return objects; |
||
1523 | } |
||
1524 | |||
1525 | /// <summary> |
||
1526 | /// Request the contents of an inventory folder |
||
1527 | /// </summary> |
||
1528 | /// <param name="folder">The folder to search</param> |
||
1529 | /// <param name="owner">The folder owners <seealso cref="UUID"/></param> |
||
1530 | /// <param name="folders">true to return <seealso cref="InventoryManager.InventoryFolder"/>s contained in folder</param> |
||
1531 | /// <param name="items">true to return <seealso cref="InventoryManager.InventoryItem"/>s containd in folder</param> |
||
1532 | /// <param name="order">the sort order to return items in</param> |
||
1533 | /// <seealso cref="InventoryManager.FolderContents"/> |
||
1534 | public void RequestFolderContents(UUID folder, UUID owner, bool folders, bool items, |
||
1535 | InventorySortOrder order) |
||
1536 | { |
||
1537 | string cap = owner == Client.Self.AgentID ? "FetchInventoryDescendents2" : "FetchLibDescendents2"; |
||
1538 | |||
1539 | if (Client.Settings.HTTP_INVENTORY && |
||
1540 | Client.Network.CurrentSim.Caps != null && |
||
1541 | Client.Network.CurrentSim.Caps.CapabilityURI(cap) != null) |
||
1542 | { |
||
1543 | RequestFolderContentsCap(folder, owner, folders, items, order); |
||
1544 | return; |
||
1545 | } |
||
1546 | |||
1547 | FetchInventoryDescendentsPacket fetch = new FetchInventoryDescendentsPacket(); |
||
1548 | fetch.AgentData.AgentID = Client.Self.AgentID; |
||
1549 | fetch.AgentData.SessionID = Client.Self.SessionID; |
||
1550 | |||
1551 | fetch.InventoryData.FetchFolders = folders; |
||
1552 | fetch.InventoryData.FetchItems = items; |
||
1553 | fetch.InventoryData.FolderID = folder; |
||
1554 | fetch.InventoryData.OwnerID = owner; |
||
1555 | fetch.InventoryData.SortOrder = (int)order; |
||
1556 | |||
1557 | Client.Network.SendPacket(fetch); |
||
1558 | } |
||
1559 | |||
1560 | /// <summary> |
||
1561 | /// Request the contents of an inventory folder using HTTP capabilities |
||
1562 | /// </summary> |
||
1563 | /// <param name="folderID">The folder to search</param> |
||
1564 | /// <param name="ownerID">The folder owners <seealso cref="UUID"/></param> |
||
1565 | /// <param name="fetchFolders">true to return <seealso cref="InventoryManager.InventoryFolder"/>s contained in folder</param> |
||
1566 | /// <param name="fetchItems">true to return <seealso cref="InventoryManager.InventoryItem"/>s containd in folder</param> |
||
1567 | /// <param name="order">the sort order to return items in</param> |
||
1568 | /// <seealso cref="InventoryManager.FolderContents"/> |
||
1569 | public void RequestFolderContentsCap(UUID folderID, UUID ownerID, bool fetchFolders, bool fetchItems, |
||
1570 | InventorySortOrder order) |
||
1571 | { |
||
1572 | Uri url = null; |
||
1573 | string cap = ownerID == Client.Self.AgentID ? "FetchInventoryDescendents2" : "FetchLibDescendents2"; |
||
1574 | if (Client.Network.CurrentSim.Caps == null || |
||
1575 | null == (url = Client.Network.CurrentSim.Caps.CapabilityURI(cap))) |
||
1576 | { |
||
1577 | Logger.Log(cap + " capability not available in the current sim", Helpers.LogLevel.Warning, Client); |
||
1578 | OnFolderUpdated(new FolderUpdatedEventArgs(folderID, false)); |
||
1579 | return; |
||
1580 | } |
||
1581 | |||
1582 | InventoryFolder folder = new InventoryFolder(folderID); |
||
1583 | folder.OwnerID = ownerID; |
||
1584 | folder.UUID = folderID; |
||
1585 | RequestFolderContentsCap(new List<InventoryFolder>() { folder }, url, fetchFolders, fetchItems, order); |
||
1586 | } |
||
1587 | |||
1588 | public void RequestFolderContentsCap(List<InventoryFolder> batch, Uri url, bool fetchFolders, bool fetchItems, InventorySortOrder order) |
||
1589 | { |
||
1590 | |||
1591 | try |
||
1592 | { |
||
1593 | CapsClient request = new CapsClient(url); |
||
1594 | request.OnComplete += (client, result, error) => |
||
1595 | { |
||
1596 | try |
||
1597 | { |
||
1598 | if (error != null) |
||
1599 | { |
||
1600 | throw error; |
||
1601 | } |
||
1602 | |||
1603 | OSDMap resultMap = ((OSDMap)result); |
||
1604 | if (resultMap.ContainsKey("folders")) |
||
1605 | { |
||
1606 | OSDArray fetchedFolders = (OSDArray)resultMap["folders"]; |
||
1607 | for (int fetchedFolderNr = 0; fetchedFolderNr < fetchedFolders.Count; fetchedFolderNr++) |
||
1608 | { |
||
1609 | OSDMap res = (OSDMap)fetchedFolders[fetchedFolderNr]; |
||
1610 | InventoryFolder fetchedFolder = null; |
||
1611 | |||
1612 | if (_Store.Contains(res["folder_id"]) |
||
1613 | && _Store[res["folder_id"]] is InventoryFolder) |
||
1614 | { |
||
1615 | fetchedFolder = (InventoryFolder)_Store[res["folder_id"]]; |
||
1616 | } |
||
1617 | else |
||
1618 | { |
||
1619 | fetchedFolder = new InventoryFolder(res["folder_id"]); |
||
1620 | _Store[res["folder_id"]] = fetchedFolder; |
||
1621 | } |
||
1622 | fetchedFolder.DescendentCount = res["descendents"]; |
||
1623 | fetchedFolder.Version = res["version"]; |
||
1624 | fetchedFolder.OwnerID = res["owner_id"]; |
||
1625 | _Store.GetNodeFor(fetchedFolder.UUID).NeedsUpdate = false; |
||
1626 | |||
1627 | // Do we have any descendants |
||
1628 | if (fetchedFolder.DescendentCount > 0) |
||
1629 | { |
||
1630 | // Fetch descendent folders |
||
1631 | if (res["categories"] is OSDArray) |
||
1632 | { |
||
1633 | OSDArray folders = (OSDArray)res["categories"]; |
||
1634 | for (int i = 0; i < folders.Count; i++) |
||
1635 | { |
||
1636 | OSDMap descFolder = (OSDMap)folders[i]; |
||
1637 | InventoryFolder folder; |
||
1638 | UUID folderID = descFolder.ContainsKey("category_id") ? descFolder["category_id"] : descFolder["folder_id"]; |
||
1639 | if (!_Store.Contains(folderID)) |
||
1640 | { |
||
1641 | folder = new InventoryFolder(folderID); |
||
1642 | folder.ParentUUID = descFolder["parent_id"]; |
||
1643 | _Store[folderID] = folder; |
||
1644 | } |
||
1645 | else |
||
1646 | { |
||
1647 | folder = (InventoryFolder)_Store[folderID]; |
||
1648 | } |
||
1649 | |||
1650 | folder.OwnerID = descFolder["agent_id"]; |
||
1651 | folder.ParentUUID = descFolder["parent_id"]; |
||
1652 | folder.Name = descFolder["name"]; |
||
1653 | folder.Version = descFolder["version"]; |
||
1654 | folder.PreferredType = (AssetType)(int)descFolder["type_default"]; |
||
1655 | } |
||
1656 | |||
1657 | // Fetch descendent items |
||
1658 | OSDArray items = (OSDArray)res["items"]; |
||
1659 | for (int i = 0; i < items.Count; i++) |
||
1660 | { |
||
1661 | InventoryItem item = InventoryItem.FromOSD(items[i]); |
||
1662 | _Store[item.UUID] = item; |
||
1663 | } |
||
1664 | } |
||
1665 | } |
||
1666 | |||
1667 | OnFolderUpdated(new FolderUpdatedEventArgs(res["folder_id"], true)); |
||
1668 | } |
||
1669 | } |
||
1670 | } |
||
1671 | catch (Exception exc) |
||
1672 | { |
||
1673 | Logger.Log(string.Format("Failed to fetch inventory descendants: {0}\n{1}", exc.Message, exc.StackTrace.ToString()), Helpers.LogLevel.Warning, Client); |
||
1674 | foreach (var f in batch) |
||
1675 | { |
||
1676 | OnFolderUpdated(new FolderUpdatedEventArgs(f.UUID, false)); |
||
1677 | } |
||
1678 | return; |
||
1679 | } |
||
1680 | |||
1681 | }; |
||
1682 | |||
1683 | // Construct request |
||
1684 | OSDArray requestedFolders = new OSDArray(1); |
||
1685 | foreach (var f in batch) |
||
1686 | { |
||
1687 | OSDMap requestedFolder = new OSDMap(1); |
||
1688 | requestedFolder["folder_id"] = f.UUID; |
||
1689 | requestedFolder["owner_id"] = f.OwnerID; |
||
1690 | requestedFolder["fetch_folders"] = fetchFolders; |
||
1691 | requestedFolder["fetch_items"] = fetchItems; |
||
1692 | requestedFolder["sort_order"] = (int)order; |
||
1693 | |||
1694 | requestedFolders.Add(requestedFolder); |
||
1695 | } |
||
1696 | OSDMap req = new OSDMap(1); |
||
1697 | req["folders"] = requestedFolders; |
||
1698 | |||
1699 | request.BeginGetResponse(req, OSDFormat.Xml, Client.Settings.CAPS_TIMEOUT); |
||
1700 | } |
||
1701 | catch (Exception ex) |
||
1702 | { |
||
1703 | Logger.Log(string.Format("Failed to fetch inventory descendants: {0}\n{1}", ex.Message, ex.StackTrace.ToString()), Helpers.LogLevel.Warning, Client); |
||
1704 | foreach (var f in batch) |
||
1705 | { |
||
1706 | OnFolderUpdated(new FolderUpdatedEventArgs(f.UUID, false)); |
||
1707 | } |
||
1708 | return; |
||
1709 | } |
||
1710 | } |
||
1711 | |||
1712 | #endregion Fetch |
||
1713 | |||
1714 | #region Find |
||
1715 | |||
1716 | /// <summary> |
||
1717 | /// Returns the UUID of the folder (category) that defaults to |
||
1718 | /// containing 'type'. The folder is not necessarily only for that |
||
1719 | /// type |
||
1720 | /// </summary> |
||
1721 | /// <remarks>This will return the root folder if one does not exist</remarks> |
||
1722 | /// <param name="type"></param> |
||
1723 | /// <returns>The UUID of the desired folder if found, the UUID of the RootFolder |
||
1724 | /// if not found, or UUID.Zero on failure</returns> |
||
1725 | public UUID FindFolderForType(AssetType type) |
||
1726 | { |
||
1727 | if (_Store == null) |
||
1728 | { |
||
1729 | Logger.Log("Inventory is null, FindFolderForType() lookup cannot continue", |
||
1730 | Helpers.LogLevel.Error, Client); |
||
1731 | return UUID.Zero; |
||
1732 | } |
||
1733 | |||
1734 | // Folders go in the root |
||
1735 | if (type == AssetType.Folder) |
||
1736 | return _Store.RootFolder.UUID; |
||
1737 | |||
1738 | // Loop through each top-level directory and check if PreferredType |
||
1739 | // matches the requested type |
||
1740 | List<InventoryBase> contents = _Store.GetContents(_Store.RootFolder.UUID); |
||
1741 | foreach (InventoryBase inv in contents) |
||
1742 | { |
||
1743 | if (inv is InventoryFolder) |
||
1744 | { |
||
1745 | InventoryFolder folder = inv as InventoryFolder; |
||
1746 | |||
1747 | if (folder.PreferredType == type) |
||
1748 | return folder.UUID; |
||
1749 | } |
||
1750 | } |
||
1751 | |||
1752 | // No match found, return Root Folder ID |
||
1753 | return _Store.RootFolder.UUID; |
||
1754 | } |
||
1755 | |||
1756 | /// <summary> |
||
1757 | /// Find an object in inventory using a specific path to search |
||
1758 | /// </summary> |
||
1759 | /// <param name="baseFolder">The folder to begin the search in</param> |
||
1760 | /// <param name="inventoryOwner">The object owners <seealso cref="UUID"/></param> |
||
1761 | /// <param name="path">A string path to search</param> |
||
1762 | /// <param name="timeoutMS">milliseconds to wait for a reply</param> |
||
1763 | /// <returns>Found items <seealso cref="UUID"/> or <seealso cref="UUID.Zero"/> if |
||
1764 | /// timeout occurs or item is not found</returns> |
||
1765 | public UUID FindObjectByPath(UUID baseFolder, UUID inventoryOwner, string path, int timeoutMS) |
||
1766 | { |
||
1767 | AutoResetEvent findEvent = new AutoResetEvent(false); |
||
1768 | UUID foundItem = UUID.Zero; |
||
1769 | |||
1770 | EventHandler<FindObjectByPathReplyEventArgs> callback = |
||
1771 | delegate(object sender, FindObjectByPathReplyEventArgs e) |
||
1772 | { |
||
1773 | if (e.Path == path) |
||
1774 | { |
||
1775 | foundItem = e.InventoryObjectID; |
||
1776 | findEvent.Set(); |
||
1777 | } |
||
1778 | }; |
||
1779 | |||
1780 | FindObjectByPathReply += callback; |
||
1781 | |||
1782 | RequestFindObjectByPath(baseFolder, inventoryOwner, path); |
||
1783 | findEvent.WaitOne(timeoutMS, false); |
||
1784 | |||
1785 | FindObjectByPathReply -= callback; |
||
1786 | |||
1787 | return foundItem; |
||
1788 | } |
||
1789 | |||
1790 | /// <summary> |
||
1791 | /// Find inventory items by path |
||
1792 | /// </summary> |
||
1793 | /// <param name="baseFolder">The folder to begin the search in</param> |
||
1794 | /// <param name="inventoryOwner">The object owners <seealso cref="UUID"/></param> |
||
1795 | /// <param name="path">A string path to search, folders/objects separated by a '/'</param> |
||
1796 | /// <remarks>Results are sent to the <seealso cref="InventoryManager.OnFindObjectByPath"/> event</remarks> |
||
1797 | public void RequestFindObjectByPath(UUID baseFolder, UUID inventoryOwner, string path) |
||
1798 | { |
||
1799 | if (path == null || path.Length == 0) |
||
1800 | throw new ArgumentException("Empty path is not supported"); |
||
1801 | |||
1802 | // Store this search |
||
1803 | InventorySearch search; |
||
1804 | search.Folder = baseFolder; |
||
1805 | search.Owner = inventoryOwner; |
||
1806 | search.Path = path.Split('/'); |
||
1807 | search.Level = 0; |
||
1808 | lock (_Searches) _Searches.Add(search); |
||
1809 | |||
1810 | // Start the search |
||
1811 | RequestFolderContents(baseFolder, inventoryOwner, true, true, InventorySortOrder.ByName); |
||
1812 | } |
||
1813 | |||
1814 | /// <summary> |
||
1815 | /// Search inventory Store object for an item or folder |
||
1816 | /// </summary> |
||
1817 | /// <param name="baseFolder">The folder to begin the search in</param> |
||
1818 | /// <param name="path">An array which creates a path to search</param> |
||
1819 | /// <param name="level">Number of levels below baseFolder to conduct searches</param> |
||
1820 | /// <param name="firstOnly">if True, will stop searching after first match is found</param> |
||
1821 | /// <returns>A list of inventory items found</returns> |
||
1822 | public List<InventoryBase> LocalFind(UUID baseFolder, string[] path, int level, bool firstOnly) |
||
1823 | { |
||
1824 | List<InventoryBase> objects = new List<InventoryBase>(); |
||
1825 | //List<InventoryFolder> folders = new List<InventoryFolder>(); |
||
1826 | List<InventoryBase> contents = _Store.GetContents(baseFolder); |
||
1827 | |||
1828 | foreach (InventoryBase inv in contents) |
||
1829 | { |
||
1830 | if (inv.Name.CompareTo(path[level]) == 0) |
||
1831 | { |
||
1832 | if (level == path.Length - 1) |
||
1833 | { |
||
1834 | objects.Add(inv); |
||
1835 | if (firstOnly) return objects; |
||
1836 | } |
||
1837 | else if (inv is InventoryFolder) |
||
1838 | objects.AddRange(LocalFind(inv.UUID, path, level + 1, firstOnly)); |
||
1839 | } |
||
1840 | } |
||
1841 | |||
1842 | return objects; |
||
1843 | } |
||
1844 | |||
1845 | #endregion Find |
||
1846 | |||
1847 | #region Move/Rename |
||
1848 | |||
1849 | /// <summary> |
||
1850 | /// Move an inventory item or folder to a new location |
||
1851 | /// </summary> |
||
1852 | /// <param name="item">The <seealso cref="T:InventoryBase"/> item or folder to move</param> |
||
1853 | /// <param name="newParent">The <seealso cref="T:InventoryFolder"/> to move item or folder to</param> |
||
1854 | public void Move(InventoryBase item, InventoryFolder newParent) |
||
1855 | { |
||
1856 | if (item is InventoryFolder) |
||
1857 | MoveFolder(item.UUID, newParent.UUID); |
||
1858 | else |
||
1859 | MoveItem(item.UUID, newParent.UUID); |
||
1860 | } |
||
1861 | |||
1862 | /// <summary> |
||
1863 | /// Move an inventory item or folder to a new location and change its name |
||
1864 | /// </summary> |
||
1865 | /// <param name="item">The <seealso cref="T:InventoryBase"/> item or folder to move</param> |
||
1866 | /// <param name="newParent">The <seealso cref="T:InventoryFolder"/> to move item or folder to</param> |
||
1867 | /// <param name="newName">The name to change the item or folder to</param> |
||
1868 | public void Move(InventoryBase item, InventoryFolder newParent, string newName) |
||
1869 | { |
||
1870 | if (item is InventoryFolder) |
||
1871 | MoveFolder(item.UUID, newParent.UUID, newName); |
||
1872 | else |
||
1873 | MoveItem(item.UUID, newParent.UUID, newName); |
||
1874 | } |
||
1875 | |||
1876 | /// <summary> |
||
1877 | /// Move and rename a folder |
||
1878 | /// </summary> |
||
1879 | /// <param name="folderID">The source folders <seealso cref="UUID"/></param> |
||
1880 | /// <param name="newparentID">The destination folders <seealso cref="UUID"/></param> |
||
1881 | /// <param name="newName">The name to change the folder to</param> |
||
1882 | public void MoveFolder(UUID folderID, UUID newparentID, string newName) |
||
1883 | { |
||
1884 | UpdateFolderProperties(folderID, newparentID, newName, AssetType.Unknown); |
||
1885 | } |
||
1886 | |||
1887 | /// <summary> |
||
1888 | /// Update folder properties |
||
1889 | /// </summary> |
||
1890 | /// <param name="folderID"><seealso cref="UUID"/> of the folder to update</param> |
||
1891 | /// <param name="parentID">Sets folder's parent to <seealso cref="UUID"/></param> |
||
1892 | /// <param name="name">Folder name</param> |
||
1893 | /// <param name="type">Folder type</param> |
||
1894 | public void UpdateFolderProperties(UUID folderID, UUID parentID, string name, AssetType type) |
||
1895 | { |
||
1896 | lock (Store) |
||
1897 | { |
||
1898 | if (_Store.Contains(folderID)) |
||
1899 | { |
||
1900 | InventoryFolder inv = (InventoryFolder)Store[folderID]; |
||
1901 | inv.Name = name; |
||
1902 | inv.ParentUUID = parentID; |
||
1903 | inv.PreferredType = type; |
||
1904 | _Store.UpdateNodeFor(inv); |
||
1905 | } |
||
1906 | } |
||
1907 | |||
1908 | UpdateInventoryFolderPacket invFolder = new UpdateInventoryFolderPacket(); |
||
1909 | invFolder.AgentData.AgentID = Client.Self.AgentID; |
||
1910 | invFolder.AgentData.SessionID = Client.Self.SessionID; |
||
1911 | invFolder.FolderData = new UpdateInventoryFolderPacket.FolderDataBlock[1]; |
||
1912 | invFolder.FolderData[0] = new UpdateInventoryFolderPacket.FolderDataBlock(); |
||
1913 | invFolder.FolderData[0].FolderID = folderID; |
||
1914 | invFolder.FolderData[0].ParentID = parentID; |
||
1915 | invFolder.FolderData[0].Name = Utils.StringToBytes(name); |
||
1916 | invFolder.FolderData[0].Type = (sbyte)type; |
||
1917 | |||
1918 | Client.Network.SendPacket(invFolder); |
||
1919 | } |
||
1920 | |||
1921 | /// <summary> |
||
1922 | /// Move a folder |
||
1923 | /// </summary> |
||
1924 | /// <param name="folderID">The source folders <seealso cref="UUID"/></param> |
||
1925 | /// <param name="newParentID">The destination folders <seealso cref="UUID"/></param> |
||
1926 | public void MoveFolder(UUID folderID, UUID newParentID) |
||
1927 | { |
||
1928 | lock (Store) |
||
1929 | { |
||
1930 | if (_Store.Contains(folderID)) |
||
1931 | { |
||
1932 | InventoryBase inv = Store[folderID]; |
||
1933 | inv.ParentUUID = newParentID; |
||
1934 | _Store.UpdateNodeFor(inv); |
||
1935 | } |
||
1936 | } |
||
1937 | |||
1938 | MoveInventoryFolderPacket move = new MoveInventoryFolderPacket(); |
||
1939 | move.AgentData.AgentID = Client.Self.AgentID; |
||
1940 | move.AgentData.SessionID = Client.Self.SessionID; |
||
1941 | move.AgentData.Stamp = false; //FIXME: ?? |
||
1942 | |||
1943 | move.InventoryData = new MoveInventoryFolderPacket.InventoryDataBlock[1]; |
||
1944 | move.InventoryData[0] = new MoveInventoryFolderPacket.InventoryDataBlock(); |
||
1945 | move.InventoryData[0].FolderID = folderID; |
||
1946 | move.InventoryData[0].ParentID = newParentID; |
||
1947 | |||
1948 | Client.Network.SendPacket(move); |
||
1949 | } |
||
1950 | |||
1951 | /// <summary> |
||
1952 | /// Move multiple folders, the keys in the Dictionary parameter, |
||
1953 | /// to a new parents, the value of that folder's key. |
||
1954 | /// </summary> |
||
1955 | /// <param name="foldersNewParents">A Dictionary containing the |
||
1956 | /// <seealso cref="UUID"/> of the source as the key, and the |
||
1957 | /// <seealso cref="UUID"/> of the destination as the value</param> |
||
1958 | public void MoveFolders(Dictionary<UUID, UUID> foldersNewParents) |
||
1959 | { |
||
1960 | // FIXME: Use two List<UUID> to stay consistent |
||
1961 | |||
1962 | lock (Store) |
||
1963 | { |
||
1964 | foreach (KeyValuePair<UUID, UUID> entry in foldersNewParents) |
||
1965 | { |
||
1966 | if (_Store.Contains(entry.Key)) |
||
1967 | { |
||
1968 | InventoryBase inv = _Store[entry.Key]; |
||
1969 | inv.ParentUUID = entry.Value; |
||
1970 | _Store.UpdateNodeFor(inv); |
||
1971 | } |
||
1972 | } |
||
1973 | } |
||
1974 | |||
1975 | //TODO: Test if this truly supports multiple-folder move |
||
1976 | MoveInventoryFolderPacket move = new MoveInventoryFolderPacket(); |
||
1977 | move.AgentData.AgentID = Client.Self.AgentID; |
||
1978 | move.AgentData.SessionID = Client.Self.SessionID; |
||
1979 | move.AgentData.Stamp = false; //FIXME: ?? |
||
1980 | |||
1981 | move.InventoryData = new MoveInventoryFolderPacket.InventoryDataBlock[foldersNewParents.Count]; |
||
1982 | |||
1983 | int index = 0; |
||
1984 | foreach (KeyValuePair<UUID, UUID> folder in foldersNewParents) |
||
1985 | { |
||
1986 | MoveInventoryFolderPacket.InventoryDataBlock block = new MoveInventoryFolderPacket.InventoryDataBlock(); |
||
1987 | block.FolderID = folder.Key; |
||
1988 | block.ParentID = folder.Value; |
||
1989 | move.InventoryData[index++] = block; |
||
1990 | } |
||
1991 | |||
1992 | Client.Network.SendPacket(move); |
||
1993 | } |
||
1994 | |||
1995 | |||
1996 | /// <summary> |
||
1997 | /// Move an inventory item to a new folder |
||
1998 | /// </summary> |
||
1999 | /// <param name="itemID">The <seealso cref="UUID"/> of the source item to move</param> |
||
2000 | /// <param name="folderID">The <seealso cref="UUID"/> of the destination folder</param> |
||
2001 | public void MoveItem(UUID itemID, UUID folderID) |
||
2002 | { |
||
2003 | MoveItem(itemID, folderID, String.Empty); |
||
2004 | } |
||
2005 | |||
2006 | /// <summary> |
||
2007 | /// Move and rename an inventory item |
||
2008 | /// </summary> |
||
2009 | /// <param name="itemID">The <seealso cref="UUID"/> of the source item to move</param> |
||
2010 | /// <param name="folderID">The <seealso cref="UUID"/> of the destination folder</param> |
||
2011 | /// <param name="newName">The name to change the folder to</param> |
||
2012 | public void MoveItem(UUID itemID, UUID folderID, string newName) |
||
2013 | { |
||
2014 | lock (_Store) |
||
2015 | { |
||
2016 | if (_Store.Contains(itemID)) |
||
2017 | { |
||
2018 | InventoryBase inv = _Store[itemID]; |
||
2019 | if (!string.IsNullOrEmpty(newName)) |
||
2020 | { |
||
2021 | inv.Name = newName; |
||
2022 | } |
||
2023 | inv.ParentUUID = folderID; |
||
2024 | _Store.UpdateNodeFor(inv); |
||
2025 | } |
||
2026 | } |
||
2027 | |||
2028 | MoveInventoryItemPacket move = new MoveInventoryItemPacket(); |
||
2029 | move.AgentData.AgentID = Client.Self.AgentID; |
||
2030 | move.AgentData.SessionID = Client.Self.SessionID; |
||
2031 | move.AgentData.Stamp = false; //FIXME: ?? |
||
2032 | |||
2033 | move.InventoryData = new MoveInventoryItemPacket.InventoryDataBlock[1]; |
||
2034 | move.InventoryData[0] = new MoveInventoryItemPacket.InventoryDataBlock(); |
||
2035 | move.InventoryData[0].ItemID = itemID; |
||
2036 | move.InventoryData[0].FolderID = folderID; |
||
2037 | move.InventoryData[0].NewName = Utils.StringToBytes(newName); |
||
2038 | |||
2039 | Client.Network.SendPacket(move); |
||
2040 | } |
||
2041 | |||
2042 | /// <summary> |
||
2043 | /// Move multiple inventory items to new locations |
||
2044 | /// </summary> |
||
2045 | /// <param name="itemsNewParents">A Dictionary containing the |
||
2046 | /// <seealso cref="UUID"/> of the source item as the key, and the |
||
2047 | /// <seealso cref="UUID"/> of the destination folder as the value</param> |
||
2048 | public void MoveItems(Dictionary<UUID, UUID> itemsNewParents) |
||
2049 | { |
||
2050 | lock (_Store) |
||
2051 | { |
||
2052 | foreach (KeyValuePair<UUID, UUID> entry in itemsNewParents) |
||
2053 | { |
||
2054 | if (_Store.Contains(entry.Key)) |
||
2055 | { |
||
2056 | InventoryBase inv = _Store[entry.Key]; |
||
2057 | inv.ParentUUID = entry.Value; |
||
2058 | _Store.UpdateNodeFor(inv); |
||
2059 | } |
||
2060 | } |
||
2061 | } |
||
2062 | |||
2063 | MoveInventoryItemPacket move = new MoveInventoryItemPacket(); |
||
2064 | move.AgentData.AgentID = Client.Self.AgentID; |
||
2065 | move.AgentData.SessionID = Client.Self.SessionID; |
||
2066 | move.AgentData.Stamp = false; //FIXME: ?? |
||
2067 | |||
2068 | move.InventoryData = new MoveInventoryItemPacket.InventoryDataBlock[itemsNewParents.Count]; |
||
2069 | |||
2070 | int index = 0; |
||
2071 | foreach (KeyValuePair<UUID, UUID> entry in itemsNewParents) |
||
2072 | { |
||
2073 | MoveInventoryItemPacket.InventoryDataBlock block = new MoveInventoryItemPacket.InventoryDataBlock(); |
||
2074 | block.ItemID = entry.Key; |
||
2075 | block.FolderID = entry.Value; |
||
2076 | block.NewName = Utils.EmptyBytes; |
||
2077 | move.InventoryData[index++] = block; |
||
2078 | } |
||
2079 | |||
2080 | Client.Network.SendPacket(move); |
||
2081 | } |
||
2082 | |||
2083 | #endregion Move |
||
2084 | |||
2085 | #region Remove |
||
2086 | |||
2087 | /// <summary> |
||
2088 | /// Remove descendants of a folder |
||
2089 | /// </summary> |
||
2090 | /// <param name="folder">The <seealso cref="UUID"/> of the folder</param> |
||
2091 | public void RemoveDescendants(UUID folder) |
||
2092 | { |
||
2093 | PurgeInventoryDescendentsPacket purge = new PurgeInventoryDescendentsPacket(); |
||
2094 | purge.AgentData.AgentID = Client.Self.AgentID; |
||
2095 | purge.AgentData.SessionID = Client.Self.SessionID; |
||
2096 | purge.InventoryData.FolderID = folder; |
||
2097 | Client.Network.SendPacket(purge); |
||
2098 | |||
2099 | // Update our local copy |
||
2100 | lock (_Store) |
||
2101 | { |
||
2102 | if (_Store.Contains(folder)) |
||
2103 | { |
||
2104 | List<InventoryBase> contents = _Store.GetContents(folder); |
||
2105 | foreach (InventoryBase obj in contents) |
||
2106 | { |
||
2107 | _Store.RemoveNodeFor(obj); |
||
2108 | } |
||
2109 | } |
||
2110 | } |
||
2111 | } |
||
2112 | |||
2113 | /// <summary> |
||
2114 | /// Remove a single item from inventory |
||
2115 | /// </summary> |
||
2116 | /// <param name="item">The <seealso cref="UUID"/> of the inventory item to remove</param> |
||
2117 | public void RemoveItem(UUID item) |
||
2118 | { |
||
2119 | List<UUID> items = new List<UUID>(1); |
||
2120 | items.Add(item); |
||
2121 | |||
2122 | Remove(items, null); |
||
2123 | } |
||
2124 | |||
2125 | /// <summary> |
||
2126 | /// Remove a folder from inventory |
||
2127 | /// </summary> |
||
2128 | /// <param name="folder">The <seealso cref="UUID"/> of the folder to remove</param> |
||
2129 | public void RemoveFolder(UUID folder) |
||
2130 | { |
||
2131 | List<UUID> folders = new List<UUID>(1); |
||
2132 | folders.Add(folder); |
||
2133 | |||
2134 | Remove(null, folders); |
||
2135 | } |
||
2136 | |||
2137 | /// <summary> |
||
2138 | /// Remove multiple items or folders from inventory |
||
2139 | /// </summary> |
||
2140 | /// <param name="items">A List containing the <seealso cref="UUID"/>s of items to remove</param> |
||
2141 | /// <param name="folders">A List containing the <seealso cref="UUID"/>s of the folders to remove</param> |
||
2142 | public void Remove(List<UUID> items, List<UUID> folders) |
||
2143 | { |
||
2144 | if ((items == null || items.Count == 0) && (folders == null || folders.Count == 0)) |
||
2145 | return; |
||
2146 | |||
2147 | RemoveInventoryObjectsPacket rem = new RemoveInventoryObjectsPacket(); |
||
2148 | rem.AgentData.AgentID = Client.Self.AgentID; |
||
2149 | rem.AgentData.SessionID = Client.Self.SessionID; |
||
2150 | |||
2151 | if (items == null || items.Count == 0) |
||
2152 | { |
||
2153 | // To indicate that we want no items removed: |
||
2154 | rem.ItemData = new RemoveInventoryObjectsPacket.ItemDataBlock[1]; |
||
2155 | rem.ItemData[0] = new RemoveInventoryObjectsPacket.ItemDataBlock(); |
||
2156 | rem.ItemData[0].ItemID = UUID.Zero; |
||
2157 | } |
||
2158 | else |
||
2159 | { |
||
2160 | lock (_Store) |
||
2161 | { |
||
2162 | rem.ItemData = new RemoveInventoryObjectsPacket.ItemDataBlock[items.Count]; |
||
2163 | for (int i = 0; i < items.Count; i++) |
||
2164 | { |
||
2165 | rem.ItemData[i] = new RemoveInventoryObjectsPacket.ItemDataBlock(); |
||
2166 | rem.ItemData[i].ItemID = items[i]; |
||
2167 | |||
2168 | // Update local copy |
||
2169 | if (_Store.Contains(items[i])) |
||
2170 | _Store.RemoveNodeFor(Store[items[i]]); |
||
2171 | } |
||
2172 | } |
||
2173 | } |
||
2174 | |||
2175 | if (folders == null || folders.Count == 0) |
||
2176 | { |
||
2177 | // To indicate we want no folders removed: |
||
2178 | rem.FolderData = new RemoveInventoryObjectsPacket.FolderDataBlock[1]; |
||
2179 | rem.FolderData[0] = new RemoveInventoryObjectsPacket.FolderDataBlock(); |
||
2180 | rem.FolderData[0].FolderID = UUID.Zero; |
||
2181 | } |
||
2182 | else |
||
2183 | { |
||
2184 | lock (_Store) |
||
2185 | { |
||
2186 | rem.FolderData = new RemoveInventoryObjectsPacket.FolderDataBlock[folders.Count]; |
||
2187 | for (int i = 0; i < folders.Count; i++) |
||
2188 | { |
||
2189 | rem.FolderData[i] = new RemoveInventoryObjectsPacket.FolderDataBlock(); |
||
2190 | rem.FolderData[i].FolderID = folders[i]; |
||
2191 | |||
2192 | // Update local copy |
||
2193 | if (_Store.Contains(folders[i])) |
||
2194 | _Store.RemoveNodeFor(Store[folders[i]]); |
||
2195 | } |
||
2196 | } |
||
2197 | } |
||
2198 | Client.Network.SendPacket(rem); |
||
2199 | } |
||
2200 | |||
2201 | /// <summary> |
||
2202 | /// Empty the Lost and Found folder |
||
2203 | /// </summary> |
||
2204 | public void EmptyLostAndFound() |
||
2205 | { |
||
2206 | EmptySystemFolder(AssetType.LostAndFoundFolder); |
||
2207 | } |
||
2208 | |||
2209 | /// <summary> |
||
2210 | /// Empty the Trash folder |
||
2211 | /// </summary> |
||
2212 | public void EmptyTrash() |
||
2213 | { |
||
2214 | EmptySystemFolder(AssetType.TrashFolder); |
||
2215 | } |
||
2216 | |||
2217 | private void EmptySystemFolder(AssetType folderType) |
||
2218 | { |
||
2219 | List<InventoryBase> items = _Store.GetContents(_Store.RootFolder); |
||
2220 | |||
2221 | UUID folderKey = UUID.Zero; |
||
2222 | foreach (InventoryBase item in items) |
||
2223 | { |
||
2224 | if ((item as InventoryFolder) != null) |
||
2225 | { |
||
2226 | InventoryFolder folder = item as InventoryFolder; |
||
2227 | if (folder.PreferredType == folderType) |
||
2228 | { |
||
2229 | folderKey = folder.UUID; |
||
2230 | break; |
||
2231 | } |
||
2232 | } |
||
2233 | } |
||
2234 | items = _Store.GetContents(folderKey); |
||
2235 | List<UUID> remItems = new List<UUID>(); |
||
2236 | List<UUID> remFolders = new List<UUID>(); |
||
2237 | foreach (InventoryBase item in items) |
||
2238 | { |
||
2239 | if ((item as InventoryFolder) != null) |
||
2240 | { |
||
2241 | remFolders.Add(item.UUID); |
||
2242 | } |
||
2243 | else |
||
2244 | { |
||
2245 | remItems.Add(item.UUID); |
||
2246 | } |
||
2247 | } |
||
2248 | Remove(remItems, remFolders); |
||
2249 | } |
||
2250 | #endregion Remove |
||
2251 | |||
2252 | #region Create |
||
2253 | |||
2254 | /// <summary> |
||
2255 | /// |
||
2256 | /// </summary> |
||
2257 | /// <param name="parentFolder"></param> |
||
2258 | /// <param name="name"></param> |
||
2259 | /// <param name="description"></param> |
||
2260 | /// <param name="type"></param> |
||
2261 | /// <param name="assetTransactionID">Proper use is to upload the inventory's asset first, then provide the Asset's TransactionID here.</param> |
||
2262 | /// <param name="invType"></param> |
||
2263 | /// <param name="nextOwnerMask"></param> |
||
2264 | /// <param name="callback"></param> |
||
2265 | public void RequestCreateItem(UUID parentFolder, string name, string description, AssetType type, UUID assetTransactionID, |
||
2266 | InventoryType invType, PermissionMask nextOwnerMask, ItemCreatedCallback callback) |
||
2267 | { |
||
2268 | // Even though WearableType 0 is Shape, in this context it is treated as NOT_WEARABLE |
||
2269 | RequestCreateItem(parentFolder, name, description, type, assetTransactionID, invType, (WearableType)0, nextOwnerMask, |
||
2270 | callback); |
||
2271 | } |
||
2272 | |||
2273 | /// <summary> |
||
2274 | /// |
||
2275 | /// </summary> |
||
2276 | /// <param name="parentFolder"></param> |
||
2277 | /// <param name="name"></param> |
||
2278 | /// <param name="description"></param> |
||
2279 | /// <param name="type"></param> |
||
2280 | /// <param name="assetTransactionID">Proper use is to upload the inventory's asset first, then provide the Asset's TransactionID here.</param> |
||
2281 | /// <param name="invType"></param> |
||
2282 | /// <param name="wearableType"></param> |
||
2283 | /// <param name="nextOwnerMask"></param> |
||
2284 | /// <param name="callback"></param> |
||
2285 | public void RequestCreateItem(UUID parentFolder, string name, string description, AssetType type, UUID assetTransactionID, |
||
2286 | InventoryType invType, WearableType wearableType, PermissionMask nextOwnerMask, ItemCreatedCallback callback) |
||
2287 | { |
||
2288 | CreateInventoryItemPacket create = new CreateInventoryItemPacket(); |
||
2289 | create.AgentData.AgentID = Client.Self.AgentID; |
||
2290 | create.AgentData.SessionID = Client.Self.SessionID; |
||
2291 | |||
2292 | create.InventoryBlock.CallbackID = RegisterItemCreatedCallback(callback); |
||
2293 | create.InventoryBlock.FolderID = parentFolder; |
||
2294 | create.InventoryBlock.TransactionID = assetTransactionID; |
||
2295 | create.InventoryBlock.NextOwnerMask = (uint)nextOwnerMask; |
||
2296 | create.InventoryBlock.Type = (sbyte)type; |
||
2297 | create.InventoryBlock.InvType = (sbyte)invType; |
||
2298 | create.InventoryBlock.WearableType = (byte)wearableType; |
||
2299 | create.InventoryBlock.Name = Utils.StringToBytes(name); |
||
2300 | create.InventoryBlock.Description = Utils.StringToBytes(description); |
||
2301 | |||
2302 | Client.Network.SendPacket(create); |
||
2303 | } |
||
2304 | |||
2305 | /// <summary> |
||
2306 | /// Creates a new inventory folder |
||
2307 | /// </summary> |
||
2308 | /// <param name="parentID">ID of the folder to put this folder in</param> |
||
2309 | /// <param name="name">Name of the folder to create</param> |
||
2310 | /// <returns>The UUID of the newly created folder</returns> |
||
2311 | public UUID CreateFolder(UUID parentID, string name) |
||
2312 | { |
||
2313 | return CreateFolder(parentID, name, AssetType.Unknown); |
||
2314 | } |
||
2315 | |||
2316 | /// <summary> |
||
2317 | /// Creates a new inventory folder |
||
2318 | /// </summary> |
||
2319 | /// <param name="parentID">ID of the folder to put this folder in</param> |
||
2320 | /// <param name="name">Name of the folder to create</param> |
||
2321 | /// <param name="preferredType">Sets this folder as the default folder |
||
2322 | /// for new assets of the specified type. Use <code>AssetType.Unknown</code> |
||
2323 | /// to create a normal folder, otherwise it will likely create a |
||
2324 | /// duplicate of an existing folder type</param> |
||
2325 | /// <returns>The UUID of the newly created folder</returns> |
||
2326 | /// <remarks>If you specify a preferred type of <code>AsseType.Folder</code> |
||
2327 | /// it will create a new root folder which may likely cause all sorts |
||
2328 | /// of strange problems</remarks> |
||
2329 | public UUID CreateFolder(UUID parentID, string name, AssetType preferredType) |
||
2330 | { |
||
2331 | UUID id = UUID.Random(); |
||
2332 | |||
2333 | // Assign a folder name if one is not already set |
||
2334 | if (String.IsNullOrEmpty(name)) |
||
2335 | { |
||
2336 | if (preferredType >= AssetType.Texture && preferredType <= AssetType.Gesture) |
||
2337 | { |
||
2338 | name = _NewFolderNames[(int)preferredType]; |
||
2339 | } |
||
2340 | else |
||
2341 | { |
||
2342 | name = "New Folder"; |
||
2343 | } |
||
2344 | } |
||
2345 | |||
2346 | // Create the new folder locally |
||
2347 | InventoryFolder newFolder = new InventoryFolder(id); |
||
2348 | newFolder.Version = 1; |
||
2349 | newFolder.DescendentCount = 0; |
||
2350 | newFolder.ParentUUID = parentID; |
||
2351 | newFolder.PreferredType = preferredType; |
||
2352 | newFolder.Name = name; |
||
2353 | newFolder.OwnerID = Client.Self.AgentID; |
||
2354 | |||
2355 | // Update the local store |
||
2356 | try { _Store[newFolder.UUID] = newFolder; } |
||
2357 | catch (InventoryException ie) { Logger.Log(ie.Message, Helpers.LogLevel.Warning, Client, ie); } |
||
2358 | |||
2359 | // Create the create folder packet and send it |
||
2360 | CreateInventoryFolderPacket create = new CreateInventoryFolderPacket(); |
||
2361 | create.AgentData.AgentID = Client.Self.AgentID; |
||
2362 | create.AgentData.SessionID = Client.Self.SessionID; |
||
2363 | |||
2364 | create.FolderData.FolderID = id; |
||
2365 | create.FolderData.ParentID = parentID; |
||
2366 | create.FolderData.Type = (sbyte)preferredType; |
||
2367 | create.FolderData.Name = Utils.StringToBytes(name); |
||
2368 | |||
2369 | Client.Network.SendPacket(create); |
||
2370 | |||
2371 | return id; |
||
2372 | } |
||
2373 | |||
2374 | /// <summary> |
||
2375 | /// Create an inventory item and upload asset data |
||
2376 | /// </summary> |
||
2377 | /// <param name="data">Asset data</param> |
||
2378 | /// <param name="name">Inventory item name</param> |
||
2379 | /// <param name="description">Inventory item description</param> |
||
2380 | /// <param name="assetType">Asset type</param> |
||
2381 | /// <param name="invType">Inventory type</param> |
||
2382 | /// <param name="folderID">Put newly created inventory in this folder</param> |
||
2383 | /// <param name="callback">Delegate that will receive feedback on success or failure</param> |
||
2384 | public void RequestCreateItemFromAsset(byte[] data, string name, string description, AssetType assetType, |
||
2385 | InventoryType invType, UUID folderID, ItemCreatedFromAssetCallback callback) |
||
2386 | { |
||
2387 | Permissions permissions = new Permissions(); |
||
2388 | permissions.EveryoneMask = PermissionMask.None; |
||
2389 | permissions.GroupMask = PermissionMask.None; |
||
2390 | permissions.NextOwnerMask = PermissionMask.All; |
||
2391 | |||
2392 | RequestCreateItemFromAsset(data, name, description, assetType, invType, folderID, permissions, callback); |
||
2393 | } |
||
2394 | |||
2395 | /// <summary> |
||
2396 | /// Create an inventory item and upload asset data |
||
2397 | /// </summary> |
||
2398 | /// <param name="data">Asset data</param> |
||
2399 | /// <param name="name">Inventory item name</param> |
||
2400 | /// <param name="description">Inventory item description</param> |
||
2401 | /// <param name="assetType">Asset type</param> |
||
2402 | /// <param name="invType">Inventory type</param> |
||
2403 | /// <param name="folderID">Put newly created inventory in this folder</param> |
||
2404 | /// <param name="permissions">Permission of the newly created item |
||
2405 | /// (EveryoneMask, GroupMask, and NextOwnerMask of Permissions struct are supported)</param> |
||
2406 | /// <param name="callback">Delegate that will receive feedback on success or failure</param> |
||
2407 | public void RequestCreateItemFromAsset(byte[] data, string name, string description, AssetType assetType, |
||
2408 | InventoryType invType, UUID folderID, Permissions permissions, ItemCreatedFromAssetCallback callback) |
||
2409 | { |
||
2410 | if (Client.Network.CurrentSim == null || Client.Network.CurrentSim.Caps == null) |
||
2411 | throw new Exception("NewFileAgentInventory capability is not currently available"); |
||
2412 | |||
2413 | Uri url = Client.Network.CurrentSim.Caps.CapabilityURI("NewFileAgentInventory"); |
||
2414 | |||
2415 | if (url != null) |
||
2416 | { |
||
2417 | OSDMap query = new OSDMap(); |
||
2418 | query.Add("folder_id", OSD.FromUUID(folderID)); |
||
2419 | query.Add("asset_type", OSD.FromString(Utils.AssetTypeToString(assetType))); |
||
2420 | query.Add("inventory_type", OSD.FromString(Utils.InventoryTypeToString(invType))); |
||
2421 | query.Add("name", OSD.FromString(name)); |
||
2422 | query.Add("description", OSD.FromString(description)); |
||
2423 | query.Add("everyone_mask", OSD.FromInteger((int)permissions.EveryoneMask)); |
||
2424 | query.Add("group_mask", OSD.FromInteger((int)permissions.GroupMask)); |
||
2425 | query.Add("next_owner_mask", OSD.FromInteger((int)permissions.NextOwnerMask)); |
||
2426 | query.Add("expected_upload_cost", OSD.FromInteger(Client.Settings.UPLOAD_COST)); |
||
2427 | |||
2428 | // Make the request |
||
2429 | CapsClient request = new CapsClient(url); |
||
2430 | request.OnComplete += CreateItemFromAssetResponse; |
||
2431 | request.UserData = new object[] { callback, data, Client.Settings.CAPS_TIMEOUT, query }; |
||
2432 | |||
2433 | request.BeginGetResponse(query, OSDFormat.Xml, Client.Settings.CAPS_TIMEOUT); |
||
2434 | } |
||
2435 | else |
||
2436 | { |
||
2437 | throw new Exception("NewFileAgentInventory capability is not currently available"); |
||
2438 | } |
||
2439 | } |
||
2440 | |||
2441 | /// <summary> |
||
2442 | /// Creates inventory link to another inventory item or folder |
||
2443 | /// </summary> |
||
2444 | /// <param name="folderID">Put newly created link in folder with this UUID</param> |
||
2445 | /// <param name="bse">Inventory item or folder</param> |
||
2446 | /// <param name="callback">Method to call upon creation of the link</param> |
||
2447 | public void CreateLink(UUID folderID, InventoryBase bse, ItemCreatedCallback callback) |
||
2448 | { |
||
2449 | if (bse is InventoryFolder) |
||
2450 | { |
||
2451 | InventoryFolder folder = (InventoryFolder)bse; |
||
2452 | CreateLink(folderID, folder, callback); |
||
2453 | } |
||
2454 | else if (bse is InventoryItem) |
||
2455 | { |
||
2456 | InventoryItem item = (InventoryItem)bse; |
||
2457 | CreateLink(folderID, item.UUID, item.Name, item.Description, AssetType.Link, item.InventoryType, UUID.Random(), callback); |
||
2458 | } |
||
2459 | } |
||
2460 | |||
2461 | /// <summary> |
||
2462 | /// Creates inventory link to another inventory item |
||
2463 | /// </summary> |
||
2464 | /// <param name="folderID">Put newly created link in folder with this UUID</param> |
||
2465 | /// <param name="item">Original inventory item</param> |
||
2466 | /// <param name="callback">Method to call upon creation of the link</param> |
||
2467 | public void CreateLink(UUID folderID, InventoryItem item, ItemCreatedCallback callback) |
||
2468 | { |
||
2469 | CreateLink(folderID, item.UUID, item.Name, item.Description, AssetType.Link, item.InventoryType, UUID.Random(), callback); |
||
2470 | } |
||
2471 | |||
2472 | /// <summary> |
||
2473 | /// Creates inventory link to another inventory folder |
||
2474 | /// </summary> |
||
2475 | /// <param name="folderID">Put newly created link in folder with this UUID</param> |
||
2476 | /// <param name="folder">Original inventory folder</param> |
||
2477 | /// <param name="callback">Method to call upon creation of the link</param> |
||
2478 | public void CreateLink(UUID folderID, InventoryFolder folder, ItemCreatedCallback callback) |
||
2479 | { |
||
2480 | CreateLink(folderID, folder.UUID, folder.Name, "", AssetType.LinkFolder, InventoryType.Folder, UUID.Random(), callback); |
||
2481 | } |
||
2482 | |||
2483 | /// <summary> |
||
2484 | /// Creates inventory link to another inventory item or folder |
||
2485 | /// </summary> |
||
2486 | /// <param name="folderID">Put newly created link in folder with this UUID</param> |
||
2487 | /// <param name="itemID">Original item's UUID</param> |
||
2488 | /// <param name="name">Name</param> |
||
2489 | /// <param name="description">Description</param> |
||
2490 | /// <param name="assetType">Asset Type</param> |
||
2491 | /// <param name="invType">Inventory Type</param> |
||
2492 | /// <param name="transactionID">Transaction UUID</param> |
||
2493 | /// <param name="callback">Method to call upon creation of the link</param> |
||
2494 | public void CreateLink(UUID folderID, UUID itemID, string name, string description, AssetType assetType, InventoryType invType, UUID transactionID, ItemCreatedCallback callback) |
||
2495 | { |
||
2496 | LinkInventoryItemPacket create = new LinkInventoryItemPacket(); |
||
2497 | create.AgentData.AgentID = Client.Self.AgentID; |
||
2498 | create.AgentData.SessionID = Client.Self.SessionID; |
||
2499 | |||
2500 | create.InventoryBlock.CallbackID = RegisterItemCreatedCallback(callback); |
||
2501 | lock (_ItemInventoryTypeRequest) |
||
2502 | { |
||
2503 | _ItemInventoryTypeRequest[create.InventoryBlock.CallbackID] = invType; |
||
2504 | } |
||
2505 | create.InventoryBlock.FolderID = folderID; |
||
2506 | create.InventoryBlock.TransactionID = transactionID; |
||
2507 | create.InventoryBlock.OldItemID = itemID; |
||
2508 | create.InventoryBlock.Type = (sbyte)assetType; |
||
2509 | create.InventoryBlock.InvType = (sbyte)invType; |
||
2510 | create.InventoryBlock.Name = Utils.StringToBytes(name); |
||
2511 | create.InventoryBlock.Description = Utils.StringToBytes(description); |
||
2512 | |||
2513 | Client.Network.SendPacket(create); |
||
2514 | } |
||
2515 | |||
2516 | #endregion Create |
||
2517 | |||
2518 | #region Copy |
||
2519 | |||
2520 | /// <summary> |
||
2521 | /// |
||
2522 | /// </summary> |
||
2523 | /// <param name="item"></param> |
||
2524 | /// <param name="newParent"></param> |
||
2525 | /// <param name="newName"></param> |
||
2526 | /// <param name="callback"></param> |
||
2527 | public void RequestCopyItem(UUID item, UUID newParent, string newName, ItemCopiedCallback callback) |
||
2528 | { |
||
2529 | RequestCopyItem(item, newParent, newName, Client.Self.AgentID, callback); |
||
2530 | } |
||
2531 | |||
2532 | /// <summary> |
||
2533 | /// |
||
2534 | /// </summary> |
||
2535 | /// <param name="item"></param> |
||
2536 | /// <param name="newParent"></param> |
||
2537 | /// <param name="newName"></param> |
||
2538 | /// <param name="oldOwnerID"></param> |
||
2539 | /// <param name="callback"></param> |
||
2540 | public void RequestCopyItem(UUID item, UUID newParent, string newName, UUID oldOwnerID, |
||
2541 | ItemCopiedCallback callback) |
||
2542 | { |
||
2543 | List<UUID> items = new List<UUID>(1); |
||
2544 | items.Add(item); |
||
2545 | |||
2546 | List<UUID> folders = new List<UUID>(1); |
||
2547 | folders.Add(newParent); |
||
2548 | |||
2549 | List<string> names = new List<string>(1); |
||
2550 | names.Add(newName); |
||
2551 | |||
2552 | RequestCopyItems(items, folders, names, oldOwnerID, callback); |
||
2553 | } |
||
2554 | |||
2555 | /// <summary> |
||
2556 | /// |
||
2557 | /// </summary> |
||
2558 | /// <param name="items"></param> |
||
2559 | /// <param name="targetFolders"></param> |
||
2560 | /// <param name="newNames"></param> |
||
2561 | /// <param name="oldOwnerID"></param> |
||
2562 | /// <param name="callback"></param> |
||
2563 | public void RequestCopyItems(List<UUID> items, List<UUID> targetFolders, List<string> newNames, |
||
2564 | UUID oldOwnerID, ItemCopiedCallback callback) |
||
2565 | { |
||
2566 | if (items.Count != targetFolders.Count || (newNames != null && items.Count != newNames.Count)) |
||
2567 | throw new ArgumentException("All list arguments must have an equal number of entries"); |
||
2568 | |||
2569 | uint callbackID = RegisterItemsCopiedCallback(callback); |
||
2570 | |||
2571 | CopyInventoryItemPacket copy = new CopyInventoryItemPacket(); |
||
2572 | copy.AgentData.AgentID = Client.Self.AgentID; |
||
2573 | copy.AgentData.SessionID = Client.Self.SessionID; |
||
2574 | |||
2575 | copy.InventoryData = new CopyInventoryItemPacket.InventoryDataBlock[items.Count]; |
||
2576 | for (int i = 0; i < items.Count; ++i) |
||
2577 | { |
||
2578 | copy.InventoryData[i] = new CopyInventoryItemPacket.InventoryDataBlock(); |
||
2579 | copy.InventoryData[i].CallbackID = callbackID; |
||
2580 | copy.InventoryData[i].NewFolderID = targetFolders[i]; |
||
2581 | copy.InventoryData[i].OldAgentID = oldOwnerID; |
||
2582 | copy.InventoryData[i].OldItemID = items[i]; |
||
2583 | |||
2584 | if (newNames != null && !String.IsNullOrEmpty(newNames[i])) |
||
2585 | copy.InventoryData[i].NewName = Utils.StringToBytes(newNames[i]); |
||
2586 | else |
||
2587 | copy.InventoryData[i].NewName = Utils.EmptyBytes; |
||
2588 | } |
||
2589 | |||
2590 | Client.Network.SendPacket(copy); |
||
2591 | } |
||
2592 | |||
2593 | /// <summary> |
||
2594 | /// Request a copy of an asset embedded within a notecard |
||
2595 | /// </summary> |
||
2596 | /// <param name="objectID">Usually UUID.Zero for copying an asset from a notecard</param> |
||
2597 | /// <param name="notecardID">UUID of the notecard to request an asset from</param> |
||
2598 | /// <param name="folderID">Target folder for asset to go to in your inventory</param> |
||
2599 | /// <param name="itemID">UUID of the embedded asset</param> |
||
2600 | /// <param name="callback">callback to run when item is copied to inventory</param> |
||
2601 | public void RequestCopyItemFromNotecard(UUID objectID, UUID notecardID, UUID folderID, UUID itemID, ItemCopiedCallback callback) |
||
2602 | { |
||
2603 | _ItemCopiedCallbacks[0] = callback; //Notecards always use callback ID 0 |
||
2604 | |||
2605 | Uri url = Client.Network.CurrentSim.Caps.CapabilityURI("CopyInventoryFromNotecard"); |
||
2606 | |||
2607 | if (url != null) |
||
2608 | { |
||
2609 | CopyInventoryFromNotecardMessage message = new CopyInventoryFromNotecardMessage(); |
||
2610 | message.CallbackID = 0; |
||
2611 | message.FolderID = folderID; |
||
2612 | message.ItemID = itemID; |
||
2613 | message.NotecardID = notecardID; |
||
2614 | message.ObjectID = objectID; |
||
2615 | |||
2616 | CapsClient request = new CapsClient(url); |
||
2617 | request.BeginGetResponse(message.Serialize(), OSDFormat.Xml, Client.Settings.CAPS_TIMEOUT); |
||
2618 | } |
||
2619 | else |
||
2620 | { |
||
2621 | CopyInventoryFromNotecardPacket copy = new CopyInventoryFromNotecardPacket(); |
||
2622 | copy.AgentData.AgentID = Client.Self.AgentID; |
||
2623 | copy.AgentData.SessionID = Client.Self.SessionID; |
||
2624 | |||
2625 | copy.NotecardData.ObjectID = objectID; |
||
2626 | copy.NotecardData.NotecardItemID = notecardID; |
||
2627 | |||
2628 | copy.InventoryData = new CopyInventoryFromNotecardPacket.InventoryDataBlock[1]; |
||
2629 | copy.InventoryData[0] = new CopyInventoryFromNotecardPacket.InventoryDataBlock(); |
||
2630 | copy.InventoryData[0].FolderID = folderID; |
||
2631 | copy.InventoryData[0].ItemID = itemID; |
||
2632 | |||
2633 | Client.Network.SendPacket(copy); |
||
2634 | } |
||
2635 | } |
||
2636 | |||
2637 | #endregion Copy |
||
2638 | |||
2639 | #region Update |
||
2640 | |||
2641 | /// <summary> |
||
2642 | /// |
||
2643 | /// </summary> |
||
2644 | /// <param name="item"></param> |
||
2645 | public void RequestUpdateItem(InventoryItem item) |
||
2646 | { |
||
2647 | List<InventoryItem> items = new List<InventoryItem>(1); |
||
2648 | items.Add(item); |
||
2649 | |||
2650 | RequestUpdateItems(items, UUID.Random()); |
||
2651 | } |
||
2652 | |||
2653 | /// <summary> |
||
2654 | /// |
||
2655 | /// </summary> |
||
2656 | /// <param name="items"></param> |
||
2657 | public void RequestUpdateItems(List<InventoryItem> items) |
||
2658 | { |
||
2659 | RequestUpdateItems(items, UUID.Random()); |
||
2660 | } |
||
2661 | |||
2662 | /// <summary> |
||
2663 | /// |
||
2664 | /// </summary> |
||
2665 | /// <param name="items"></param> |
||
2666 | /// <param name="transactionID"></param> |
||
2667 | public void RequestUpdateItems(List<InventoryItem> items, UUID transactionID) |
||
2668 | { |
||
2669 | UpdateInventoryItemPacket update = new UpdateInventoryItemPacket(); |
||
2670 | update.AgentData.AgentID = Client.Self.AgentID; |
||
2671 | update.AgentData.SessionID = Client.Self.SessionID; |
||
2672 | update.AgentData.TransactionID = transactionID; |
||
2673 | |||
2674 | update.InventoryData = new UpdateInventoryItemPacket.InventoryDataBlock[items.Count]; |
||
2675 | for (int i = 0; i < items.Count; i++) |
||
2676 | { |
||
2677 | InventoryItem item = items[i]; |
||
2678 | |||
2679 | UpdateInventoryItemPacket.InventoryDataBlock block = new UpdateInventoryItemPacket.InventoryDataBlock(); |
||
2680 | block.BaseMask = (uint)item.Permissions.BaseMask; |
||
2681 | block.CRC = ItemCRC(item); |
||
2682 | block.CreationDate = (int)Utils.DateTimeToUnixTime(item.CreationDate); |
||
2683 | block.CreatorID = item.CreatorID; |
||
2684 | block.Description = Utils.StringToBytes(item.Description); |
||
2685 | block.EveryoneMask = (uint)item.Permissions.EveryoneMask; |
||
2686 | block.Flags = (uint)item.Flags; |
||
2687 | block.FolderID = item.ParentUUID; |
||
2688 | block.GroupID = item.GroupID; |
||
2689 | block.GroupMask = (uint)item.Permissions.GroupMask; |
||
2690 | block.GroupOwned = item.GroupOwned; |
||
2691 | block.InvType = (sbyte)item.InventoryType; |
||
2692 | block.ItemID = item.UUID; |
||
2693 | block.Name = Utils.StringToBytes(item.Name); |
||
2694 | block.NextOwnerMask = (uint)item.Permissions.NextOwnerMask; |
||
2695 | block.OwnerID = item.OwnerID; |
||
2696 | block.OwnerMask = (uint)item.Permissions.OwnerMask; |
||
2697 | block.SalePrice = item.SalePrice; |
||
2698 | block.SaleType = (byte)item.SaleType; |
||
2699 | block.TransactionID = item.TransactionID; |
||
2700 | block.Type = (sbyte)item.AssetType; |
||
2701 | |||
2702 | update.InventoryData[i] = block; |
||
2703 | } |
||
2704 | |||
2705 | Client.Network.SendPacket(update); |
||
2706 | } |
||
2707 | |||
2708 | /// <summary> |
||
2709 | /// |
||
2710 | /// </summary> |
||
2711 | /// <param name="data"></param> |
||
2712 | /// <param name="notecardID"></param> |
||
2713 | /// <param name="callback"></param> |
||
2714 | public void RequestUploadNotecardAsset(byte[] data, UUID notecardID, InventoryUploadedAssetCallback callback) |
||
2715 | { |
||
2716 | if (Client.Network.CurrentSim == null || Client.Network.CurrentSim.Caps == null) |
||
2717 | throw new Exception("UpdateNotecardAgentInventory capability is not currently available"); |
||
2718 | |||
2719 | Uri url = Client.Network.CurrentSim.Caps.CapabilityURI("UpdateNotecardAgentInventory"); |
||
2720 | |||
2721 | if (url != null) |
||
2722 | { |
||
2723 | OSDMap query = new OSDMap(); |
||
2724 | query.Add("item_id", OSD.FromUUID(notecardID)); |
||
2725 | |||
2726 | // Make the request |
||
2727 | CapsClient request = new CapsClient(url); |
||
2728 | request.OnComplete += UploadInventoryAssetResponse; |
||
2729 | request.UserData = new object[] { new KeyValuePair<InventoryUploadedAssetCallback, byte[]>(callback, data), notecardID }; |
||
2730 | request.BeginGetResponse(query, OSDFormat.Xml, Client.Settings.CAPS_TIMEOUT); |
||
2731 | } |
||
2732 | else |
||
2733 | { |
||
2734 | throw new Exception("UpdateNotecardAgentInventory capability is not currently available"); |
||
2735 | } |
||
2736 | } |
||
2737 | |||
2738 | /// <summary> |
||
2739 | /// Save changes to notecard embedded in object contents |
||
2740 | /// </summary> |
||
2741 | /// <param name="data">Encoded notecard asset data</param> |
||
2742 | /// <param name="notecardID">Notecard UUID</param> |
||
2743 | /// <param name="taskID">Object's UUID</param> |
||
2744 | /// <param name="callback">Called upon finish of the upload with status information</param> |
||
2745 | public void RequestUpdateNotecardTask(byte[] data, UUID notecardID, UUID taskID, InventoryUploadedAssetCallback callback) |
||
2746 | { |
||
2747 | if (Client.Network.CurrentSim == null || Client.Network.CurrentSim.Caps == null) |
||
2748 | throw new Exception("UpdateNotecardTaskInventory capability is not currently available"); |
||
2749 | |||
2750 | Uri url = Client.Network.CurrentSim.Caps.CapabilityURI("UpdateNotecardTaskInventory"); |
||
2751 | |||
2752 | if (url != null) |
||
2753 | { |
||
2754 | OSDMap query = new OSDMap(); |
||
2755 | query.Add("item_id", OSD.FromUUID(notecardID)); |
||
2756 | query.Add("task_id", OSD.FromUUID(taskID)); |
||
2757 | |||
2758 | // Make the request |
||
2759 | CapsClient request = new CapsClient(url); |
||
2760 | request.OnComplete += UploadInventoryAssetResponse; |
||
2761 | request.UserData = new object[] { new KeyValuePair<InventoryUploadedAssetCallback, byte[]>(callback, data), notecardID }; |
||
2762 | request.BeginGetResponse(query, OSDFormat.Xml, Client.Settings.CAPS_TIMEOUT); |
||
2763 | } |
||
2764 | else |
||
2765 | { |
||
2766 | throw new Exception("UpdateNotecardTaskInventory capability is not currently available"); |
||
2767 | } |
||
2768 | } |
||
2769 | |||
2770 | /// <summary> |
||
2771 | /// Upload new gesture asset for an inventory gesture item |
||
2772 | /// </summary> |
||
2773 | /// <param name="data">Encoded gesture asset</param> |
||
2774 | /// <param name="gestureID">Gesture inventory UUID</param> |
||
2775 | /// <param name="callback">Callback whick will be called when upload is complete</param> |
||
2776 | public void RequestUploadGestureAsset(byte[] data, UUID gestureID, InventoryUploadedAssetCallback callback) |
||
2777 | { |
||
2778 | if (Client.Network.CurrentSim == null || Client.Network.CurrentSim.Caps == null) |
||
2779 | throw new Exception("UpdateGestureAgentInventory capability is not currently available"); |
||
2780 | |||
2781 | Uri url = Client.Network.CurrentSim.Caps.CapabilityURI("UpdateGestureAgentInventory"); |
||
2782 | |||
2783 | if (url != null) |
||
2784 | { |
||
2785 | OSDMap query = new OSDMap(); |
||
2786 | query.Add("item_id", OSD.FromUUID(gestureID)); |
||
2787 | |||
2788 | // Make the request |
||
2789 | CapsClient request = new CapsClient(url); |
||
2790 | request.OnComplete += UploadInventoryAssetResponse; |
||
2791 | request.UserData = new object[] { new KeyValuePair<InventoryUploadedAssetCallback, byte[]>(callback, data), gestureID }; |
||
2792 | request.BeginGetResponse(query, OSDFormat.Xml, Client.Settings.CAPS_TIMEOUT); |
||
2793 | } |
||
2794 | else |
||
2795 | { |
||
2796 | throw new Exception("UpdateGestureAgentInventory capability is not currently available"); |
||
2797 | } |
||
2798 | } |
||
2799 | |||
2800 | /// <summary> |
||
2801 | /// Update an existing script in an agents Inventory |
||
2802 | /// </summary> |
||
2803 | /// <param name="data">A byte[] array containing the encoded scripts contents</param> |
||
2804 | /// <param name="itemID">the itemID of the script</param> |
||
2805 | /// <param name="mono">if true, sets the script content to run on the mono interpreter</param> |
||
2806 | /// <param name="callback"></param> |
||
2807 | public void RequestUpdateScriptAgentInventory(byte[] data, UUID itemID, bool mono, ScriptUpdatedCallback callback) |
||
2808 | { |
||
2809 | Uri url = Client.Network.CurrentSim.Caps.CapabilityURI("UpdateScriptAgent"); |
||
2810 | |||
2811 | if (url != null) |
||
2812 | { |
||
2813 | UpdateScriptAgentRequestMessage msg = new UpdateScriptAgentRequestMessage(); |
||
2814 | msg.ItemID = itemID; |
||
2815 | msg.Target = mono ? "mono" : "lsl2"; |
||
2816 | |||
2817 | CapsClient request = new CapsClient(url); |
||
2818 | request.OnComplete += new CapsClient.CompleteCallback(UpdateScriptAgentInventoryResponse); |
||
2819 | request.UserData = new object[2] { new KeyValuePair<ScriptUpdatedCallback, byte[]>(callback, data), itemID }; |
||
2820 | request.BeginGetResponse(msg.Serialize(), OSDFormat.Xml, Client.Settings.CAPS_TIMEOUT); |
||
2821 | } |
||
2822 | else |
||
2823 | { |
||
2824 | throw new Exception("UpdateScriptAgent capability is not currently available"); |
||
2825 | } |
||
2826 | } |
||
2827 | |||
2828 | /// <summary> |
||
2829 | /// Update an existing script in an task Inventory |
||
2830 | /// </summary> |
||
2831 | /// <param name="data">A byte[] array containing the encoded scripts contents</param> |
||
2832 | /// <param name="itemID">the itemID of the script</param> |
||
2833 | /// <param name="taskID">UUID of the prim containting the script</param> |
||
2834 | /// <param name="mono">if true, sets the script content to run on the mono interpreter</param> |
||
2835 | /// <param name="running">if true, sets the script to running</param> |
||
2836 | /// <param name="callback"></param> |
||
2837 | public void RequestUpdateScriptTask(byte[] data, UUID itemID, UUID taskID, bool mono, bool running, ScriptUpdatedCallback callback) |
||
2838 | { |
||
2839 | Uri url = Client.Network.CurrentSim.Caps.CapabilityURI("UpdateScriptTask"); |
||
2840 | |||
2841 | if (url != null) |
||
2842 | { |
||
2843 | UpdateScriptTaskUpdateMessage msg = new UpdateScriptTaskUpdateMessage(); |
||
2844 | msg.ItemID = itemID; |
||
2845 | msg.TaskID = taskID; |
||
2846 | msg.ScriptRunning = running; |
||
2847 | msg.Target = mono ? "mono" : "lsl2"; |
||
2848 | |||
2849 | CapsClient request = new CapsClient(url); |
||
2850 | request.OnComplete += new CapsClient.CompleteCallback(UpdateScriptAgentInventoryResponse); |
||
2851 | request.UserData = new object[2] { new KeyValuePair<ScriptUpdatedCallback, byte[]>(callback, data), itemID }; |
||
2852 | request.BeginGetResponse(msg.Serialize(), OSDFormat.Xml, Client.Settings.CAPS_TIMEOUT); |
||
2853 | } |
||
2854 | else |
||
2855 | { |
||
2856 | throw new Exception("UpdateScriptTask capability is not currently available"); |
||
2857 | } |
||
2858 | } |
||
2859 | #endregion Update |
||
2860 | |||
2861 | #region Rez/Give |
||
2862 | |||
2863 | /// <summary> |
||
2864 | /// Rez an object from inventory |
||
2865 | /// </summary> |
||
2866 | /// <param name="simulator">Simulator to place object in</param> |
||
2867 | /// <param name="rotation">Rotation of the object when rezzed</param> |
||
2868 | /// <param name="position">Vector of where to place object</param> |
||
2869 | /// <param name="item">InventoryItem object containing item details</param> |
||
2870 | public UUID RequestRezFromInventory(Simulator simulator, Quaternion rotation, Vector3 position, |
||
2871 | InventoryItem item) |
||
2872 | { |
||
2873 | return RequestRezFromInventory(simulator, rotation, position, item, Client.Self.ActiveGroup, |
||
2874 | UUID.Random(), true); |
||
2875 | } |
||
2876 | |||
2877 | /// <summary> |
||
2878 | /// Rez an object from inventory |
||
2879 | /// </summary> |
||
2880 | /// <param name="simulator">Simulator to place object in</param> |
||
2881 | /// <param name="rotation">Rotation of the object when rezzed</param> |
||
2882 | /// <param name="position">Vector of where to place object</param> |
||
2883 | /// <param name="item">InventoryItem object containing item details</param> |
||
2884 | /// <param name="groupOwner">UUID of group to own the object</param> |
||
2885 | public UUID RequestRezFromInventory(Simulator simulator, Quaternion rotation, Vector3 position, |
||
2886 | InventoryItem item, UUID groupOwner) |
||
2887 | { |
||
2888 | return RequestRezFromInventory(simulator, rotation, position, item, groupOwner, UUID.Random(), true); |
||
2889 | } |
||
2890 | |||
2891 | /// <summary> |
||
2892 | /// Rez an object from inventory |
||
2893 | /// </summary> |
||
2894 | /// <param name="simulator">Simulator to place object in</param> |
||
2895 | /// <param name="rotation">Rotation of the object when rezzed</param> |
||
2896 | /// <param name="position">Vector of where to place object</param> |
||
2897 | /// <param name="item">InventoryItem object containing item details</param> |
||
2898 | /// <param name="groupOwner">UUID of group to own the object</param> |
||
2899 | /// <param name="queryID">User defined queryID to correlate replies</param> |
||
2900 | /// <param name="rezSelected">If set to true, the CreateSelected flag |
||
2901 | /// will be set on the rezzed object</param> |
||
2902 | public UUID RequestRezFromInventory(Simulator simulator, Quaternion rotation, Vector3 position, |
||
2903 | InventoryItem item, UUID groupOwner, UUID queryID, bool rezSelected) |
||
2904 | { |
||
2905 | return RequestRezFromInventory(simulator, UUID.Zero, rotation, position, item, groupOwner, queryID, |
||
2906 | rezSelected); |
||
2907 | } |
||
2908 | |||
2909 | /// <summary> |
||
2910 | /// Rez an object from inventory |
||
2911 | /// </summary> |
||
2912 | /// <param name="simulator">Simulator to place object in</param> |
||
2913 | /// <param name="taskID">TaskID object when rezzed</param> |
||
2914 | /// <param name="rotation">Rotation of the object when rezzed</param> |
||
2915 | /// <param name="position">Vector of where to place object</param> |
||
2916 | /// <param name="item">InventoryItem object containing item details</param> |
||
2917 | /// <param name="groupOwner">UUID of group to own the object</param> |
||
2918 | /// <param name="queryID">User defined queryID to correlate replies</param> |
||
2919 | /// <param name="rezSelected">If set to true, the CreateSelected flag |
||
2920 | /// will be set on the rezzed object</param> |
||
2921 | public UUID RequestRezFromInventory(Simulator simulator, UUID taskID, Quaternion rotation, Vector3 position, |
||
2922 | InventoryItem item, UUID groupOwner, UUID queryID, bool rezSelected) |
||
2923 | { |
||
2924 | RezObjectPacket add = new RezObjectPacket(); |
||
2925 | |||
2926 | add.AgentData.AgentID = Client.Self.AgentID; |
||
2927 | add.AgentData.SessionID = Client.Self.SessionID; |
||
2928 | add.AgentData.GroupID = groupOwner; |
||
2929 | |||
2930 | add.RezData.FromTaskID = taskID; |
||
2931 | add.RezData.BypassRaycast = 1; |
||
2932 | add.RezData.RayStart = position; |
||
2933 | add.RezData.RayEnd = position; |
||
2934 | add.RezData.RayTargetID = UUID.Zero; |
||
2935 | add.RezData.RayEndIsIntersection = false; |
||
2936 | add.RezData.RezSelected = rezSelected; |
||
2937 | add.RezData.RemoveItem = false; |
||
2938 | add.RezData.ItemFlags = (uint)item.Flags; |
||
2939 | add.RezData.GroupMask = (uint)item.Permissions.GroupMask; |
||
2940 | add.RezData.EveryoneMask = (uint)item.Permissions.EveryoneMask; |
||
2941 | add.RezData.NextOwnerMask = (uint)item.Permissions.NextOwnerMask; |
||
2942 | |||
2943 | add.InventoryData.ItemID = item.UUID; |
||
2944 | add.InventoryData.FolderID = item.ParentUUID; |
||
2945 | add.InventoryData.CreatorID = item.CreatorID; |
||
2946 | add.InventoryData.OwnerID = item.OwnerID; |
||
2947 | add.InventoryData.GroupID = item.GroupID; |
||
2948 | add.InventoryData.BaseMask = (uint)item.Permissions.BaseMask; |
||
2949 | add.InventoryData.OwnerMask = (uint)item.Permissions.OwnerMask; |
||
2950 | add.InventoryData.GroupMask = (uint)item.Permissions.GroupMask; |
||
2951 | add.InventoryData.EveryoneMask = (uint)item.Permissions.EveryoneMask; |
||
2952 | add.InventoryData.NextOwnerMask = (uint)item.Permissions.NextOwnerMask; |
||
2953 | add.InventoryData.GroupOwned = item.GroupOwned; |
||
2954 | add.InventoryData.TransactionID = queryID; |
||
2955 | add.InventoryData.Type = (sbyte)item.InventoryType; |
||
2956 | add.InventoryData.InvType = (sbyte)item.InventoryType; |
||
2957 | add.InventoryData.Flags = (uint)item.Flags; |
||
2958 | add.InventoryData.SaleType = (byte)item.SaleType; |
||
2959 | add.InventoryData.SalePrice = item.SalePrice; |
||
2960 | add.InventoryData.Name = Utils.StringToBytes(item.Name); |
||
2961 | add.InventoryData.Description = Utils.StringToBytes(item.Description); |
||
2962 | add.InventoryData.CreationDate = (int)Utils.DateTimeToUnixTime(item.CreationDate); |
||
2963 | |||
2964 | Client.Network.SendPacket(add, simulator); |
||
2965 | |||
2966 | // Remove from store if the item is no copy |
||
2967 | if (Store.Items.ContainsKey(item.UUID) && Store[item.UUID] is InventoryItem) |
||
2968 | { |
||
2969 | InventoryItem invItem = (InventoryItem)Store[item.UUID]; |
||
2970 | if ((invItem.Permissions.OwnerMask & PermissionMask.Copy) == PermissionMask.None) |
||
2971 | { |
||
2972 | Store.RemoveNodeFor(invItem); |
||
2973 | } |
||
2974 | } |
||
2975 | |||
2976 | return queryID; |
||
2977 | } |
||
2978 | |||
2979 | /// <summary> |
||
2980 | /// DeRez an object from the simulator to the agents Objects folder in the agents Inventory |
||
2981 | /// </summary> |
||
2982 | /// <param name="objectLocalID">The simulator Local ID of the object</param> |
||
2983 | /// <remarks>If objectLocalID is a child primitive in a linkset, the entire linkset will be derezzed</remarks> |
||
2984 | public void RequestDeRezToInventory(uint objectLocalID) |
||
2985 | { |
||
2986 | RequestDeRezToInventory(objectLocalID, DeRezDestination.AgentInventoryTake, |
||
2987 | Client.Inventory.FindFolderForType(AssetType.Object), UUID.Random()); |
||
2988 | } |
||
2989 | |||
2990 | /// <summary> |
||
2991 | /// DeRez an object from the simulator and return to inventory |
||
2992 | /// </summary> |
||
2993 | /// <param name="objectLocalID">The simulator Local ID of the object</param> |
||
2994 | /// <param name="destType">The type of destination from the <seealso cref="DeRezDestination"/> enum</param> |
||
2995 | /// <param name="destFolder">The destination inventory folders <seealso cref="UUID"/> -or- |
||
2996 | /// if DeRezzing object to a tasks Inventory, the Tasks <seealso cref="UUID"/></param> |
||
2997 | /// <param name="transactionID">The transaction ID for this request which |
||
2998 | /// can be used to correlate this request with other packets</param> |
||
2999 | /// <remarks>If objectLocalID is a child primitive in a linkset, the entire linkset will be derezzed</remarks> |
||
3000 | public void RequestDeRezToInventory(uint objectLocalID, DeRezDestination destType, UUID destFolder, UUID transactionID) |
||
3001 | { |
||
3002 | DeRezObjectPacket take = new DeRezObjectPacket(); |
||
3003 | |||
3004 | take.AgentData.AgentID = Client.Self.AgentID; |
||
3005 | take.AgentData.SessionID = Client.Self.SessionID; |
||
3006 | take.AgentBlock = new DeRezObjectPacket.AgentBlockBlock(); |
||
3007 | take.AgentBlock.GroupID = UUID.Zero; |
||
3008 | take.AgentBlock.Destination = (byte)destType; |
||
3009 | take.AgentBlock.DestinationID = destFolder; |
||
3010 | take.AgentBlock.PacketCount = 1; |
||
3011 | take.AgentBlock.PacketNumber = 1; |
||
3012 | take.AgentBlock.TransactionID = transactionID; |
||
3013 | |||
3014 | take.ObjectData = new DeRezObjectPacket.ObjectDataBlock[1]; |
||
3015 | take.ObjectData[0] = new DeRezObjectPacket.ObjectDataBlock(); |
||
3016 | take.ObjectData[0].ObjectLocalID = objectLocalID; |
||
3017 | |||
3018 | Client.Network.SendPacket(take); |
||
3019 | } |
||
3020 | |||
3021 | /// <summary> |
||
3022 | /// Rez an item from inventory to its previous simulator location |
||
3023 | /// </summary> |
||
3024 | /// <param name="simulator"></param> |
||
3025 | /// <param name="item"></param> |
||
3026 | /// <param name="queryID"></param> |
||
3027 | /// <returns></returns> |
||
3028 | public UUID RequestRestoreRezFromInventory(Simulator simulator, InventoryItem item, UUID queryID) |
||
3029 | { |
||
3030 | RezRestoreToWorldPacket add = new RezRestoreToWorldPacket(); |
||
3031 | |||
3032 | add.AgentData.AgentID = Client.Self.AgentID; |
||
3033 | add.AgentData.SessionID = Client.Self.SessionID; |
||
3034 | |||
3035 | add.InventoryData.ItemID = item.UUID; |
||
3036 | add.InventoryData.FolderID = item.ParentUUID; |
||
3037 | add.InventoryData.CreatorID = item.CreatorID; |
||
3038 | add.InventoryData.OwnerID = item.OwnerID; |
||
3039 | add.InventoryData.GroupID = item.GroupID; |
||
3040 | add.InventoryData.BaseMask = (uint)item.Permissions.BaseMask; |
||
3041 | add.InventoryData.OwnerMask = (uint)item.Permissions.OwnerMask; |
||
3042 | add.InventoryData.GroupMask = (uint)item.Permissions.GroupMask; |
||
3043 | add.InventoryData.EveryoneMask = (uint)item.Permissions.EveryoneMask; |
||
3044 | add.InventoryData.NextOwnerMask = (uint)item.Permissions.NextOwnerMask; |
||
3045 | add.InventoryData.GroupOwned = item.GroupOwned; |
||
3046 | add.InventoryData.TransactionID = queryID; |
||
3047 | add.InventoryData.Type = (sbyte)item.InventoryType; |
||
3048 | add.InventoryData.InvType = (sbyte)item.InventoryType; |
||
3049 | add.InventoryData.Flags = (uint)item.Flags; |
||
3050 | add.InventoryData.SaleType = (byte)item.SaleType; |
||
3051 | add.InventoryData.SalePrice = item.SalePrice; |
||
3052 | add.InventoryData.Name = Utils.StringToBytes(item.Name); |
||
3053 | add.InventoryData.Description = Utils.StringToBytes(item.Description); |
||
3054 | add.InventoryData.CreationDate = (int)Utils.DateTimeToUnixTime(item.CreationDate); |
||
3055 | |||
3056 | Client.Network.SendPacket(add, simulator); |
||
3057 | |||
3058 | return queryID; |
||
3059 | } |
||
3060 | |||
3061 | /// <summary> |
||
3062 | /// Give an inventory item to another avatar |
||
3063 | /// </summary> |
||
3064 | /// <param name="itemID">The <seealso cref="UUID"/> of the item to give</param> |
||
3065 | /// <param name="itemName">The name of the item</param> |
||
3066 | /// <param name="assetType">The type of the item from the <seealso cref="AssetType"/> enum</param> |
||
3067 | /// <param name="recipient">The <seealso cref="UUID"/> of the recipient</param> |
||
3068 | /// <param name="doEffect">true to generate a beameffect during transfer</param> |
||
3069 | public void GiveItem(UUID itemID, string itemName, AssetType assetType, UUID recipient, |
||
3070 | bool doEffect) |
||
3071 | { |
||
3072 | byte[] bucket; |
||
3073 | |||
3074 | |||
3075 | bucket = new byte[17]; |
||
3076 | bucket[0] = (byte)assetType; |
||
3077 | Buffer.BlockCopy(itemID.GetBytes(), 0, bucket, 1, 16); |
||
3078 | |||
3079 | Client.Self.InstantMessage( |
||
3080 | Client.Self.Name, |
||
3081 | recipient, |
||
3082 | itemName, |
||
3083 | UUID.Random(), |
||
3084 | InstantMessageDialog.InventoryOffered, |
||
3085 | InstantMessageOnline.Online, |
||
3086 | Client.Self.SimPosition, |
||
3087 | Client.Network.CurrentSim.ID, |
||
3088 | bucket); |
||
3089 | |||
3090 | if (doEffect) |
||
3091 | { |
||
3092 | Client.Self.BeamEffect(Client.Self.AgentID, recipient, Vector3d.Zero, |
||
3093 | Client.Settings.DEFAULT_EFFECT_COLOR, 1f, UUID.Random()); |
||
3094 | } |
||
3095 | |||
3096 | // Remove from store if the item is no copy |
||
3097 | if (Store.Items.ContainsKey(itemID) && Store[itemID] is InventoryItem) |
||
3098 | { |
||
3099 | InventoryItem invItem = (InventoryItem)Store[itemID]; |
||
3100 | if ((invItem.Permissions.OwnerMask & PermissionMask.Copy) == PermissionMask.None) |
||
3101 | { |
||
3102 | Store.RemoveNodeFor(invItem); |
||
3103 | } |
||
3104 | } |
||
3105 | } |
||
3106 | |||
3107 | /// <summary> |
||
3108 | /// Give an inventory Folder with contents to another avatar |
||
3109 | /// </summary> |
||
3110 | /// <param name="folderID">The <seealso cref="UUID"/> of the Folder to give</param> |
||
3111 | /// <param name="folderName">The name of the folder</param> |
||
3112 | /// <param name="assetType">The type of the item from the <seealso cref="AssetType"/> enum</param> |
||
3113 | /// <param name="recipient">The <seealso cref="UUID"/> of the recipient</param> |
||
3114 | /// <param name="doEffect">true to generate a beameffect during transfer</param> |
||
3115 | public void GiveFolder(UUID folderID, string folderName, AssetType assetType, UUID recipient, |
||
3116 | bool doEffect) |
||
3117 | { |
||
3118 | byte[] bucket; |
||
3119 | |||
3120 | List<InventoryItem> folderContents = new List<InventoryItem>(); |
||
3121 | |||
3122 | Client.Inventory.FolderContents(folderID, Client.Self.AgentID, false, true, InventorySortOrder.ByDate, 1000 * 15).ForEach( |
||
3123 | delegate(InventoryBase ib) |
||
3124 | { |
||
3125 | folderContents.Add(Client.Inventory.FetchItem(ib.UUID, Client.Self.AgentID, 1000 * 10)); |
||
3126 | }); |
||
3127 | bucket = new byte[17 * (folderContents.Count + 1)]; |
||
3128 | |||
3129 | //Add parent folder (first item in bucket) |
||
3130 | bucket[0] = (byte)assetType; |
||
3131 | Buffer.BlockCopy(folderID.GetBytes(), 0, bucket, 1, 16); |
||
3132 | |||
3133 | //Add contents to bucket after folder |
||
3134 | for (int i = 1; i <= folderContents.Count; ++i) |
||
3135 | { |
||
3136 | bucket[i * 17] = (byte)folderContents[i - 1].AssetType; |
||
3137 | Buffer.BlockCopy(folderContents[i - 1].UUID.GetBytes(), 0, bucket, i * 17 + 1, 16); |
||
3138 | } |
||
3139 | |||
3140 | Client.Self.InstantMessage( |
||
3141 | Client.Self.Name, |
||
3142 | recipient, |
||
3143 | folderName, |
||
3144 | UUID.Random(), |
||
3145 | InstantMessageDialog.InventoryOffered, |
||
3146 | InstantMessageOnline.Online, |
||
3147 | Client.Self.SimPosition, |
||
3148 | Client.Network.CurrentSim.ID, |
||
3149 | bucket); |
||
3150 | |||
3151 | if (doEffect) |
||
3152 | { |
||
3153 | Client.Self.BeamEffect(Client.Self.AgentID, recipient, Vector3d.Zero, |
||
3154 | Client.Settings.DEFAULT_EFFECT_COLOR, 1f, UUID.Random()); |
||
3155 | } |
||
3156 | |||
3157 | // Remove from store if items were no copy |
||
3158 | for (int i = 0; i < folderContents.Count; i++) |
||
3159 | { |
||
3160 | |||
3161 | if (Store.Items.ContainsKey(folderContents[i].UUID) && Store[folderContents[i].UUID] is InventoryItem) |
||
3162 | { |
||
3163 | InventoryItem invItem = (InventoryItem)Store[folderContents[i].UUID]; |
||
3164 | if ((invItem.Permissions.OwnerMask & PermissionMask.Copy) == PermissionMask.None) |
||
3165 | { |
||
3166 | Store.RemoveNodeFor(invItem); |
||
3167 | } |
||
3168 | } |
||
3169 | |||
3170 | } |
||
3171 | } |
||
3172 | |||
3173 | #endregion Rez/Give |
||
3174 | |||
3175 | #region Task |
||
3176 | |||
3177 | /// <summary> |
||
3178 | /// Copy or move an <see cref="InventoryItem"/> from agent inventory to a task (primitive) inventory |
||
3179 | /// </summary> |
||
3180 | /// <param name="objectLocalID">The target object</param> |
||
3181 | /// <param name="item">The item to copy or move from inventory</param> |
||
3182 | /// <returns></returns> |
||
3183 | /// <remarks>For items with copy permissions a copy of the item is placed in the tasks inventory, |
||
3184 | /// for no-copy items the object is moved to the tasks inventory</remarks> |
||
3185 | // DocTODO: what does the return UUID correlate to if anything? |
||
3186 | public UUID UpdateTaskInventory(uint objectLocalID, InventoryItem item) |
||
3187 | { |
||
3188 | UUID transactionID = UUID.Random(); |
||
3189 | |||
3190 | UpdateTaskInventoryPacket update = new UpdateTaskInventoryPacket(); |
||
3191 | update.AgentData.AgentID = Client.Self.AgentID; |
||
3192 | update.AgentData.SessionID = Client.Self.SessionID; |
||
3193 | update.UpdateData.Key = 0; |
||
3194 | update.UpdateData.LocalID = objectLocalID; |
||
3195 | |||
3196 | update.InventoryData.ItemID = item.UUID; |
||
3197 | update.InventoryData.FolderID = item.ParentUUID; |
||
3198 | update.InventoryData.CreatorID = item.CreatorID; |
||
3199 | update.InventoryData.OwnerID = item.OwnerID; |
||
3200 | update.InventoryData.GroupID = item.GroupID; |
||
3201 | update.InventoryData.BaseMask = (uint)item.Permissions.BaseMask; |
||
3202 | update.InventoryData.OwnerMask = (uint)item.Permissions.OwnerMask; |
||
3203 | update.InventoryData.GroupMask = (uint)item.Permissions.GroupMask; |
||
3204 | update.InventoryData.EveryoneMask = (uint)item.Permissions.EveryoneMask; |
||
3205 | update.InventoryData.NextOwnerMask = (uint)item.Permissions.NextOwnerMask; |
||
3206 | update.InventoryData.GroupOwned = item.GroupOwned; |
||
3207 | update.InventoryData.TransactionID = transactionID; |
||
3208 | update.InventoryData.Type = (sbyte)item.AssetType; |
||
3209 | update.InventoryData.InvType = (sbyte)item.InventoryType; |
||
3210 | update.InventoryData.Flags = (uint)item.Flags; |
||
3211 | update.InventoryData.SaleType = (byte)item.SaleType; |
||
3212 | update.InventoryData.SalePrice = item.SalePrice; |
||
3213 | update.InventoryData.Name = Utils.StringToBytes(item.Name); |
||
3214 | update.InventoryData.Description = Utils.StringToBytes(item.Description); |
||
3215 | update.InventoryData.CreationDate = (int)Utils.DateTimeToUnixTime(item.CreationDate); |
||
3216 | update.InventoryData.CRC = ItemCRC(item); |
||
3217 | |||
3218 | Client.Network.SendPacket(update); |
||
3219 | |||
3220 | return transactionID; |
||
3221 | } |
||
3222 | |||
3223 | /// <summary> |
||
3224 | /// Retrieve a listing of the items contained in a task (Primitive) |
||
3225 | /// </summary> |
||
3226 | /// <param name="objectID">The tasks <seealso cref="UUID"/></param> |
||
3227 | /// <param name="objectLocalID">The tasks simulator local ID</param> |
||
3228 | /// <param name="timeoutMS">milliseconds to wait for reply from simulator</param> |
||
3229 | /// <returns>A list containing the inventory items inside the task or null |
||
3230 | /// if a timeout occurs</returns> |
||
3231 | /// <remarks>This request blocks until the response from the simulator arrives |
||
3232 | /// or timeoutMS is exceeded</remarks> |
||
3233 | public List<InventoryBase> GetTaskInventory(UUID objectID, UInt32 objectLocalID, Int32 timeoutMS) |
||
3234 | { |
||
3235 | String filename = null; |
||
3236 | AutoResetEvent taskReplyEvent = new AutoResetEvent(false); |
||
3237 | |||
3238 | EventHandler<TaskInventoryReplyEventArgs> callback = |
||
3239 | delegate(object sender, TaskInventoryReplyEventArgs e) |
||
3240 | { |
||
3241 | if (e.ItemID == objectID) |
||
3242 | { |
||
3243 | filename = e.AssetFilename; |
||
3244 | taskReplyEvent.Set(); |
||
3245 | } |
||
3246 | }; |
||
3247 | |||
3248 | TaskInventoryReply += callback; |
||
3249 | |||
3250 | RequestTaskInventory(objectLocalID); |
||
3251 | |||
3252 | if (taskReplyEvent.WaitOne(timeoutMS, false)) |
||
3253 | { |
||
3254 | TaskInventoryReply -= callback; |
||
3255 | |||
3256 | if (!String.IsNullOrEmpty(filename)) |
||
3257 | { |
||
3258 | byte[] assetData = null; |
||
3259 | ulong xferID = 0; |
||
3260 | AutoResetEvent taskDownloadEvent = new AutoResetEvent(false); |
||
3261 | |||
3262 | EventHandler<XferReceivedEventArgs> xferCallback = |
||
3263 | delegate(object sender, XferReceivedEventArgs e) |
||
3264 | { |
||
3265 | if (e.Xfer.XferID == xferID) |
||
3266 | { |
||
3267 | assetData = e.Xfer.AssetData; |
||
3268 | taskDownloadEvent.Set(); |
||
3269 | } |
||
3270 | }; |
||
3271 | |||
3272 | Client.Assets.XferReceived += xferCallback; |
||
3273 | |||
3274 | // Start the actual asset xfer |
||
3275 | xferID = Client.Assets.RequestAssetXfer(filename, true, false, UUID.Zero, AssetType.Unknown, true); |
||
3276 | |||
3277 | if (taskDownloadEvent.WaitOne(timeoutMS, false)) |
||
3278 | { |
||
3279 | Client.Assets.XferReceived -= xferCallback; |
||
3280 | |||
3281 | String taskList = Utils.BytesToString(assetData); |
||
3282 | return ParseTaskInventory(taskList); |
||
3283 | } |
||
3284 | else |
||
3285 | { |
||
3286 | Logger.Log("Timed out waiting for task inventory download for " + filename, Helpers.LogLevel.Warning, Client); |
||
3287 | Client.Assets.XferReceived -= xferCallback; |
||
3288 | return null; |
||
3289 | } |
||
3290 | } |
||
3291 | else |
||
3292 | { |
||
3293 | Logger.DebugLog("Task is empty for " + objectLocalID, Client); |
||
3294 | return new List<InventoryBase>(0); |
||
3295 | } |
||
3296 | } |
||
3297 | else |
||
3298 | { |
||
3299 | Logger.Log("Timed out waiting for task inventory reply for " + objectLocalID, Helpers.LogLevel.Warning, Client); |
||
3300 | TaskInventoryReply -= callback; |
||
3301 | return null; |
||
3302 | } |
||
3303 | } |
||
3304 | |||
3305 | /// <summary> |
||
3306 | /// Request the contents of a tasks (primitives) inventory from the |
||
3307 | /// current simulator |
||
3308 | /// </summary> |
||
3309 | /// <param name="objectLocalID">The LocalID of the object</param> |
||
3310 | /// <seealso cref="TaskInventoryReply"/> |
||
3311 | public void RequestTaskInventory(uint objectLocalID) |
||
3312 | { |
||
3313 | RequestTaskInventory(objectLocalID, Client.Network.CurrentSim); |
||
3314 | } |
||
3315 | |||
3316 | /// <summary> |
||
3317 | /// Request the contents of a tasks (primitives) inventory |
||
3318 | /// </summary> |
||
3319 | /// <param name="objectLocalID">The simulator Local ID of the object</param> |
||
3320 | /// <param name="simulator">A reference to the simulator object that contains the object</param> |
||
3321 | /// <seealso cref="TaskInventoryReply"/> |
||
3322 | public void RequestTaskInventory(uint objectLocalID, Simulator simulator) |
||
3323 | { |
||
3324 | RequestTaskInventoryPacket request = new RequestTaskInventoryPacket(); |
||
3325 | request.AgentData.AgentID = Client.Self.AgentID; |
||
3326 | request.AgentData.SessionID = Client.Self.SessionID; |
||
3327 | request.InventoryData.LocalID = objectLocalID; |
||
3328 | |||
3329 | Client.Network.SendPacket(request, simulator); |
||
3330 | } |
||
3331 | |||
3332 | /// <summary> |
||
3333 | /// Move an item from a tasks (Primitive) inventory to the specified folder in the avatars inventory |
||
3334 | /// </summary> |
||
3335 | /// <param name="objectLocalID">LocalID of the object in the simulator</param> |
||
3336 | /// <param name="taskItemID">UUID of the task item to move</param> |
||
3337 | /// <param name="inventoryFolderID">The ID of the destination folder in this agents inventory</param> |
||
3338 | /// <param name="simulator">Simulator Object</param> |
||
3339 | /// <remarks>Raises the <see cref="OnTaskItemReceived"/> event</remarks> |
||
3340 | public void MoveTaskInventory(uint objectLocalID, UUID taskItemID, UUID inventoryFolderID, Simulator simulator) |
||
3341 | { |
||
3342 | MoveTaskInventoryPacket request = new MoveTaskInventoryPacket(); |
||
3343 | request.AgentData.AgentID = Client.Self.AgentID; |
||
3344 | request.AgentData.SessionID = Client.Self.SessionID; |
||
3345 | |||
3346 | request.AgentData.FolderID = inventoryFolderID; |
||
3347 | |||
3348 | request.InventoryData.ItemID = taskItemID; |
||
3349 | request.InventoryData.LocalID = objectLocalID; |
||
3350 | |||
3351 | Client.Network.SendPacket(request, simulator); |
||
3352 | } |
||
3353 | |||
3354 | /// <summary> |
||
3355 | /// Remove an item from an objects (Prim) Inventory |
||
3356 | /// </summary> |
||
3357 | /// <param name="objectLocalID">LocalID of the object in the simulator</param> |
||
3358 | /// <param name="taskItemID">UUID of the task item to remove</param> |
||
3359 | /// <param name="simulator">Simulator Object</param> |
||
3360 | /// <remarks>You can confirm the removal by comparing the tasks inventory serial before and after the |
||
3361 | /// request with the <see cref="RequestTaskInventory"/> request combined with |
||
3362 | /// the <seealso cref="TaskInventoryReply"/> event</remarks> |
||
3363 | public void RemoveTaskInventory(uint objectLocalID, UUID taskItemID, Simulator simulator) |
||
3364 | { |
||
3365 | RemoveTaskInventoryPacket remove = new RemoveTaskInventoryPacket(); |
||
3366 | remove.AgentData.AgentID = Client.Self.AgentID; |
||
3367 | remove.AgentData.SessionID = Client.Self.SessionID; |
||
3368 | |||
3369 | remove.InventoryData.ItemID = taskItemID; |
||
3370 | remove.InventoryData.LocalID = objectLocalID; |
||
3371 | |||
3372 | Client.Network.SendPacket(remove, simulator); |
||
3373 | } |
||
3374 | |||
3375 | /// <summary> |
||
3376 | /// Copy an InventoryScript item from the Agents Inventory into a primitives task inventory |
||
3377 | /// </summary> |
||
3378 | /// <param name="objectLocalID">An unsigned integer representing a primitive being simulated</param> |
||
3379 | /// <param name="item">An <seealso cref="InventoryItem"/> which represents a script object from the agents inventory</param> |
||
3380 | /// <param name="enableScript">true to set the scripts running state to enabled</param> |
||
3381 | /// <returns>A Unique Transaction ID</returns> |
||
3382 | /// <example> |
||
3383 | /// The following example shows the basic steps necessary to copy a script from the agents inventory into a tasks inventory |
||
3384 | /// and assumes the script exists in the agents inventory. |
||
3385 | /// <code> |
||
3386 | /// uint primID = 95899503; // Fake prim ID |
||
3387 | /// UUID scriptID = UUID.Parse("92a7fe8a-e949-dd39-a8d8-1681d8673232"); // Fake Script UUID in Inventory |
||
3388 | /// |
||
3389 | /// Client.Inventory.FolderContents(Client.Inventory.FindFolderForType(AssetType.LSLText), Client.Self.AgentID, |
||
3390 | /// false, true, InventorySortOrder.ByName, 10000); |
||
3391 | /// |
||
3392 | /// Client.Inventory.RezScript(primID, (InventoryItem)Client.Inventory.Store[scriptID]); |
||
3393 | /// </code> |
||
3394 | /// </example> |
||
3395 | // DocTODO: what does the return UUID correlate to if anything? |
||
3396 | public UUID CopyScriptToTask(uint objectLocalID, InventoryItem item, bool enableScript) |
||
3397 | { |
||
3398 | UUID transactionID = UUID.Random(); |
||
3399 | |||
3400 | RezScriptPacket ScriptPacket = new RezScriptPacket(); |
||
3401 | ScriptPacket.AgentData.AgentID = Client.Self.AgentID; |
||
3402 | ScriptPacket.AgentData.SessionID = Client.Self.SessionID; |
||
3403 | |||
3404 | ScriptPacket.UpdateBlock.ObjectLocalID = objectLocalID; |
||
3405 | ScriptPacket.UpdateBlock.Enabled = enableScript; |
||
3406 | |||
3407 | ScriptPacket.InventoryBlock.ItemID = item.UUID; |
||
3408 | ScriptPacket.InventoryBlock.FolderID = item.ParentUUID; |
||
3409 | ScriptPacket.InventoryBlock.CreatorID = item.CreatorID; |
||
3410 | ScriptPacket.InventoryBlock.OwnerID = item.OwnerID; |
||
3411 | ScriptPacket.InventoryBlock.GroupID = item.GroupID; |
||
3412 | ScriptPacket.InventoryBlock.BaseMask = (uint)item.Permissions.BaseMask; |
||
3413 | ScriptPacket.InventoryBlock.OwnerMask = (uint)item.Permissions.OwnerMask; |
||
3414 | ScriptPacket.InventoryBlock.GroupMask = (uint)item.Permissions.GroupMask; |
||
3415 | ScriptPacket.InventoryBlock.EveryoneMask = (uint)item.Permissions.EveryoneMask; |
||
3416 | ScriptPacket.InventoryBlock.NextOwnerMask = (uint)item.Permissions.NextOwnerMask; |
||
3417 | ScriptPacket.InventoryBlock.GroupOwned = item.GroupOwned; |
||
3418 | ScriptPacket.InventoryBlock.TransactionID = transactionID; |
||
3419 | ScriptPacket.InventoryBlock.Type = (sbyte)item.AssetType; |
||
3420 | ScriptPacket.InventoryBlock.InvType = (sbyte)item.InventoryType; |
||
3421 | ScriptPacket.InventoryBlock.Flags = (uint)item.Flags; |
||
3422 | ScriptPacket.InventoryBlock.SaleType = (byte)item.SaleType; |
||
3423 | ScriptPacket.InventoryBlock.SalePrice = item.SalePrice; |
||
3424 | ScriptPacket.InventoryBlock.Name = Utils.StringToBytes(item.Name); |
||
3425 | ScriptPacket.InventoryBlock.Description = Utils.StringToBytes(item.Description); |
||
3426 | ScriptPacket.InventoryBlock.CreationDate = (int)Utils.DateTimeToUnixTime(item.CreationDate); |
||
3427 | ScriptPacket.InventoryBlock.CRC = ItemCRC(item); |
||
3428 | |||
3429 | Client.Network.SendPacket(ScriptPacket); |
||
3430 | |||
3431 | return transactionID; |
||
3432 | } |
||
3433 | |||
3434 | |||
3435 | /// <summary> |
||
3436 | /// Request the running status of a script contained in a task (primitive) inventory |
||
3437 | /// </summary> |
||
3438 | /// <param name="objectID">The ID of the primitive containing the script</param> |
||
3439 | /// <param name="scriptID">The ID of the script</param> |
||
3440 | /// <remarks>The <see cref="ScriptRunningReply"/> event can be used to obtain the results of the |
||
3441 | /// request</remarks> |
||
3442 | /// <seealso cref="ScriptRunningReply"/> |
||
3443 | public void RequestGetScriptRunning(UUID objectID, UUID scriptID) |
||
3444 | { |
||
3445 | GetScriptRunningPacket request = new GetScriptRunningPacket(); |
||
3446 | request.Script.ObjectID = objectID; |
||
3447 | request.Script.ItemID = scriptID; |
||
3448 | |||
3449 | Client.Network.SendPacket(request); |
||
3450 | } |
||
3451 | |||
3452 | /// <summary> |
||
3453 | /// Send a request to set the running state of a script contained in a task (primitive) inventory |
||
3454 | /// </summary> |
||
3455 | /// <param name="objectID">The ID of the primitive containing the script</param> |
||
3456 | /// <param name="scriptID">The ID of the script</param> |
||
3457 | /// <param name="running">true to set the script running, false to stop a running script</param> |
||
3458 | /// <remarks>To verify the change you can use the <see cref="RequestGetScriptRunning"/> method combined |
||
3459 | /// with the <see cref="ScriptRunningReply"/> event</remarks> |
||
3460 | public void RequestSetScriptRunning(UUID objectID, UUID scriptID, bool running) |
||
3461 | { |
||
3462 | SetScriptRunningPacket request = new SetScriptRunningPacket(); |
||
3463 | request.AgentData.AgentID = Client.Self.AgentID; |
||
3464 | request.AgentData.SessionID = Client.Self.SessionID; |
||
3465 | request.Script.Running = running; |
||
3466 | request.Script.ItemID = scriptID; |
||
3467 | request.Script.ObjectID = objectID; |
||
3468 | |||
3469 | Client.Network.SendPacket(request); |
||
3470 | } |
||
3471 | |||
3472 | #endregion Task |
||
3473 | |||
3474 | #region Helper Functions |
||
3475 | |||
3476 | private uint RegisterItemCreatedCallback(ItemCreatedCallback callback) |
||
3477 | { |
||
3478 | lock (_CallbacksLock) |
||
3479 | { |
||
3480 | if (_CallbackPos == UInt32.MaxValue) |
||
3481 | _CallbackPos = 0; |
||
3482 | |||
3483 | _CallbackPos++; |
||
3484 | |||
3485 | if (_ItemCreatedCallbacks.ContainsKey(_CallbackPos)) |
||
3486 | Logger.Log("Overwriting an existing ItemCreatedCallback", Helpers.LogLevel.Warning, Client); |
||
3487 | |||
3488 | _ItemCreatedCallbacks[_CallbackPos] = callback; |
||
3489 | |||
3490 | return _CallbackPos; |
||
3491 | } |
||
3492 | } |
||
3493 | |||
3494 | private uint RegisterItemsCopiedCallback(ItemCopiedCallback callback) |
||
3495 | { |
||
3496 | lock (_CallbacksLock) |
||
3497 | { |
||
3498 | if (_CallbackPos == UInt32.MaxValue) |
||
3499 | _CallbackPos = 0; |
||
3500 | |||
3501 | _CallbackPos++; |
||
3502 | |||
3503 | if (_ItemCopiedCallbacks.ContainsKey(_CallbackPos)) |
||
3504 | Logger.Log("Overwriting an existing ItemsCopiedCallback", Helpers.LogLevel.Warning, Client); |
||
3505 | |||
3506 | _ItemCopiedCallbacks[_CallbackPos] = callback; |
||
3507 | |||
3508 | return _CallbackPos; |
||
3509 | } |
||
3510 | } |
||
3511 | |||
3512 | /// <summary> |
||
3513 | /// Create a CRC from an InventoryItem |
||
3514 | /// </summary> |
||
3515 | /// <param name="iitem">The source InventoryItem</param> |
||
3516 | /// <returns>A uint representing the source InventoryItem as a CRC</returns> |
||
3517 | public static uint ItemCRC(InventoryItem iitem) |
||
3518 | { |
||
3519 | uint CRC = 0; |
||
3520 | |||
3521 | // IDs |
||
3522 | CRC += iitem.AssetUUID.CRC(); // AssetID |
||
3523 | CRC += iitem.ParentUUID.CRC(); // FolderID |
||
3524 | CRC += iitem.UUID.CRC(); // ItemID |
||
3525 | |||
3526 | // Permission stuff |
||
3527 | CRC += iitem.CreatorID.CRC(); // CreatorID |
||
3528 | CRC += iitem.OwnerID.CRC(); // OwnerID |
||
3529 | CRC += iitem.GroupID.CRC(); // GroupID |
||
3530 | |||
3531 | // CRC += another 4 words which always seem to be zero -- unclear if this is a UUID or what |
||
3532 | CRC += (uint)iitem.Permissions.OwnerMask; //owner_mask; // Either owner_mask or next_owner_mask may need to be |
||
3533 | CRC += (uint)iitem.Permissions.NextOwnerMask; //next_owner_mask; // switched with base_mask -- 2 values go here and in my |
||
3534 | CRC += (uint)iitem.Permissions.EveryoneMask; //everyone_mask; // study item, the three were identical. |
||
3535 | CRC += (uint)iitem.Permissions.GroupMask; //group_mask; |
||
3536 | |||
3537 | // The rest of the CRC fields |
||
3538 | CRC += (uint)iitem.Flags; // Flags |
||
3539 | CRC += (uint)iitem.InventoryType; // InvType |
||
3540 | CRC += (uint)iitem.AssetType; // Type |
||
3541 | CRC += (uint)Utils.DateTimeToUnixTime(iitem.CreationDate); // CreationDate |
||
3542 | CRC += (uint)iitem.SalePrice; // SalePrice |
||
3543 | CRC += (uint)((uint)iitem.SaleType * 0x07073096); // SaleType |
||
3544 | |||
3545 | return CRC; |
||
3546 | } |
||
3547 | |||
3548 | /// <summary> |
||
3549 | /// Reverses a cheesy XORing with a fixed UUID to convert a shadow_id to an asset_id |
||
3550 | /// </summary> |
||
3551 | /// <param name="shadowID">Obfuscated shadow_id value</param> |
||
3552 | /// <returns>Deobfuscated asset_id value</returns> |
||
3553 | public static UUID DecryptShadowID(UUID shadowID) |
||
3554 | { |
||
3555 | return shadowID ^ MAGIC_ID; |
||
3556 | } |
||
3557 | |||
3558 | /// <summary> |
||
3559 | /// Does a cheesy XORing with a fixed UUID to convert an asset_id to a shadow_id |
||
3560 | /// </summary> |
||
3561 | /// <param name="assetID">asset_id value to obfuscate</param> |
||
3562 | /// <returns>Obfuscated shadow_id value</returns> |
||
3563 | public static UUID EncryptAssetID(UUID assetID) |
||
3564 | { |
||
3565 | return assetID ^ MAGIC_ID; |
||
3566 | } |
||
3567 | |||
3568 | /// <summary> |
||
3569 | /// Wrapper for creating a new <seealso cref="InventoryItem"/> object |
||
3570 | /// </summary> |
||
3571 | /// <param name="type">The type of item from the <seealso cref="InventoryType"/> enum</param> |
||
3572 | /// <param name="id">The <seealso cref="UUID"/> of the newly created object</param> |
||
3573 | /// <returns>An <seealso cref="InventoryItem"/> object with the type and id passed</returns> |
||
3574 | public static InventoryItem CreateInventoryItem(InventoryType type, UUID id) |
||
3575 | { |
||
3576 | switch (type) |
||
3577 | { |
||
3578 | case InventoryType.Texture: return new InventoryTexture(id); |
||
3579 | case InventoryType.Sound: return new InventorySound(id); |
||
3580 | case InventoryType.CallingCard: return new InventoryCallingCard(id); |
||
3581 | case InventoryType.Landmark: return new InventoryLandmark(id); |
||
3582 | case InventoryType.Object: return new InventoryObject(id); |
||
3583 | case InventoryType.Notecard: return new InventoryNotecard(id); |
||
3584 | case InventoryType.Category: return new InventoryCategory(id); |
||
3585 | case InventoryType.LSL: return new InventoryLSL(id); |
||
3586 | case InventoryType.Snapshot: return new InventorySnapshot(id); |
||
3587 | case InventoryType.Attachment: return new InventoryAttachment(id); |
||
3588 | case InventoryType.Wearable: return new InventoryWearable(id); |
||
3589 | case InventoryType.Animation: return new InventoryAnimation(id); |
||
3590 | case InventoryType.Gesture: return new InventoryGesture(id); |
||
3591 | default: return new InventoryItem(type, id); |
||
3592 | } |
||
3593 | } |
||
3594 | |||
3595 | private InventoryItem SafeCreateInventoryItem(InventoryType InvType, UUID ItemID) |
||
3596 | { |
||
3597 | InventoryItem ret = null; |
||
3598 | |||
3599 | if (_Store.Contains(ItemID)) |
||
3600 | ret = _Store[ItemID] as InventoryItem; |
||
3601 | |||
3602 | if (ret == null) |
||
3603 | ret = CreateInventoryItem(InvType, ItemID); |
||
3604 | |||
3605 | return ret; |
||
3606 | } |
||
3607 | |||
3608 | private static bool ParseLine(string line, out string key, out string value) |
||
3609 | { |
||
3610 | // Clean up and convert tabs to spaces |
||
3611 | line = line.Trim(); |
||
3612 | line = line.Replace('\t', ' '); |
||
3613 | |||
3614 | // Shrink all whitespace down to single spaces |
||
3615 | while (line.IndexOf(" ") > 0) |
||
3616 | line = line.Replace(" ", " "); |
||
3617 | |||
3618 | if (line.Length > 2) |
||
3619 | { |
||
3620 | int sep = line.IndexOf(' '); |
||
3621 | if (sep > 0) |
||
3622 | { |
||
3623 | key = line.Substring(0, sep); |
||
3624 | value = line.Substring(sep + 1); |
||
3625 | |||
3626 | return true; |
||
3627 | } |
||
3628 | } |
||
3629 | else if (line.Length == 1) |
||
3630 | { |
||
3631 | key = line; |
||
3632 | value = String.Empty; |
||
3633 | return true; |
||
3634 | } |
||
3635 | |||
3636 | key = null; |
||
3637 | value = null; |
||
3638 | return false; |
||
3639 | } |
||
3640 | |||
3641 | /// <summary> |
||
3642 | /// Parse the results of a RequestTaskInventory() response |
||
3643 | /// </summary> |
||
3644 | /// <param name="taskData">A string which contains the data from the task reply</param> |
||
3645 | /// <returns>A List containing the items contained within the tasks inventory</returns> |
||
3646 | public static List<InventoryBase> ParseTaskInventory(string taskData) |
||
3647 | { |
||
3648 | List<InventoryBase> items = new List<InventoryBase>(); |
||
3649 | int lineNum = 0; |
||
3650 | string[] lines = taskData.Replace("\r\n", "\n").Split('\n'); |
||
3651 | |||
3652 | while (lineNum < lines.Length) |
||
3653 | { |
||
3654 | string key, value; |
||
3655 | if (ParseLine(lines[lineNum++], out key, out value)) |
||
3656 | { |
||
3657 | if (key == "inv_object") |
||
3658 | { |
||
3659 | #region inv_object |
||
3660 | |||
3661 | // In practice this appears to only be used for folders |
||
3662 | UUID itemID = UUID.Zero; |
||
3663 | UUID parentID = UUID.Zero; |
||
3664 | string name = String.Empty; |
||
3665 | AssetType assetType = AssetType.Unknown; |
||
3666 | |||
3667 | while (lineNum < lines.Length) |
||
3668 | { |
||
3669 | if (ParseLine(lines[lineNum++], out key, out value)) |
||
3670 | { |
||
3671 | if (key == "{") |
||
3672 | { |
||
3673 | continue; |
||
3674 | } |
||
3675 | else if (key == "}") |
||
3676 | { |
||
3677 | break; |
||
3678 | } |
||
3679 | else if (key == "obj_id") |
||
3680 | { |
||
3681 | UUID.TryParse(value, out itemID); |
||
3682 | } |
||
3683 | else if (key == "parent_id") |
||
3684 | { |
||
3685 | UUID.TryParse(value, out parentID); |
||
3686 | } |
||
3687 | else if (key == "type") |
||
3688 | { |
||
3689 | assetType = Utils.StringToAssetType(value); |
||
3690 | } |
||
3691 | else if (key == "name") |
||
3692 | { |
||
3693 | name = value.Substring(0, value.IndexOf('|')); |
||
3694 | } |
||
3695 | } |
||
3696 | } |
||
3697 | |||
3698 | if (assetType == AssetType.Folder) |
||
3699 | { |
||
3700 | InventoryFolder folder = new InventoryFolder(itemID); |
||
3701 | folder.Name = name; |
||
3702 | folder.ParentUUID = parentID; |
||
3703 | |||
3704 | items.Add(folder); |
||
3705 | } |
||
3706 | else |
||
3707 | { |
||
3708 | InventoryItem item = new InventoryItem(itemID); |
||
3709 | item.Name = name; |
||
3710 | item.ParentUUID = parentID; |
||
3711 | item.AssetType = assetType; |
||
3712 | |||
3713 | items.Add(item); |
||
3714 | } |
||
3715 | |||
3716 | #endregion inv_object |
||
3717 | } |
||
3718 | else if (key == "inv_item") |
||
3719 | { |
||
3720 | #region inv_item |
||
3721 | |||
3722 | // Any inventory item that links to an assetID, has permissions, etc |
||
3723 | UUID itemID = UUID.Zero; |
||
3724 | UUID assetID = UUID.Zero; |
||
3725 | UUID parentID = UUID.Zero; |
||
3726 | UUID creatorID = UUID.Zero; |
||
3727 | UUID ownerID = UUID.Zero; |
||
3728 | UUID lastOwnerID = UUID.Zero; |
||
3729 | UUID groupID = UUID.Zero; |
||
3730 | bool groupOwned = false; |
||
3731 | string name = String.Empty; |
||
3732 | string desc = String.Empty; |
||
3733 | AssetType assetType = AssetType.Unknown; |
||
3734 | InventoryType inventoryType = InventoryType.Unknown; |
||
3735 | DateTime creationDate = Utils.Epoch; |
||
3736 | uint flags = 0; |
||
3737 | Permissions perms = Permissions.NoPermissions; |
||
3738 | SaleType saleType = SaleType.Not; |
||
3739 | int salePrice = 0; |
||
3740 | |||
3741 | while (lineNum < lines.Length) |
||
3742 | { |
||
3743 | if (ParseLine(lines[lineNum++], out key, out value)) |
||
3744 | { |
||
3745 | if (key == "{") |
||
3746 | { |
||
3747 | continue; |
||
3748 | } |
||
3749 | else if (key == "}") |
||
3750 | { |
||
3751 | break; |
||
3752 | } |
||
3753 | else if (key == "item_id") |
||
3754 | { |
||
3755 | UUID.TryParse(value, out itemID); |
||
3756 | } |
||
3757 | else if (key == "parent_id") |
||
3758 | { |
||
3759 | UUID.TryParse(value, out parentID); |
||
3760 | } |
||
3761 | else if (key == "permissions") |
||
3762 | { |
||
3763 | #region permissions |
||
3764 | |||
3765 | while (lineNum < lines.Length) |
||
3766 | { |
||
3767 | if (ParseLine(lines[lineNum++], out key, out value)) |
||
3768 | { |
||
3769 | if (key == "{") |
||
3770 | { |
||
3771 | continue; |
||
3772 | } |
||
3773 | else if (key == "}") |
||
3774 | { |
||
3775 | break; |
||
3776 | } |
||
3777 | else if (key == "creator_mask") |
||
3778 | { |
||
3779 | // Deprecated |
||
3780 | uint val; |
||
3781 | if (Utils.TryParseHex(value, out val)) |
||
3782 | perms.BaseMask = (PermissionMask)val; |
||
3783 | } |
||
3784 | else if (key == "base_mask") |
||
3785 | { |
||
3786 | uint val; |
||
3787 | if (Utils.TryParseHex(value, out val)) |
||
3788 | perms.BaseMask = (PermissionMask)val; |
||
3789 | } |
||
3790 | else if (key == "owner_mask") |
||
3791 | { |
||
3792 | uint val; |
||
3793 | if (Utils.TryParseHex(value, out val)) |
||
3794 | perms.OwnerMask = (PermissionMask)val; |
||
3795 | } |
||
3796 | else if (key == "group_mask") |
||
3797 | { |
||
3798 | uint val; |
||
3799 | if (Utils.TryParseHex(value, out val)) |
||
3800 | perms.GroupMask = (PermissionMask)val; |
||
3801 | } |
||
3802 | else if (key == "everyone_mask") |
||
3803 | { |
||
3804 | uint val; |
||
3805 | if (Utils.TryParseHex(value, out val)) |
||
3806 | perms.EveryoneMask = (PermissionMask)val; |
||
3807 | } |
||
3808 | else if (key == "next_owner_mask") |
||
3809 | { |
||
3810 | uint val; |
||
3811 | if (Utils.TryParseHex(value, out val)) |
||
3812 | perms.NextOwnerMask = (PermissionMask)val; |
||
3813 | } |
||
3814 | else if (key == "creator_id") |
||
3815 | { |
||
3816 | |||
3817 | UUID.TryParse(value, out creatorID); |
||
3818 | } |
||
3819 | else if (key == "owner_id") |
||
3820 | { |
||
3821 | UUID.TryParse(value, out ownerID); |
||
3822 | } |
||
3823 | else if (key == "last_owner_id") |
||
3824 | { |
||
3825 | UUID.TryParse(value, out lastOwnerID); |
||
3826 | } |
||
3827 | else if (key == "group_id") |
||
3828 | { |
||
3829 | UUID.TryParse(value, out groupID); |
||
3830 | } |
||
3831 | else if (key == "group_owned") |
||
3832 | { |
||
3833 | uint val; |
||
3834 | if (UInt32.TryParse(value, out val)) |
||
3835 | groupOwned = (val != 0); |
||
3836 | } |
||
3837 | } |
||
3838 | } |
||
3839 | |||
3840 | #endregion permissions |
||
3841 | } |
||
3842 | else if (key == "sale_info") |
||
3843 | { |
||
3844 | #region sale_info |
||
3845 | |||
3846 | while (lineNum < lines.Length) |
||
3847 | { |
||
3848 | if (ParseLine(lines[lineNum++], out key, out value)) |
||
3849 | { |
||
3850 | if (key == "{") |
||
3851 | { |
||
3852 | continue; |
||
3853 | } |
||
3854 | else if (key == "}") |
||
3855 | { |
||
3856 | break; |
||
3857 | } |
||
3858 | else if (key == "sale_type") |
||
3859 | { |
||
3860 | saleType = Utils.StringToSaleType(value); |
||
3861 | } |
||
3862 | else if (key == "sale_price") |
||
3863 | { |
||
3864 | Int32.TryParse(value, out salePrice); |
||
3865 | } |
||
3866 | } |
||
3867 | } |
||
3868 | |||
3869 | #endregion sale_info |
||
3870 | } |
||
3871 | else if (key == "shadow_id") |
||
3872 | { |
||
3873 | UUID shadowID; |
||
3874 | if (UUID.TryParse(value, out shadowID)) |
||
3875 | assetID = DecryptShadowID(shadowID); |
||
3876 | } |
||
3877 | else if (key == "asset_id") |
||
3878 | { |
||
3879 | UUID.TryParse(value, out assetID); |
||
3880 | } |
||
3881 | else if (key == "type") |
||
3882 | { |
||
3883 | assetType = Utils.StringToAssetType(value); |
||
3884 | } |
||
3885 | else if (key == "inv_type") |
||
3886 | { |
||
3887 | inventoryType = Utils.StringToInventoryType(value); |
||
3888 | } |
||
3889 | else if (key == "flags") |
||
3890 | { |
||
3891 | UInt32.TryParse(value, out flags); |
||
3892 | } |
||
3893 | else if (key == "name") |
||
3894 | { |
||
3895 | name = value.Substring(0, value.IndexOf('|')); |
||
3896 | } |
||
3897 | else if (key == "desc") |
||
3898 | { |
||
3899 | desc = value.Substring(0, value.IndexOf('|')); |
||
3900 | } |
||
3901 | else if (key == "creation_date") |
||
3902 | { |
||
3903 | uint timestamp; |
||
3904 | if (UInt32.TryParse(value, out timestamp)) |
||
3905 | creationDate = Utils.UnixTimeToDateTime(timestamp); |
||
3906 | else |
||
3907 | Logger.Log("Failed to parse creation_date " + value, Helpers.LogLevel.Warning); |
||
3908 | } |
||
3909 | } |
||
3910 | } |
||
3911 | |||
3912 | InventoryItem item = CreateInventoryItem(inventoryType, itemID); |
||
3913 | item.AssetUUID = assetID; |
||
3914 | item.AssetType = assetType; |
||
3915 | item.CreationDate = creationDate; |
||
3916 | item.CreatorID = creatorID; |
||
3917 | item.Description = desc; |
||
3918 | item.Flags = flags; |
||
3919 | item.GroupID = groupID; |
||
3920 | item.GroupOwned = groupOwned; |
||
3921 | item.Name = name; |
||
3922 | item.OwnerID = ownerID; |
||
3923 | item.LastOwnerID = lastOwnerID; |
||
3924 | item.ParentUUID = parentID; |
||
3925 | item.Permissions = perms; |
||
3926 | item.SalePrice = salePrice; |
||
3927 | item.SaleType = saleType; |
||
3928 | |||
3929 | items.Add(item); |
||
3930 | |||
3931 | #endregion inv_item |
||
3932 | } |
||
3933 | else |
||
3934 | { |
||
3935 | Logger.Log("Unrecognized token " + key + " in: " + Environment.NewLine + taskData, |
||
3936 | Helpers.LogLevel.Error); |
||
3937 | } |
||
3938 | } |
||
3939 | } |
||
3940 | |||
3941 | return items; |
||
3942 | } |
||
3943 | |||
3944 | #endregion Helper Functions |
||
3945 | |||
3946 | #region Internal Callbacks |
||
3947 | |||
3948 | void Self_IM(object sender, InstantMessageEventArgs e) |
||
3949 | { |
||
3950 | // TODO: MainAvatar.InstantMessageDialog.GroupNotice can also be an inventory offer, should we |
||
3951 | // handle it here? |
||
3952 | |||
3953 | if (m_InventoryObjectOffered != null && |
||
3954 | (e.IM.Dialog == InstantMessageDialog.InventoryOffered |
||
3955 | || e.IM.Dialog == InstantMessageDialog.TaskInventoryOffered)) |
||
3956 | { |
||
3957 | AssetType type = AssetType.Unknown; |
||
3958 | UUID objectID = UUID.Zero; |
||
3959 | bool fromTask = false; |
||
3960 | |||
3961 | if (e.IM.Dialog == InstantMessageDialog.InventoryOffered) |
||
3962 | { |
||
3963 | if (e.IM.BinaryBucket.Length == 17) |
||
3964 | { |
||
3965 | type = (AssetType)e.IM.BinaryBucket[0]; |
||
3966 | objectID = new UUID(e.IM.BinaryBucket, 1); |
||
3967 | fromTask = false; |
||
3968 | } |
||
3969 | else |
||
3970 | { |
||
3971 | Logger.Log("Malformed inventory offer from agent", Helpers.LogLevel.Warning, Client); |
||
3972 | return; |
||
3973 | } |
||
3974 | } |
||
3975 | else if (e.IM.Dialog == InstantMessageDialog.TaskInventoryOffered) |
||
3976 | { |
||
3977 | if (e.IM.BinaryBucket.Length == 1) |
||
3978 | { |
||
3979 | type = (AssetType)e.IM.BinaryBucket[0]; |
||
3980 | fromTask = true; |
||
3981 | } |
||
3982 | else |
||
3983 | { |
||
3984 | Logger.Log("Malformed inventory offer from object", Helpers.LogLevel.Warning, Client); |
||
3985 | return; |
||
3986 | } |
||
3987 | } |
||
3988 | |||
3989 | // Find the folder where this is going to go |
||
3990 | UUID destinationFolderID = FindFolderForType(type); |
||
3991 | |||
3992 | // Fire the callback |
||
3993 | try |
||
3994 | { |
||
3995 | ImprovedInstantMessagePacket imp = new ImprovedInstantMessagePacket(); |
||
3996 | imp.AgentData.AgentID = Client.Self.AgentID; |
||
3997 | imp.AgentData.SessionID = Client.Self.SessionID; |
||
3998 | imp.MessageBlock.FromGroup = false; |
||
3999 | imp.MessageBlock.ToAgentID = e.IM.FromAgentID; |
||
4000 | imp.MessageBlock.Offline = 0; |
||
4001 | imp.MessageBlock.ID = e.IM.IMSessionID; |
||
4002 | imp.MessageBlock.Timestamp = 0; |
||
4003 | imp.MessageBlock.FromAgentName = Utils.StringToBytes(Client.Self.Name); |
||
4004 | imp.MessageBlock.Message = Utils.EmptyBytes; |
||
4005 | imp.MessageBlock.ParentEstateID = 0; |
||
4006 | imp.MessageBlock.RegionID = UUID.Zero; |
||
4007 | imp.MessageBlock.Position = Client.Self.SimPosition; |
||
4008 | |||
4009 | InventoryObjectOfferedEventArgs args = new InventoryObjectOfferedEventArgs(e.IM, type, objectID, fromTask, destinationFolderID); |
||
4010 | |||
4011 | OnInventoryObjectOffered(args); |
||
4012 | |||
4013 | if (args.Accept) |
||
4014 | { |
||
4015 | // Accept the inventory offer |
||
4016 | switch (e.IM.Dialog) |
||
4017 | { |
||
4018 | case InstantMessageDialog.InventoryOffered: |
||
4019 | imp.MessageBlock.Dialog = (byte)InstantMessageDialog.InventoryAccepted; |
||
4020 | break; |
||
4021 | case InstantMessageDialog.TaskInventoryOffered: |
||
4022 | imp.MessageBlock.Dialog = (byte)InstantMessageDialog.TaskInventoryAccepted; |
||
4023 | break; |
||
4024 | case InstantMessageDialog.GroupNotice: |
||
4025 | imp.MessageBlock.Dialog = (byte)InstantMessageDialog.GroupNoticeInventoryAccepted; |
||
4026 | break; |
||
4027 | } |
||
4028 | imp.MessageBlock.BinaryBucket = args.FolderID.GetBytes(); |
||
4029 | } |
||
4030 | else |
||
4031 | { |
||
4032 | // Decline the inventory offer |
||
4033 | switch (e.IM.Dialog) |
||
4034 | { |
||
4035 | case InstantMessageDialog.InventoryOffered: |
||
4036 | imp.MessageBlock.Dialog = (byte)InstantMessageDialog.InventoryDeclined; |
||
4037 | break; |
||
4038 | case InstantMessageDialog.TaskInventoryOffered: |
||
4039 | imp.MessageBlock.Dialog = (byte)InstantMessageDialog.TaskInventoryDeclined; |
||
4040 | break; |
||
4041 | case InstantMessageDialog.GroupNotice: |
||
4042 | imp.MessageBlock.Dialog = (byte)InstantMessageDialog.GroupNoticeInventoryDeclined; |
||
4043 | break; |
||
4044 | } |
||
4045 | |||
4046 | imp.MessageBlock.BinaryBucket = Utils.EmptyBytes; |
||
4047 | } |
||
4048 | |||
4049 | Client.Network.SendPacket(imp, e.Simulator); |
||
4050 | } |
||
4051 | catch (Exception ex) |
||
4052 | { |
||
4053 | Logger.Log(ex.Message, Helpers.LogLevel.Error, Client, ex); |
||
4054 | } |
||
4055 | } |
||
4056 | } |
||
4057 | |||
4058 | private void CreateItemFromAssetResponse(CapsClient client, OSD result, Exception error) |
||
4059 | { |
||
4060 | object[] args = (object[])client.UserData; |
||
4061 | ItemCreatedFromAssetCallback callback = (ItemCreatedFromAssetCallback)args[0]; |
||
4062 | byte[] itemData = (byte[])args[1]; |
||
4063 | int millisecondsTimeout = (int)args[2]; |
||
4064 | OSDMap request = (OSDMap)args[3]; |
||
4065 | |||
4066 | if (result == null) |
||
4067 | { |
||
4068 | try { callback(false, error.Message, UUID.Zero, UUID.Zero); } |
||
4069 | catch (Exception e) { Logger.Log(e.Message, Helpers.LogLevel.Error, Client, e); } |
||
4070 | return; |
||
4071 | } |
||
4072 | |||
4073 | if (result.Type == OSDType.Unknown) |
||
4074 | { |
||
4075 | try |
||
4076 | { |
||
4077 | callback(false, "Failed to parse asset and item UUIDs", UUID.Zero, UUID.Zero); |
||
4078 | } |
||
4079 | catch (Exception e) |
||
4080 | { |
||
4081 | Logger.Log(e.Message, Helpers.LogLevel.Error, Client, e); |
||
4082 | } |
||
4083 | } |
||
4084 | |||
4085 | OSDMap contents = (OSDMap)result; |
||
4086 | |||
4087 | string status = contents["state"].AsString().ToLower(); |
||
4088 | |||
4089 | if (status == "upload") |
||
4090 | { |
||
4091 | string uploadURL = contents["uploader"].AsString(); |
||
4092 | |||
4093 | Logger.DebugLog("CreateItemFromAsset: uploading to " + uploadURL); |
||
4094 | |||
4095 | // This makes the assumption that all uploads go to CurrentSim, to avoid |
||
4096 | // the problem of HttpRequestState not knowing anything about simulators |
||
4097 | CapsClient upload = new CapsClient(new Uri(uploadURL)); |
||
4098 | upload.OnComplete += CreateItemFromAssetResponse; |
||
4099 | upload.UserData = new object[] { callback, itemData, millisecondsTimeout, request }; |
||
4100 | upload.BeginGetResponse(itemData, "application/octet-stream", millisecondsTimeout); |
||
4101 | } |
||
4102 | else if (status == "complete") |
||
4103 | { |
||
4104 | Logger.DebugLog("CreateItemFromAsset: completed"); |
||
4105 | |||
4106 | if (contents.ContainsKey("new_inventory_item") && contents.ContainsKey("new_asset")) |
||
4107 | { |
||
4108 | // Request full update on the item in order to update the local store |
||
4109 | RequestFetchInventory(contents["new_inventory_item"].AsUUID(), Client.Self.AgentID); |
||
4110 | |||
4111 | try { callback(true, String.Empty, contents["new_inventory_item"].AsUUID(), contents["new_asset"].AsUUID()); } |
||
4112 | catch (Exception e) { Logger.Log(e.Message, Helpers.LogLevel.Error, Client, e); } |
||
4113 | } |
||
4114 | else |
||
4115 | { |
||
4116 | try { callback(false, "Failed to parse asset and item UUIDs", UUID.Zero, UUID.Zero); } |
||
4117 | catch (Exception e) { Logger.Log(e.Message, Helpers.LogLevel.Error, Client, e); } |
||
4118 | } |
||
4119 | } |
||
4120 | else |
||
4121 | { |
||
4122 | // Failure |
||
4123 | try { callback(false, status, UUID.Zero, UUID.Zero); } |
||
4124 | catch (Exception e) { Logger.Log(e.Message, Helpers.LogLevel.Error, Client, e); } |
||
4125 | } |
||
4126 | } |
||
4127 | |||
4128 | |||
4129 | private void Network_OnLoginResponse(bool loginSuccess, bool redirect, string message, string reason, LoginResponseData replyData) |
||
4130 | { |
||
4131 | if (loginSuccess) |
||
4132 | { |
||
4133 | // Initialize the store here so we know who owns it: |
||
4134 | _Store = new Inventory(Client, this, Client.Self.AgentID); |
||
4135 | Logger.DebugLog("Setting InventoryRoot to " + replyData.InventoryRoot.ToString(), Client); |
||
4136 | InventoryFolder rootFolder = new InventoryFolder(replyData.InventoryRoot); |
||
4137 | rootFolder.Name = String.Empty; |
||
4138 | rootFolder.ParentUUID = UUID.Zero; |
||
4139 | _Store.RootFolder = rootFolder; |
||
4140 | |||
4141 | for (int i = 0; i < replyData.InventorySkeleton.Length; i++) |
||
4142 | _Store.UpdateNodeFor(replyData.InventorySkeleton[i]); |
||
4143 | |||
4144 | InventoryFolder libraryRootFolder = new InventoryFolder(replyData.LibraryRoot); |
||
4145 | libraryRootFolder.Name = String.Empty; |
||
4146 | libraryRootFolder.ParentUUID = UUID.Zero; |
||
4147 | _Store.LibraryFolder = libraryRootFolder; |
||
4148 | |||
4149 | for (int i = 0; i < replyData.LibrarySkeleton.Length; i++) |
||
4150 | _Store.UpdateNodeFor(replyData.LibrarySkeleton[i]); |
||
4151 | } |
||
4152 | } |
||
4153 | |||
4154 | private void UploadInventoryAssetResponse(CapsClient client, OSD result, Exception error) |
||
4155 | { |
||
4156 | OSDMap contents = result as OSDMap; |
||
4157 | KeyValuePair<InventoryUploadedAssetCallback, byte[]> kvp = (KeyValuePair<InventoryUploadedAssetCallback, byte[]>)(((object[])client.UserData)[0]); |
||
4158 | InventoryUploadedAssetCallback callback = kvp.Key; |
||
4159 | byte[] itemData = (byte[])kvp.Value; |
||
4160 | |||
4161 | if (error == null && contents != null) |
||
4162 | { |
||
4163 | string status = contents["state"].AsString(); |
||
4164 | |||
4165 | if (status == "upload") |
||
4166 | { |
||
4167 | Uri uploadURL = contents["uploader"].AsUri(); |
||
4168 | |||
4169 | if (uploadURL != null) |
||
4170 | { |
||
4171 | // This makes the assumption that all uploads go to CurrentSim, to avoid |
||
4172 | // the problem of HttpRequestState not knowing anything about simulators |
||
4173 | CapsClient upload = new CapsClient(uploadURL); |
||
4174 | upload.OnComplete += UploadInventoryAssetResponse; |
||
4175 | upload.UserData = new object[2] { kvp, (UUID)(((object[])client.UserData)[1]) }; |
||
4176 | upload.BeginGetResponse(itemData, "application/octet-stream", Client.Settings.CAPS_TIMEOUT); |
||
4177 | } |
||
4178 | else |
||
4179 | { |
||
4180 | try { callback(false, "Missing uploader URL", UUID.Zero, UUID.Zero); } |
||
4181 | catch (Exception e) { Logger.Log(e.Message, Helpers.LogLevel.Error, Client, e); } |
||
4182 | } |
||
4183 | } |
||
4184 | else if (status == "complete") |
||
4185 | { |
||
4186 | if (contents.ContainsKey("new_asset")) |
||
4187 | { |
||
4188 | // Request full item update so we keep store in sync |
||
4189 | RequestFetchInventory((UUID)(((object[])client.UserData)[1]), contents["new_asset"].AsUUID()); |
||
4190 | |||
4191 | try { callback(true, String.Empty, (UUID)(((object[])client.UserData)[1]), contents["new_asset"].AsUUID()); } |
||
4192 | catch (Exception e) { Logger.Log(e.Message, Helpers.LogLevel.Error, Client, e); } |
||
4193 | } |
||
4194 | else |
||
4195 | { |
||
4196 | try { callback(false, "Failed to parse asset and item UUIDs", UUID.Zero, UUID.Zero); } |
||
4197 | catch (Exception e) { Logger.Log(e.Message, Helpers.LogLevel.Error, Client, e); } |
||
4198 | } |
||
4199 | } |
||
4200 | else |
||
4201 | { |
||
4202 | try { callback(false, status, UUID.Zero, UUID.Zero); } |
||
4203 | catch (Exception e) { Logger.Log(e.Message, Helpers.LogLevel.Error, Client, e); } |
||
4204 | } |
||
4205 | } |
||
4206 | else |
||
4207 | { |
||
4208 | string message = "Unrecognized or empty response"; |
||
4209 | |||
4210 | if (error != null) |
||
4211 | { |
||
4212 | if (error is WebException) |
||
4213 | message = ((HttpWebResponse)((WebException)error).Response).StatusDescription; |
||
4214 | |||
4215 | if (message == null || message == "None") |
||
4216 | message = error.Message; |
||
4217 | } |
||
4218 | |||
4219 | try { callback(false, message, UUID.Zero, UUID.Zero); } |
||
4220 | catch (Exception e) { Logger.Log(e.Message, Helpers.LogLevel.Error, Client, e); } |
||
4221 | } |
||
4222 | } |
||
4223 | |||
4224 | private void UpdateScriptAgentInventoryResponse(CapsClient client, OSD result, Exception error) |
||
4225 | { |
||
4226 | KeyValuePair<ScriptUpdatedCallback, byte[]> kvp = (KeyValuePair<ScriptUpdatedCallback, byte[]>)(((object[])client.UserData)[0]); |
||
4227 | ScriptUpdatedCallback callback = kvp.Key; |
||
4228 | byte[] itemData = (byte[])kvp.Value; |
||
4229 | |||
4230 | if (result == null) |
||
4231 | { |
||
4232 | try { callback(false, error.Message, false, null, UUID.Zero, UUID.Zero); } |
||
4233 | catch (Exception e) { Logger.Log(e.Message, Helpers.LogLevel.Error, Client, e); } |
||
4234 | return; |
||
4235 | } |
||
4236 | |||
4237 | OSDMap contents = (OSDMap)result; |
||
4238 | |||
4239 | string status = contents["state"].AsString(); |
||
4240 | if (status == "upload") |
||
4241 | { |
||
4242 | string uploadURL = contents["uploader"].AsString(); |
||
4243 | |||
4244 | CapsClient upload = new CapsClient(new Uri(uploadURL)); |
||
4245 | upload.OnComplete += new CapsClient.CompleteCallback(UpdateScriptAgentInventoryResponse); |
||
4246 | upload.UserData = new object[2] { kvp, (UUID)(((object[])client.UserData)[1]) }; |
||
4247 | upload.BeginGetResponse(itemData, "application/octet-stream", Client.Settings.CAPS_TIMEOUT); |
||
4248 | } |
||
4249 | else if (status == "complete" && callback != null) |
||
4250 | { |
||
4251 | if (contents.ContainsKey("new_asset")) |
||
4252 | { |
||
4253 | // Request full item update so we keep store in sync |
||
4254 | RequestFetchInventory((UUID)(((object[])client.UserData)[1]), contents["new_asset"].AsUUID()); |
||
4255 | |||
4256 | |||
4257 | try |
||
4258 | { |
||
4259 | List<string> compileErrors = null; |
||
4260 | |||
4261 | if (contents.ContainsKey("errors")) |
||
4262 | { |
||
4263 | OSDArray errors = (OSDArray)contents["errors"]; |
||
4264 | compileErrors = new List<string>(errors.Count); |
||
4265 | |||
4266 | for (int i = 0; i < errors.Count; i++) |
||
4267 | { |
||
4268 | compileErrors.Add(errors[i].AsString()); |
||
4269 | } |
||
4270 | } |
||
4271 | |||
4272 | callback(true, |
||
4273 | status, |
||
4274 | contents["compiled"].AsBoolean(), |
||
4275 | compileErrors, |
||
4276 | (UUID)(((object[])client.UserData)[1]), |
||
4277 | contents["new_asset"].AsUUID()); |
||
4278 | } |
||
4279 | catch (Exception e) { Logger.Log(e.Message, Helpers.LogLevel.Error, Client, e); } |
||
4280 | } |
||
4281 | else |
||
4282 | { |
||
4283 | try { callback(false, "Failed to parse asset UUID", false, null, UUID.Zero, UUID.Zero); } |
||
4284 | catch (Exception e) { Logger.Log(e.Message, Helpers.LogLevel.Error, Client, e); } |
||
4285 | } |
||
4286 | } |
||
4287 | else if (callback != null) |
||
4288 | { |
||
4289 | try { callback(false, status, false, null, UUID.Zero, UUID.Zero); } |
||
4290 | catch (Exception e) { Logger.Log(e.Message, Helpers.LogLevel.Error, Client, e); } |
||
4291 | } |
||
4292 | } |
||
4293 | #endregion Internal Handlers |
||
4294 | |||
4295 | #region Packet Handlers |
||
4296 | |||
4297 | /// <summary>Process an incoming packet and raise the appropriate events</summary> |
||
4298 | /// <param name="sender">The sender</param> |
||
4299 | /// <param name="e">The EventArgs object containing the packet data</param> |
||
4300 | protected void SaveAssetIntoInventoryHandler(object sender, PacketReceivedEventArgs e) |
||
4301 | { |
||
4302 | if (m_SaveAssetToInventory != null) |
||
4303 | { |
||
4304 | Packet packet = e.Packet; |
||
4305 | |||
4306 | SaveAssetIntoInventoryPacket save = (SaveAssetIntoInventoryPacket)packet; |
||
4307 | OnSaveAssetToInventory(new SaveAssetToInventoryEventArgs(save.InventoryData.ItemID, save.InventoryData.NewAssetID)); |
||
4308 | } |
||
4309 | } |
||
4310 | |||
4311 | /// <summary>Process an incoming packet and raise the appropriate events</summary> |
||
4312 | /// <param name="sender">The sender</param> |
||
4313 | /// <param name="e">The EventArgs object containing the packet data</param> |
||
4314 | protected void InventoryDescendentsHandler(object sender, PacketReceivedEventArgs e) |
||
4315 | { |
||
4316 | Packet packet = e.Packet; |
||
4317 | |||
4318 | InventoryDescendentsPacket reply = (InventoryDescendentsPacket)packet; |
||
4319 | |||
4320 | if (reply.AgentData.Descendents > 0) |
||
4321 | { |
||
4322 | // InventoryDescendantsReply sends a null folder if the parent doesnt contain any folders |
||
4323 | if (reply.FolderData[0].FolderID != UUID.Zero) |
||
4324 | { |
||
4325 | // Iterate folders in this packet |
||
4326 | for (int i = 0; i < reply.FolderData.Length; i++) |
||
4327 | { |
||
4328 | // If folder already exists then ignore, we assume the version cache |
||
4329 | // logic is working and if the folder is stale then it should not be present. |
||
4330 | |||
4331 | if (!_Store.Contains(reply.FolderData[i].FolderID)) |
||
4332 | { |
||
4333 | InventoryFolder folder = new InventoryFolder(reply.FolderData[i].FolderID); |
||
4334 | folder.ParentUUID = reply.FolderData[i].ParentID; |
||
4335 | folder.Name = Utils.BytesToString(reply.FolderData[i].Name); |
||
4336 | folder.PreferredType = (AssetType)reply.FolderData[i].Type; |
||
4337 | folder.OwnerID = reply.AgentData.OwnerID; |
||
4338 | |||
4339 | _Store[folder.UUID] = folder; |
||
4340 | } |
||
4341 | } |
||
4342 | } |
||
4343 | |||
4344 | // InventoryDescendantsReply sends a null item if the parent doesnt contain any items. |
||
4345 | if (reply.ItemData[0].ItemID != UUID.Zero) |
||
4346 | { |
||
4347 | // Iterate items in this packet |
||
4348 | for (int i = 0; i < reply.ItemData.Length; i++) |
||
4349 | { |
||
4350 | if (reply.ItemData[i].ItemID != UUID.Zero) |
||
4351 | { |
||
4352 | InventoryItem item; |
||
4353 | /* |
||
4354 | * Objects that have been attached in-world prior to being stored on the |
||
4355 | * asset server are stored with the InventoryType of 0 (Texture) |
||
4356 | * instead of 17 (Attachment) |
||
4357 | * |
||
4358 | * This corrects that behavior by forcing Object Asset types that have an |
||
4359 | * invalid InventoryType with the proper InventoryType of Attachment. |
||
4360 | */ |
||
4361 | if ((AssetType)reply.ItemData[i].Type == AssetType.Object |
||
4362 | && (InventoryType)reply.ItemData[i].InvType == InventoryType.Texture) |
||
4363 | { |
||
4364 | item = CreateInventoryItem(InventoryType.Attachment, reply.ItemData[i].ItemID); |
||
4365 | item.InventoryType = InventoryType.Attachment; |
||
4366 | } |
||
4367 | else |
||
4368 | { |
||
4369 | item = CreateInventoryItem((InventoryType)reply.ItemData[i].InvType, reply.ItemData[i].ItemID); |
||
4370 | item.InventoryType = (InventoryType)reply.ItemData[i].InvType; |
||
4371 | } |
||
4372 | |||
4373 | item.ParentUUID = reply.ItemData[i].FolderID; |
||
4374 | item.CreatorID = reply.ItemData[i].CreatorID; |
||
4375 | item.AssetType = (AssetType)reply.ItemData[i].Type; |
||
4376 | item.AssetUUID = reply.ItemData[i].AssetID; |
||
4377 | item.CreationDate = Utils.UnixTimeToDateTime((uint)reply.ItemData[i].CreationDate); |
||
4378 | item.Description = Utils.BytesToString(reply.ItemData[i].Description); |
||
4379 | item.Flags = reply.ItemData[i].Flags; |
||
4380 | item.Name = Utils.BytesToString(reply.ItemData[i].Name); |
||
4381 | item.GroupID = reply.ItemData[i].GroupID; |
||
4382 | item.GroupOwned = reply.ItemData[i].GroupOwned; |
||
4383 | item.Permissions = new Permissions( |
||
4384 | reply.ItemData[i].BaseMask, |
||
4385 | reply.ItemData[i].EveryoneMask, |
||
4386 | reply.ItemData[i].GroupMask, |
||
4387 | reply.ItemData[i].NextOwnerMask, |
||
4388 | reply.ItemData[i].OwnerMask); |
||
4389 | item.SalePrice = reply.ItemData[i].SalePrice; |
||
4390 | item.SaleType = (SaleType)reply.ItemData[i].SaleType; |
||
4391 | item.OwnerID = reply.AgentData.OwnerID; |
||
4392 | |||
4393 | _Store[item.UUID] = item; |
||
4394 | } |
||
4395 | } |
||
4396 | } |
||
4397 | } |
||
4398 | |||
4399 | InventoryFolder parentFolder = null; |
||
4400 | |||
4401 | if (_Store.Contains(reply.AgentData.FolderID) && |
||
4402 | _Store[reply.AgentData.FolderID] is InventoryFolder) |
||
4403 | { |
||
4404 | parentFolder = _Store[reply.AgentData.FolderID] as InventoryFolder; |
||
4405 | } |
||
4406 | else |
||
4407 | { |
||
4408 | Logger.Log("Don't have a reference to FolderID " + reply.AgentData.FolderID.ToString() + |
||
4409 | " or it is not a folder", Helpers.LogLevel.Error, Client); |
||
4410 | return; |
||
4411 | } |
||
4412 | |||
4413 | if (reply.AgentData.Version < parentFolder.Version) |
||
4414 | { |
||
4415 | Logger.Log("Got an outdated InventoryDescendents packet for folder " + parentFolder.Name + |
||
4416 | ", this version = " + reply.AgentData.Version + ", latest version = " + parentFolder.Version, |
||
4417 | Helpers.LogLevel.Warning, Client); |
||
4418 | return; |
||
4419 | } |
||
4420 | |||
4421 | parentFolder.Version = reply.AgentData.Version; |
||
4422 | // FIXME: reply.AgentData.Descendants is not parentFolder.DescendentCount if we didn't |
||
4423 | // request items and folders |
||
4424 | parentFolder.DescendentCount = reply.AgentData.Descendents; |
||
4425 | _Store.GetNodeFor(reply.AgentData.FolderID).NeedsUpdate = false; |
||
4426 | |||
4427 | #region FindObjectsByPath Handling |
||
4428 | |||
4429 | if (_Searches.Count > 0) |
||
4430 | { |
||
4431 | lock (_Searches) |
||
4432 | { |
||
4433 | StartSearch: |
||
4434 | |||
4435 | // Iterate over all of the outstanding searches |
||
4436 | for (int i = 0; i < _Searches.Count; i++) |
||
4437 | { |
||
4438 | InventorySearch search = _Searches[i]; |
||
4439 | List<InventoryBase> folderContents = _Store.GetContents(search.Folder); |
||
4440 | |||
4441 | // Iterate over all of the inventory objects in the base search folder |
||
4442 | for (int j = 0; j < folderContents.Count; j++) |
||
4443 | { |
||
4444 | // Check if this inventory object matches the current path node |
||
4445 | if (folderContents[j].Name == search.Path[search.Level]) |
||
4446 | { |
||
4447 | if (search.Level == search.Path.Length - 1) |
||
4448 | { |
||
4449 | Logger.DebugLog("Finished path search of " + String.Join("/", search.Path), Client); |
||
4450 | |||
4451 | // This is the last node in the path, fire the callback and clean up |
||
4452 | if (m_FindObjectByPathReply != null) |
||
4453 | { |
||
4454 | OnFindObjectByPathReply(new FindObjectByPathReplyEventArgs(String.Join("/", search.Path), |
||
4455 | folderContents[j].UUID)); |
||
4456 | } |
||
4457 | |||
4458 | // Remove this entry and restart the loop since we are changing the collection size |
||
4459 | _Searches.RemoveAt(i); |
||
4460 | goto StartSearch; |
||
4461 | } |
||
4462 | else |
||
4463 | { |
||
4464 | // We found a match but it is not the end of the path, request the next level |
||
4465 | Logger.DebugLog(String.Format("Matched level {0}/{1} in a path search of {2}", |
||
4466 | search.Level, search.Path.Length - 1, String.Join("/", search.Path)), Client); |
||
4467 | |||
4468 | search.Folder = folderContents[j].UUID; |
||
4469 | search.Level++; |
||
4470 | _Searches[i] = search; |
||
4471 | |||
4472 | RequestFolderContents(search.Folder, search.Owner, true, true, |
||
4473 | InventorySortOrder.ByName); |
||
4474 | } |
||
4475 | } |
||
4476 | } |
||
4477 | } |
||
4478 | } |
||
4479 | } |
||
4480 | |||
4481 | #endregion FindObjectsByPath Handling |
||
4482 | |||
4483 | // Callback for inventory folder contents being updated |
||
4484 | OnFolderUpdated(new FolderUpdatedEventArgs(parentFolder.UUID, true)); |
||
4485 | } |
||
4486 | |||
4487 | /// <summary> |
||
4488 | /// UpdateCreateInventoryItem packets are received when a new inventory item |
||
4489 | /// is created. This may occur when an object that's rezzed in world is |
||
4490 | /// taken into inventory, when an item is created using the CreateInventoryItem |
||
4491 | /// packet, or when an object is purchased |
||
4492 | /// </summary> |
||
4493 | /// <param name="sender">The sender</param> |
||
4494 | /// <param name="e">The EventArgs object containing the packet data</param> |
||
4495 | protected void UpdateCreateInventoryItemHandler(object sender, PacketReceivedEventArgs e) |
||
4496 | { |
||
4497 | Packet packet = e.Packet; |
||
4498 | |||
4499 | UpdateCreateInventoryItemPacket reply = packet as UpdateCreateInventoryItemPacket; |
||
4500 | |||
4501 | foreach (UpdateCreateInventoryItemPacket.InventoryDataBlock dataBlock in reply.InventoryData) |
||
4502 | { |
||
4503 | if (dataBlock.InvType == (sbyte)InventoryType.Folder) |
||
4504 | { |
||
4505 | Logger.Log("Received InventoryFolder in an UpdateCreateInventoryItem packet, this should not happen!", |
||
4506 | Helpers.LogLevel.Error, Client); |
||
4507 | continue; |
||
4508 | } |
||
4509 | |||
4510 | InventoryItem item = CreateInventoryItem((InventoryType)dataBlock.InvType, dataBlock.ItemID); |
||
4511 | item.AssetType = (AssetType)dataBlock.Type; |
||
4512 | item.AssetUUID = dataBlock.AssetID; |
||
4513 | item.CreationDate = Utils.UnixTimeToDateTime(dataBlock.CreationDate); |
||
4514 | item.CreatorID = dataBlock.CreatorID; |
||
4515 | item.Description = Utils.BytesToString(dataBlock.Description); |
||
4516 | item.Flags = dataBlock.Flags; |
||
4517 | item.GroupID = dataBlock.GroupID; |
||
4518 | item.GroupOwned = dataBlock.GroupOwned; |
||
4519 | item.Name = Utils.BytesToString(dataBlock.Name); |
||
4520 | item.OwnerID = dataBlock.OwnerID; |
||
4521 | item.ParentUUID = dataBlock.FolderID; |
||
4522 | item.Permissions = new Permissions( |
||
4523 | dataBlock.BaseMask, |
||
4524 | dataBlock.EveryoneMask, |
||
4525 | dataBlock.GroupMask, |
||
4526 | dataBlock.NextOwnerMask, |
||
4527 | dataBlock.OwnerMask); |
||
4528 | item.SalePrice = dataBlock.SalePrice; |
||
4529 | item.SaleType = (SaleType)dataBlock.SaleType; |
||
4530 | |||
4531 | /* |
||
4532 | * When attaching new objects, an UpdateCreateInventoryItem packet will be |
||
4533 | * returned by the server that has a FolderID/ParentUUID of zero. It is up |
||
4534 | * to the client to make sure that the item gets a good folder, otherwise |
||
4535 | * it will end up inaccesible in inventory. |
||
4536 | */ |
||
4537 | if (item.ParentUUID == UUID.Zero) |
||
4538 | { |
||
4539 | // assign default folder for type |
||
4540 | item.ParentUUID = FindFolderForType(item.AssetType); |
||
4541 | |||
4542 | Logger.Log("Received an item through UpdateCreateInventoryItem with no parent folder, assigning to folder " + |
||
4543 | item.ParentUUID, Helpers.LogLevel.Info); |
||
4544 | |||
4545 | // send update to the sim |
||
4546 | RequestUpdateItem(item); |
||
4547 | } |
||
4548 | |||
4549 | // Update the local copy |
||
4550 | _Store[item.UUID] = item; |
||
4551 | |||
4552 | // Look for an "item created" callback |
||
4553 | ItemCreatedCallback createdCallback; |
||
4554 | if (_ItemCreatedCallbacks.TryGetValue(dataBlock.CallbackID, out createdCallback)) |
||
4555 | { |
||
4556 | _ItemCreatedCallbacks.Remove(dataBlock.CallbackID); |
||
4557 | |||
4558 | try { createdCallback(true, item); } |
||
4559 | catch (Exception ex) { Logger.Log(ex.Message, Helpers.LogLevel.Error, Client, ex); } |
||
4560 | } |
||
4561 | |||
4562 | // TODO: Is this callback even triggered when items are copied? |
||
4563 | // Look for an "item copied" callback |
||
4564 | ItemCopiedCallback copyCallback; |
||
4565 | if (_ItemCopiedCallbacks.TryGetValue(dataBlock.CallbackID, out copyCallback)) |
||
4566 | { |
||
4567 | _ItemCopiedCallbacks.Remove(dataBlock.CallbackID); |
||
4568 | |||
4569 | try { copyCallback(item); } |
||
4570 | catch (Exception ex) { Logger.Log(ex.Message, Helpers.LogLevel.Error, Client, ex); } |
||
4571 | } |
||
4572 | |||
4573 | //This is triggered when an item is received from a task |
||
4574 | if (m_TaskItemReceived != null) |
||
4575 | { |
||
4576 | OnTaskItemReceived(new TaskItemReceivedEventArgs(item.UUID, dataBlock.FolderID, |
||
4577 | item.CreatorID, item.AssetUUID, item.InventoryType)); |
||
4578 | } |
||
4579 | } |
||
4580 | } |
||
4581 | |||
4582 | /// <summary>Process an incoming packet and raise the appropriate events</summary> |
||
4583 | /// <param name="sender">The sender</param> |
||
4584 | /// <param name="e">The EventArgs object containing the packet data</param> |
||
4585 | protected void MoveInventoryItemHandler(object sender, PacketReceivedEventArgs e) |
||
4586 | { |
||
4587 | Packet packet = e.Packet; |
||
4588 | |||
4589 | MoveInventoryItemPacket move = (MoveInventoryItemPacket)packet; |
||
4590 | |||
4591 | for (int i = 0; i < move.InventoryData.Length; i++) |
||
4592 | { |
||
4593 | // FIXME: Do something here |
||
4594 | string newName = Utils.BytesToString(move.InventoryData[i].NewName); |
||
4595 | |||
4596 | Logger.Log(String.Format( |
||
4597 | "MoveInventoryItemHandler: Item {0} is moving to Folder {1} with new name \"{2}\". Someone write this function!", |
||
4598 | move.InventoryData[i].ItemID.ToString(), move.InventoryData[i].FolderID.ToString(), |
||
4599 | newName), Helpers.LogLevel.Warning, Client); |
||
4600 | } |
||
4601 | } |
||
4602 | |||
4603 | protected void BulkUpdateInventoryCapHandler(string capsKey, Interfaces.IMessage message, Simulator simulator) |
||
4604 | { |
||
4605 | BulkUpdateInventoryMessage msg = (BulkUpdateInventoryMessage)message; |
||
4606 | |||
4607 | foreach (BulkUpdateInventoryMessage.FolderDataInfo newFolder in msg.FolderData) |
||
4608 | { |
||
4609 | if (newFolder.FolderID == UUID.Zero) continue; |
||
4610 | |||
4611 | InventoryFolder folder; |
||
4612 | if (!_Store.Contains(newFolder.FolderID)) |
||
4613 | { |
||
4614 | folder = new InventoryFolder(newFolder.FolderID); |
||
4615 | } |
||
4616 | else |
||
4617 | { |
||
4618 | folder = (InventoryFolder)_Store[newFolder.FolderID]; |
||
4619 | } |
||
4620 | |||
4621 | folder.Name = newFolder.Name; |
||
4622 | folder.ParentUUID = newFolder.ParentID; |
||
4623 | folder.PreferredType = newFolder.Type; |
||
4624 | _Store[folder.UUID] = folder; |
||
4625 | } |
||
4626 | |||
4627 | foreach (BulkUpdateInventoryMessage.ItemDataInfo newItem in msg.ItemData) |
||
4628 | { |
||
4629 | if (newItem.ItemID == UUID.Zero) continue; |
||
4630 | InventoryType invType = newItem.InvType; |
||
4631 | |||
4632 | lock (_ItemInventoryTypeRequest) |
||
4633 | { |
||
4634 | InventoryType storedType = 0; |
||
4635 | if (_ItemInventoryTypeRequest.TryGetValue(newItem.CallbackID, out storedType)) |
||
4636 | { |
||
4637 | _ItemInventoryTypeRequest.Remove(newItem.CallbackID); |
||
4638 | invType = storedType; |
||
4639 | } |
||
4640 | } |
||
4641 | InventoryItem item = SafeCreateInventoryItem(invType, newItem.ItemID); |
||
4642 | |||
4643 | item.AssetType = newItem.Type; |
||
4644 | item.AssetUUID = newItem.AssetID; |
||
4645 | item.CreationDate = newItem.CreationDate; |
||
4646 | item.CreatorID = newItem.CreatorID; |
||
4647 | item.Description = newItem.Description; |
||
4648 | item.Flags = newItem.Flags; |
||
4649 | item.GroupID = newItem.GroupID; |
||
4650 | item.GroupOwned = newItem.GroupOwned; |
||
4651 | item.Name = newItem.Name; |
||
4652 | item.OwnerID = newItem.OwnerID; |
||
4653 | item.ParentUUID = newItem.FolderID; |
||
4654 | item.Permissions.BaseMask = newItem.BaseMask; |
||
4655 | item.Permissions.EveryoneMask = newItem.EveryoneMask; |
||
4656 | item.Permissions.GroupMask = newItem.GroupMask; |
||
4657 | item.Permissions.NextOwnerMask = newItem.NextOwnerMask; |
||
4658 | item.Permissions.OwnerMask = newItem.OwnerMask; |
||
4659 | item.SalePrice = newItem.SalePrice; |
||
4660 | item.SaleType = newItem.SaleType; |
||
4661 | |||
4662 | _Store[item.UUID] = item; |
||
4663 | |||
4664 | // Look for an "item created" callback |
||
4665 | ItemCreatedCallback callback; |
||
4666 | if (_ItemCreatedCallbacks.TryGetValue(newItem.CallbackID, out callback)) |
||
4667 | { |
||
4668 | _ItemCreatedCallbacks.Remove(newItem.CallbackID); |
||
4669 | |||
4670 | try { callback(true, item); } |
||
4671 | catch (Exception ex) { Logger.Log(ex.Message, Helpers.LogLevel.Error, Client, ex); } |
||
4672 | } |
||
4673 | |||
4674 | // Look for an "item copied" callback |
||
4675 | ItemCopiedCallback copyCallback; |
||
4676 | if (_ItemCopiedCallbacks.TryGetValue(newItem.CallbackID, out copyCallback)) |
||
4677 | { |
||
4678 | _ItemCopiedCallbacks.Remove(newItem.CallbackID); |
||
4679 | |||
4680 | try { copyCallback(item); } |
||
4681 | catch (Exception ex) { Logger.Log(ex.Message, Helpers.LogLevel.Error, Client, ex); } |
||
4682 | } |
||
4683 | |||
4684 | } |
||
4685 | |||
4686 | } |
||
4687 | |||
4688 | /// <summary>Process an incoming packet and raise the appropriate events</summary> |
||
4689 | /// <param name="sender">The sender</param> |
||
4690 | /// <param name="e">The EventArgs object containing the packet data</param> |
||
4691 | protected void BulkUpdateInventoryHandler(object sender, PacketReceivedEventArgs e) |
||
4692 | { |
||
4693 | Packet packet = e.Packet; |
||
4694 | |||
4695 | BulkUpdateInventoryPacket update = packet as BulkUpdateInventoryPacket; |
||
4696 | |||
4697 | if (update.FolderData.Length > 0 && update.FolderData[0].FolderID != UUID.Zero) |
||
4698 | { |
||
4699 | foreach (BulkUpdateInventoryPacket.FolderDataBlock dataBlock in update.FolderData) |
||
4700 | { |
||
4701 | InventoryFolder folder; |
||
4702 | if (!_Store.Contains(dataBlock.FolderID)) |
||
4703 | { |
||
4704 | folder = new InventoryFolder(dataBlock.FolderID); |
||
4705 | } |
||
4706 | else |
||
4707 | { |
||
4708 | folder = (InventoryFolder)_Store[dataBlock.FolderID]; |
||
4709 | } |
||
4710 | |||
4711 | if (dataBlock.Name != null) |
||
4712 | { |
||
4713 | folder.Name = Utils.BytesToString(dataBlock.Name); |
||
4714 | } |
||
4715 | folder.OwnerID = update.AgentData.AgentID; |
||
4716 | folder.ParentUUID = dataBlock.ParentID; |
||
4717 | _Store[folder.UUID] = folder; |
||
4718 | } |
||
4719 | } |
||
4720 | |||
4721 | if (update.ItemData.Length > 0 && update.ItemData[0].ItemID != UUID.Zero) |
||
4722 | { |
||
4723 | for (int i = 0; i < update.ItemData.Length; i++) |
||
4724 | { |
||
4725 | BulkUpdateInventoryPacket.ItemDataBlock dataBlock = update.ItemData[i]; |
||
4726 | |||
4727 | InventoryItem item = SafeCreateInventoryItem((InventoryType)dataBlock.InvType, dataBlock.ItemID); |
||
4728 | |||
4729 | item.AssetType = (AssetType)dataBlock.Type; |
||
4730 | if (dataBlock.AssetID != UUID.Zero) item.AssetUUID = dataBlock.AssetID; |
||
4731 | item.CreationDate = Utils.UnixTimeToDateTime(dataBlock.CreationDate); |
||
4732 | item.CreatorID = dataBlock.CreatorID; |
||
4733 | item.Description = Utils.BytesToString(dataBlock.Description); |
||
4734 | item.Flags = dataBlock.Flags; |
||
4735 | item.GroupID = dataBlock.GroupID; |
||
4736 | item.GroupOwned = dataBlock.GroupOwned; |
||
4737 | item.Name = Utils.BytesToString(dataBlock.Name); |
||
4738 | item.OwnerID = dataBlock.OwnerID; |
||
4739 | item.ParentUUID = dataBlock.FolderID; |
||
4740 | item.Permissions = new Permissions( |
||
4741 | dataBlock.BaseMask, |
||
4742 | dataBlock.EveryoneMask, |
||
4743 | dataBlock.GroupMask, |
||
4744 | dataBlock.NextOwnerMask, |
||
4745 | dataBlock.OwnerMask); |
||
4746 | item.SalePrice = dataBlock.SalePrice; |
||
4747 | item.SaleType = (SaleType)dataBlock.SaleType; |
||
4748 | |||
4749 | _Store[item.UUID] = item; |
||
4750 | |||
4751 | // Look for an "item created" callback |
||
4752 | ItemCreatedCallback callback; |
||
4753 | if (_ItemCreatedCallbacks.TryGetValue(dataBlock.CallbackID, out callback)) |
||
4754 | { |
||
4755 | _ItemCreatedCallbacks.Remove(dataBlock.CallbackID); |
||
4756 | |||
4757 | try { callback(true, item); } |
||
4758 | catch (Exception ex) { Logger.Log(ex.Message, Helpers.LogLevel.Error, Client, ex); } |
||
4759 | } |
||
4760 | |||
4761 | // Look for an "item copied" callback |
||
4762 | ItemCopiedCallback copyCallback; |
||
4763 | if (_ItemCopiedCallbacks.TryGetValue(dataBlock.CallbackID, out copyCallback)) |
||
4764 | { |
||
4765 | _ItemCopiedCallbacks.Remove(dataBlock.CallbackID); |
||
4766 | |||
4767 | try { copyCallback(item); } |
||
4768 | catch (Exception ex) { Logger.Log(ex.Message, Helpers.LogLevel.Error, Client, ex); } |
||
4769 | } |
||
4770 | } |
||
4771 | } |
||
4772 | } |
||
4773 | |||
4774 | /// <summary>Process an incoming packet and raise the appropriate events</summary> |
||
4775 | /// <param name="sender">The sender</param> |
||
4776 | /// <param name="e">The EventArgs object containing the packet data</param> |
||
4777 | protected void FetchInventoryReplyHandler(object sender, PacketReceivedEventArgs e) |
||
4778 | { |
||
4779 | Packet packet = e.Packet; |
||
4780 | |||
4781 | FetchInventoryReplyPacket reply = packet as FetchInventoryReplyPacket; |
||
4782 | |||
4783 | foreach (FetchInventoryReplyPacket.InventoryDataBlock dataBlock in reply.InventoryData) |
||
4784 | { |
||
4785 | if (dataBlock.InvType == (sbyte)InventoryType.Folder) |
||
4786 | { |
||
4787 | Logger.Log("Received FetchInventoryReply for an inventory folder, this should not happen!", |
||
4788 | Helpers.LogLevel.Error, Client); |
||
4789 | continue; |
||
4790 | } |
||
4791 | |||
4792 | InventoryItem item = CreateInventoryItem((InventoryType)dataBlock.InvType, dataBlock.ItemID); |
||
4793 | item.AssetType = (AssetType)dataBlock.Type; |
||
4794 | item.AssetUUID = dataBlock.AssetID; |
||
4795 | item.CreationDate = Utils.UnixTimeToDateTime(dataBlock.CreationDate); |
||
4796 | item.CreatorID = dataBlock.CreatorID; |
||
4797 | item.Description = Utils.BytesToString(dataBlock.Description); |
||
4798 | item.Flags = dataBlock.Flags; |
||
4799 | item.GroupID = dataBlock.GroupID; |
||
4800 | item.GroupOwned = dataBlock.GroupOwned; |
||
4801 | item.InventoryType = (InventoryType)dataBlock.InvType; |
||
4802 | item.Name = Utils.BytesToString(dataBlock.Name); |
||
4803 | item.OwnerID = dataBlock.OwnerID; |
||
4804 | item.ParentUUID = dataBlock.FolderID; |
||
4805 | item.Permissions = new Permissions( |
||
4806 | dataBlock.BaseMask, |
||
4807 | dataBlock.EveryoneMask, |
||
4808 | dataBlock.GroupMask, |
||
4809 | dataBlock.NextOwnerMask, |
||
4810 | dataBlock.OwnerMask); |
||
4811 | item.SalePrice = dataBlock.SalePrice; |
||
4812 | item.SaleType = (SaleType)dataBlock.SaleType; |
||
4813 | item.UUID = dataBlock.ItemID; |
||
4814 | |||
4815 | _Store[item.UUID] = item; |
||
4816 | |||
4817 | // Fire the callback for an item being fetched |
||
4818 | OnItemReceived(new ItemReceivedEventArgs(item)); |
||
4819 | } |
||
4820 | } |
||
4821 | |||
4822 | /// <summary>Process an incoming packet and raise the appropriate events</summary> |
||
4823 | /// <param name="sender">The sender</param> |
||
4824 | /// <param name="e">The EventArgs object containing the packet data</param> |
||
4825 | protected void ReplyTaskInventoryHandler(object sender, PacketReceivedEventArgs e) |
||
4826 | { |
||
4827 | if (m_TaskInventoryReply != null) |
||
4828 | { |
||
4829 | Packet packet = e.Packet; |
||
4830 | |||
4831 | ReplyTaskInventoryPacket reply = (ReplyTaskInventoryPacket)packet; |
||
4832 | |||
4833 | OnTaskInventoryReply(new TaskInventoryReplyEventArgs(reply.InventoryData.TaskID, reply.InventoryData.Serial, |
||
4834 | Utils.BytesToString(reply.InventoryData.Filename))); |
||
4835 | } |
||
4836 | } |
||
4837 | |||
4838 | protected void ScriptRunningReplyMessageHandler(string capsKey, Interfaces.IMessage message, Simulator simulator) |
||
4839 | { |
||
4840 | if (m_ScriptRunningReply != null) |
||
4841 | { |
||
4842 | ScriptRunningReplyMessage msg = (ScriptRunningReplyMessage)message; |
||
4843 | OnScriptRunningReply(new ScriptRunningReplyEventArgs(msg.ObjectID, msg.ItemID, msg.Mono, msg.Running)); |
||
4844 | } |
||
4845 | } |
||
4846 | |||
4847 | #endregion Packet Handlers |
||
4848 | } |
||
4849 | |||
4850 | #region EventArgs |
||
4851 | |||
4852 | public class InventoryObjectOfferedEventArgs : EventArgs |
||
4853 | { |
||
4854 | private readonly InstantMessage m_Offer; |
||
4855 | private readonly AssetType m_AssetType; |
||
4856 | private readonly UUID m_ObjectID; |
||
4857 | private readonly bool m_FromTask; |
||
4858 | |||
4859 | /// <summary>Set to true to accept offer, false to decline it</summary> |
||
4860 | public bool Accept { get; set; } |
||
4861 | /// <summary>The folder to accept the inventory into, if null default folder for <see cref="AssetType"/> will be used</summary> |
||
4862 | public UUID FolderID { get; set; } |
||
4863 | |||
4864 | public InstantMessage Offer { get { return m_Offer; } } |
||
4865 | public AssetType AssetType { get { return m_AssetType; } } |
||
4866 | public UUID ObjectID { get { return m_ObjectID; } } |
||
4867 | public bool FromTask { get { return m_FromTask; } } |
||
4868 | |||
4869 | public InventoryObjectOfferedEventArgs(InstantMessage offerDetails, AssetType type, UUID objectID, bool fromTask, UUID folderID) |
||
4870 | { |
||
4871 | this.Accept = false; |
||
4872 | this.FolderID = folderID; |
||
4873 | this.m_Offer = offerDetails; |
||
4874 | this.m_AssetType = type; |
||
4875 | this.m_ObjectID = objectID; |
||
4876 | this.m_FromTask = fromTask; |
||
4877 | } |
||
4878 | } |
||
4879 | |||
4880 | public class FolderUpdatedEventArgs : EventArgs |
||
4881 | { |
||
4882 | private readonly UUID m_FolderID; |
||
4883 | public UUID FolderID { get { return m_FolderID; } } |
||
4884 | private readonly bool m_Success; |
||
4885 | public bool Success { get { return m_Success; } } |
||
4886 | |||
4887 | public FolderUpdatedEventArgs(UUID folderID, bool success) |
||
4888 | { |
||
4889 | this.m_FolderID = folderID; |
||
4890 | this.m_Success = success; |
||
4891 | } |
||
4892 | } |
||
4893 | |||
4894 | public class ItemReceivedEventArgs : EventArgs |
||
4895 | { |
||
4896 | private readonly InventoryItem m_Item; |
||
4897 | |||
4898 | public InventoryItem Item { get { return m_Item; } } |
||
4899 | |||
4900 | public ItemReceivedEventArgs(InventoryItem item) |
||
4901 | { |
||
4902 | this.m_Item = item; |
||
4903 | } |
||
4904 | } |
||
4905 | |||
4906 | public class FindObjectByPathReplyEventArgs : EventArgs |
||
4907 | { |
||
4908 | private readonly String m_Path; |
||
4909 | private readonly UUID m_InventoryObjectID; |
||
4910 | |||
4911 | public String Path { get { return m_Path; } } |
||
4912 | public UUID InventoryObjectID { get { return m_InventoryObjectID; } } |
||
4913 | |||
4914 | public FindObjectByPathReplyEventArgs(string path, UUID inventoryObjectID) |
||
4915 | { |
||
4916 | this.m_Path = path; |
||
4917 | this.m_InventoryObjectID = inventoryObjectID; |
||
4918 | } |
||
4919 | } |
||
4920 | |||
4921 | /// <summary> |
||
4922 | /// Callback when an inventory object is accepted and received from a |
||
4923 | /// task inventory. This is the callback in which you actually get |
||
4924 | /// the ItemID, as in ObjectOfferedCallback it is null when received |
||
4925 | /// from a task. |
||
4926 | /// </summary> |
||
4927 | public class TaskItemReceivedEventArgs : EventArgs |
||
4928 | { |
||
4929 | private readonly UUID m_ItemID; |
||
4930 | private readonly UUID m_FolderID; |
||
4931 | private readonly UUID m_CreatorID; |
||
4932 | private readonly UUID m_AssetID; |
||
4933 | private readonly InventoryType m_Type; |
||
4934 | |||
4935 | public UUID ItemID { get { return m_ItemID; } } |
||
4936 | public UUID FolderID { get { return m_FolderID; } } |
||
4937 | public UUID CreatorID { get { return m_CreatorID; } } |
||
4938 | public UUID AssetID { get { return m_AssetID; } } |
||
4939 | public InventoryType Type { get { return m_Type; } } |
||
4940 | |||
4941 | public TaskItemReceivedEventArgs(UUID itemID, UUID folderID, UUID creatorID, UUID assetID, InventoryType type) |
||
4942 | { |
||
4943 | this.m_ItemID = itemID; |
||
4944 | this.m_FolderID = folderID; |
||
4945 | this.m_CreatorID = creatorID; |
||
4946 | this.m_AssetID = assetID; |
||
4947 | this.m_Type = type; |
||
4948 | } |
||
4949 | } |
||
4950 | |||
4951 | public class TaskInventoryReplyEventArgs : EventArgs |
||
4952 | { |
||
4953 | private readonly UUID m_ItemID; |
||
4954 | private readonly Int16 m_Serial; |
||
4955 | private readonly String m_AssetFilename; |
||
4956 | |||
4957 | public UUID ItemID { get { return m_ItemID; } } |
||
4958 | public Int16 Serial { get { return m_Serial; } } |
||
4959 | public String AssetFilename { get { return m_AssetFilename; } } |
||
4960 | |||
4961 | public TaskInventoryReplyEventArgs(UUID itemID, short serial, string assetFilename) |
||
4962 | { |
||
4963 | this.m_ItemID = itemID; |
||
4964 | this.m_Serial = serial; |
||
4965 | this.m_AssetFilename = assetFilename; |
||
4966 | } |
||
4967 | } |
||
4968 | |||
4969 | public class SaveAssetToInventoryEventArgs : EventArgs |
||
4970 | { |
||
4971 | private readonly UUID m_ItemID; |
||
4972 | private readonly UUID m_NewAssetID; |
||
4973 | |||
4974 | public UUID ItemID { get { return m_ItemID; } } |
||
4975 | public UUID NewAssetID { get { return m_NewAssetID; } } |
||
4976 | |||
4977 | public SaveAssetToInventoryEventArgs(UUID itemID, UUID newAssetID) |
||
4978 | { |
||
4979 | this.m_ItemID = itemID; |
||
4980 | this.m_NewAssetID = newAssetID; |
||
4981 | } |
||
4982 | } |
||
4983 | |||
4984 | public class ScriptRunningReplyEventArgs : EventArgs |
||
4985 | { |
||
4986 | private readonly UUID m_ObjectID; |
||
4987 | private readonly UUID m_ScriptID; |
||
4988 | private readonly bool m_IsMono; |
||
4989 | private readonly bool m_IsRunning; |
||
4990 | |||
4991 | public UUID ObjectID { get { return m_ObjectID; } } |
||
4992 | public UUID ScriptID { get { return m_ScriptID; } } |
||
4993 | public bool IsMono { get { return m_IsMono; } } |
||
4994 | public bool IsRunning { get { return m_IsRunning; } } |
||
4995 | |||
4996 | public ScriptRunningReplyEventArgs(UUID objectID, UUID sctriptID, bool isMono, bool isRunning) |
||
4997 | { |
||
4998 | this.m_ObjectID = objectID; |
||
4999 | this.m_ScriptID = sctriptID; |
||
5000 | this.m_IsMono = isMono; |
||
5001 | this.m_IsRunning = isRunning; |
||
5002 | } |
||
5003 | } |
||
5004 | #endregion |
||
5005 | } |