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.IO; |
||
30 | using System.Runtime.Serialization.Formatters.Binary; |
||
31 | using System.Runtime.Serialization; |
||
32 | |||
33 | namespace OpenMetaverse |
||
34 | { |
||
35 | /// <summary> |
||
36 | /// Exception class to identify inventory exceptions |
||
37 | /// </summary> |
||
38 | public class InventoryException : Exception |
||
39 | { |
||
40 | public InventoryException(string message) |
||
41 | : base(message) { } |
||
42 | } |
||
43 | |||
44 | /// <summary> |
||
45 | /// Responsible for maintaining inventory structure. Inventory constructs nodes |
||
46 | /// and manages node children as is necessary to maintain a coherant hirarchy. |
||
47 | /// Other classes should not manipulate or create InventoryNodes explicitly. When |
||
48 | /// A node's parent changes (when a folder is moved, for example) simply pass |
||
49 | /// Inventory the updated InventoryFolder and it will make the appropriate changes |
||
50 | /// to its internal representation. |
||
51 | /// </summary> |
||
52 | public class Inventory |
||
53 | { |
||
54 | |||
55 | /// <summary>The event subscribers, null of no subscribers</summary> |
||
56 | private EventHandler<InventoryObjectUpdatedEventArgs> m_InventoryObjectUpdated; |
||
57 | |||
58 | ///<summary>Raises the InventoryObjectUpdated Event</summary> |
||
59 | /// <param name="e">A InventoryObjectUpdatedEventArgs object containing |
||
60 | /// the data sent from the simulator</param> |
||
61 | protected virtual void OnInventoryObjectUpdated(InventoryObjectUpdatedEventArgs e) |
||
62 | { |
||
63 | EventHandler<InventoryObjectUpdatedEventArgs> handler = m_InventoryObjectUpdated; |
||
64 | if (handler != null) |
||
65 | handler(this, e); |
||
66 | } |
||
67 | |||
68 | /// <summary>Thread sync lock object</summary> |
||
69 | private readonly object m_InventoryObjectUpdatedLock = new object(); |
||
70 | |||
71 | /// <summary>Raised when the simulator sends us data containing |
||
72 | /// ...</summary> |
||
73 | public event EventHandler<InventoryObjectUpdatedEventArgs> InventoryObjectUpdated |
||
74 | { |
||
75 | add { lock (m_InventoryObjectUpdatedLock) { m_InventoryObjectUpdated += value; } } |
||
76 | remove { lock (m_InventoryObjectUpdatedLock) { m_InventoryObjectUpdated -= value; } } |
||
77 | } |
||
78 | |||
79 | /// <summary>The event subscribers, null of no subscribers</summary> |
||
80 | private EventHandler<InventoryObjectRemovedEventArgs> m_InventoryObjectRemoved; |
||
81 | |||
82 | ///<summary>Raises the InventoryObjectRemoved Event</summary> |
||
83 | /// <param name="e">A InventoryObjectRemovedEventArgs object containing |
||
84 | /// the data sent from the simulator</param> |
||
85 | protected virtual void OnInventoryObjectRemoved(InventoryObjectRemovedEventArgs e) |
||
86 | { |
||
87 | EventHandler<InventoryObjectRemovedEventArgs> handler = m_InventoryObjectRemoved; |
||
88 | if (handler != null) |
||
89 | handler(this, e); |
||
90 | } |
||
91 | |||
92 | /// <summary>Thread sync lock object</summary> |
||
93 | private readonly object m_InventoryObjectRemovedLock = new object(); |
||
94 | |||
95 | /// <summary>Raised when the simulator sends us data containing |
||
96 | /// ...</summary> |
||
97 | public event EventHandler<InventoryObjectRemovedEventArgs> InventoryObjectRemoved |
||
98 | { |
||
99 | add { lock (m_InventoryObjectRemovedLock) { m_InventoryObjectRemoved += value; } } |
||
100 | remove { lock (m_InventoryObjectRemovedLock) { m_InventoryObjectRemoved -= value; } } |
||
101 | } |
||
102 | |||
103 | /// <summary>The event subscribers, null of no subscribers</summary> |
||
104 | private EventHandler<InventoryObjectAddedEventArgs> m_InventoryObjectAdded; |
||
105 | |||
106 | ///<summary>Raises the InventoryObjectAdded Event</summary> |
||
107 | /// <param name="e">A InventoryObjectAddedEventArgs object containing |
||
108 | /// the data sent from the simulator</param> |
||
109 | protected virtual void OnInventoryObjectAdded(InventoryObjectAddedEventArgs e) |
||
110 | { |
||
111 | EventHandler<InventoryObjectAddedEventArgs> handler = m_InventoryObjectAdded; |
||
112 | if (handler != null) |
||
113 | handler(this, e); |
||
114 | } |
||
115 | |||
116 | /// <summary>Thread sync lock object</summary> |
||
117 | private readonly object m_InventoryObjectAddedLock = new object(); |
||
118 | |||
119 | /// <summary>Raised when the simulator sends us data containing |
||
120 | /// ...</summary> |
||
121 | public event EventHandler<InventoryObjectAddedEventArgs> InventoryObjectAdded |
||
122 | { |
||
123 | add { lock (m_InventoryObjectAddedLock) { m_InventoryObjectAdded += value; } } |
||
124 | remove { lock (m_InventoryObjectAddedLock) { m_InventoryObjectAdded -= value; } } |
||
125 | } |
||
126 | |||
127 | /// <summary> |
||
128 | /// The root folder of this avatars inventory |
||
129 | /// </summary> |
||
130 | public InventoryFolder RootFolder |
||
131 | { |
||
132 | get { return RootNode.Data as InventoryFolder; } |
||
133 | set |
||
134 | { |
||
135 | UpdateNodeFor(value); |
||
136 | _RootNode = Items[value.UUID]; |
||
137 | } |
||
138 | } |
||
139 | |||
140 | /// <summary> |
||
141 | /// The default shared library folder |
||
142 | /// </summary> |
||
143 | public InventoryFolder LibraryFolder |
||
144 | { |
||
145 | get { return LibraryRootNode.Data as InventoryFolder; } |
||
146 | set |
||
147 | { |
||
148 | UpdateNodeFor(value); |
||
149 | _LibraryRootNode = Items[value.UUID]; |
||
150 | } |
||
151 | } |
||
152 | |||
153 | private InventoryNode _LibraryRootNode; |
||
154 | private InventoryNode _RootNode; |
||
155 | |||
156 | /// <summary> |
||
157 | /// The root node of the avatars inventory |
||
158 | /// </summary> |
||
159 | public InventoryNode RootNode |
||
160 | { |
||
161 | get { return _RootNode; } |
||
162 | } |
||
163 | |||
164 | /// <summary> |
||
165 | /// The root node of the default shared library |
||
166 | /// </summary> |
||
167 | public InventoryNode LibraryRootNode |
||
168 | { |
||
169 | get { return _LibraryRootNode; } |
||
170 | } |
||
171 | |||
172 | public UUID Owner { |
||
173 | get { return _Owner; } |
||
174 | } |
||
175 | |||
176 | private UUID _Owner; |
||
177 | |||
178 | private GridClient Client; |
||
179 | //private InventoryManager Manager; |
||
180 | public Dictionary<UUID, InventoryNode> Items = new Dictionary<UUID, InventoryNode>(); |
||
181 | |||
182 | public Inventory(GridClient client, InventoryManager manager) |
||
183 | : this(client, manager, client.Self.AgentID) { } |
||
184 | |||
185 | public Inventory(GridClient client, InventoryManager manager, UUID owner) |
||
186 | { |
||
187 | Client = client; |
||
188 | //Manager = manager; |
||
189 | _Owner = owner; |
||
190 | if (owner == UUID.Zero) |
||
191 | Logger.Log("Inventory owned by nobody!", Helpers.LogLevel.Warning, Client); |
||
192 | Items = new Dictionary<UUID, InventoryNode>(); |
||
193 | } |
||
194 | |||
195 | public List<InventoryBase> GetContents(InventoryFolder folder) |
||
196 | { |
||
197 | return GetContents(folder.UUID); |
||
198 | } |
||
199 | |||
200 | /// <summary> |
||
201 | /// Returns the contents of the specified folder |
||
202 | /// </summary> |
||
203 | /// <param name="folder">A folder's UUID</param> |
||
204 | /// <returns>The contents of the folder corresponding to <code>folder</code></returns> |
||
205 | /// <exception cref="InventoryException">When <code>folder</code> does not exist in the inventory</exception> |
||
206 | public List<InventoryBase> GetContents(UUID folder) |
||
207 | { |
||
208 | InventoryNode folderNode; |
||
209 | if (!Items.TryGetValue(folder, out folderNode)) |
||
210 | throw new InventoryException("Unknown folder: " + folder); |
||
211 | lock (folderNode.Nodes.SyncRoot) |
||
212 | { |
||
213 | List<InventoryBase> contents = new List<InventoryBase>(folderNode.Nodes.Count); |
||
214 | foreach (InventoryNode node in folderNode.Nodes.Values) |
||
215 | { |
||
216 | contents.Add(node.Data); |
||
217 | } |
||
218 | return contents; |
||
219 | } |
||
220 | } |
||
221 | |||
222 | /// <summary> |
||
223 | /// Updates the state of the InventoryNode and inventory data structure that |
||
224 | /// is responsible for the InventoryObject. If the item was previously not added to inventory, |
||
225 | /// it adds the item, and updates structure accordingly. If it was, it updates the |
||
226 | /// InventoryNode, changing the parent node if <code>item.parentUUID</code> does |
||
227 | /// not match <code>node.Parent.Data.UUID</code>. |
||
228 | /// |
||
229 | /// You can not set the inventory root folder using this method |
||
230 | /// </summary> |
||
231 | /// <param name="item">The InventoryObject to store</param> |
||
232 | public void UpdateNodeFor(InventoryBase item) |
||
233 | { |
||
234 | lock (Items) |
||
235 | { |
||
236 | InventoryNode itemParent = null; |
||
237 | if (item.ParentUUID != UUID.Zero && !Items.TryGetValue(item.ParentUUID, out itemParent)) |
||
238 | { |
||
239 | // OK, we have no data on the parent, let's create a fake one. |
||
240 | InventoryFolder fakeParent = new InventoryFolder(item.ParentUUID); |
||
241 | fakeParent.DescendentCount = 1; // Dear god, please forgive me. |
||
242 | itemParent = new InventoryNode(fakeParent); |
||
243 | Items[item.ParentUUID] = itemParent; |
||
244 | // Unfortunately, this breaks the nice unified tree |
||
245 | // while we're waiting for the parent's data to come in. |
||
246 | // As soon as we get the parent, the tree repairs itself. |
||
247 | //Logger.DebugLog("Attempting to update inventory child of " + |
||
248 | // item.ParentUUID.ToString() + " when we have no local reference to that folder", Client); |
||
249 | |||
250 | if (Client.Settings.FETCH_MISSING_INVENTORY) |
||
251 | { |
||
252 | // Fetch the parent |
||
253 | List<UUID> fetchreq = new List<UUID>(1); |
||
254 | fetchreq.Add(item.ParentUUID); |
||
255 | } |
||
256 | } |
||
257 | |||
258 | InventoryNode itemNode; |
||
259 | if (Items.TryGetValue(item.UUID, out itemNode)) // We're updating. |
||
260 | { |
||
261 | InventoryNode oldParent = itemNode.Parent; |
||
262 | // Handle parent change |
||
263 | if (oldParent == null || itemParent == null || itemParent.Data.UUID != oldParent.Data.UUID) |
||
264 | { |
||
265 | if (oldParent != null) |
||
266 | { |
||
267 | lock (oldParent.Nodes.SyncRoot) |
||
268 | oldParent.Nodes.Remove(item.UUID); |
||
269 | } |
||
270 | if (itemParent != null) |
||
271 | { |
||
272 | lock (itemParent.Nodes.SyncRoot) |
||
273 | itemParent.Nodes[item.UUID] = itemNode; |
||
274 | } |
||
275 | } |
||
276 | |||
277 | itemNode.Parent = itemParent; |
||
278 | |||
279 | if (m_InventoryObjectUpdated != null) |
||
280 | { |
||
281 | OnInventoryObjectUpdated(new InventoryObjectUpdatedEventArgs(itemNode.Data, item)); |
||
282 | } |
||
283 | |||
284 | itemNode.Data = item; |
||
285 | } |
||
286 | else // We're adding. |
||
287 | { |
||
288 | itemNode = new InventoryNode(item, itemParent); |
||
289 | Items.Add(item.UUID, itemNode); |
||
290 | if (m_InventoryObjectAdded != null) |
||
291 | { |
||
292 | OnInventoryObjectAdded(new InventoryObjectAddedEventArgs(item)); |
||
293 | } |
||
294 | } |
||
295 | } |
||
296 | } |
||
297 | |||
298 | public InventoryNode GetNodeFor(UUID uuid) |
||
299 | { |
||
300 | return Items[uuid]; |
||
301 | } |
||
302 | |||
303 | /// <summary> |
||
304 | /// Removes the InventoryObject and all related node data from Inventory. |
||
305 | /// </summary> |
||
306 | /// <param name="item">The InventoryObject to remove.</param> |
||
307 | public void RemoveNodeFor(InventoryBase item) |
||
308 | { |
||
309 | lock (Items) |
||
310 | { |
||
311 | InventoryNode node; |
||
312 | if (Items.TryGetValue(item.UUID, out node)) |
||
313 | { |
||
314 | if (node.Parent != null) |
||
315 | lock (node.Parent.Nodes.SyncRoot) |
||
316 | node.Parent.Nodes.Remove(item.UUID); |
||
317 | Items.Remove(item.UUID); |
||
318 | if (m_InventoryObjectRemoved != null) |
||
319 | { |
||
320 | OnInventoryObjectRemoved(new InventoryObjectRemovedEventArgs(item)); |
||
321 | } |
||
322 | } |
||
323 | |||
324 | // In case there's a new parent: |
||
325 | InventoryNode newParent; |
||
326 | if (Items.TryGetValue(item.ParentUUID, out newParent)) |
||
327 | { |
||
328 | lock (newParent.Nodes.SyncRoot) |
||
329 | newParent.Nodes.Remove(item.UUID); |
||
330 | } |
||
331 | } |
||
332 | } |
||
333 | |||
334 | /// <summary> |
||
335 | /// Used to find out if Inventory contains the InventoryObject |
||
336 | /// specified by <code>uuid</code>. |
||
337 | /// </summary> |
||
338 | /// <param name="uuid">The UUID to check.</param> |
||
339 | /// <returns>true if inventory contains uuid, false otherwise</returns> |
||
340 | public bool Contains(UUID uuid) |
||
341 | { |
||
342 | return Items.ContainsKey(uuid); |
||
343 | } |
||
344 | |||
345 | public bool Contains(InventoryBase obj) |
||
346 | { |
||
347 | return Contains(obj.UUID); |
||
348 | } |
||
349 | |||
350 | /// <summary> |
||
351 | /// Saves the current inventory structure to a cache file |
||
352 | /// </summary> |
||
353 | /// <param name="filename">Name of the cache file to save to</param> |
||
354 | public void SaveToDisk(string filename) |
||
355 | { |
||
356 | try |
||
357 | { |
||
358 | using (Stream stream = File.Open(filename, FileMode.Create)) |
||
359 | { |
||
360 | BinaryFormatter bformatter = new BinaryFormatter(); |
||
361 | lock (Items) |
||
362 | { |
||
363 | Logger.Log("Caching " + Items.Count.ToString() + " inventory items to " + filename, Helpers.LogLevel.Info); |
||
364 | foreach (KeyValuePair<UUID, InventoryNode> kvp in Items) |
||
365 | { |
||
366 | bformatter.Serialize(stream, kvp.Value); |
||
367 | } |
||
368 | } |
||
369 | } |
||
370 | } |
||
371 | catch (Exception e) |
||
372 | { |
||
373 | Logger.Log("Error saving inventory cache to disk :"+e.Message,Helpers.LogLevel.Error); |
||
374 | } |
||
375 | } |
||
376 | |||
377 | /// <summary> |
||
378 | /// Loads in inventory cache file into the inventory structure. Note only valid to call after login has been successful. |
||
379 | /// </summary> |
||
380 | /// <param name="filename">Name of the cache file to load</param> |
||
381 | /// <returns>The number of inventory items sucessfully reconstructed into the inventory node tree</returns> |
||
382 | public int RestoreFromDisk(string filename) |
||
383 | { |
||
384 | List<InventoryNode> nodes = new List<InventoryNode>(); |
||
385 | int item_count = 0; |
||
386 | |||
387 | try |
||
388 | { |
||
389 | if (!File.Exists(filename)) |
||
390 | return -1; |
||
391 | |||
392 | using (Stream stream = File.Open(filename, FileMode.Open)) |
||
393 | { |
||
394 | BinaryFormatter bformatter = new BinaryFormatter(); |
||
395 | |||
396 | while (stream.Position < stream.Length) |
||
397 | { |
||
398 | OpenMetaverse.InventoryNode node = (InventoryNode)bformatter.Deserialize(stream); |
||
399 | nodes.Add(node); |
||
400 | item_count++; |
||
401 | } |
||
402 | } |
||
403 | } |
||
404 | catch (Exception e) |
||
405 | { |
||
406 | Logger.Log("Error accessing inventory cache file :" + e.Message, Helpers.LogLevel.Error); |
||
407 | return -1; |
||
408 | } |
||
409 | |||
410 | Logger.Log("Read " + item_count.ToString() + " items from inventory cache file", Helpers.LogLevel.Info); |
||
411 | |||
412 | item_count = 0; |
||
413 | List<InventoryNode> del_nodes = new List<InventoryNode>(); //nodes that we have processed and will delete |
||
414 | List<UUID> dirty_folders = new List<UUID>(); // Tainted folders that we will not restore items into |
||
415 | |||
416 | // Because we could get child nodes before parents we must itterate around and only add nodes who have |
||
417 | // a parent already in the list because we must update both child and parent to link together |
||
418 | // But sometimes we have seen orphin nodes due to bad/incomplete data when caching so we have an emergency abort route |
||
419 | int stuck = 0; |
||
420 | |||
421 | while (nodes.Count != 0 && stuck<5) |
||
422 | { |
||
423 | foreach (InventoryNode node in nodes) |
||
424 | { |
||
425 | InventoryNode pnode; |
||
426 | if (node.ParentID == UUID.Zero) |
||
427 | { |
||
428 | //We don't need the root nodes "My Inventory" etc as they will already exist for the correct |
||
429 | // user of this cache. |
||
430 | del_nodes.Add(node); |
||
431 | item_count--; |
||
432 | } |
||
433 | else if(Items.TryGetValue(node.Data.UUID,out pnode)) |
||
434 | { |
||
435 | //We already have this it must be a folder |
||
436 | if (node.Data is InventoryFolder) |
||
437 | { |
||
438 | InventoryFolder cache_folder = (InventoryFolder)node.Data; |
||
439 | InventoryFolder server_folder = (InventoryFolder)pnode.Data; |
||
440 | |||
441 | if (cache_folder.Version != server_folder.Version) |
||
442 | { |
||
443 | Logger.DebugLog("Inventory Cache/Server version mismatch on " + node.Data.Name + " " + cache_folder.Version.ToString() + " vs " + server_folder.Version.ToString()); |
||
444 | pnode.NeedsUpdate = true; |
||
445 | dirty_folders.Add(node.Data.UUID); |
||
446 | } |
||
447 | else |
||
448 | { |
||
449 | pnode.NeedsUpdate = false; |
||
450 | } |
||
451 | |||
452 | del_nodes.Add(node); |
||
453 | } |
||
454 | } |
||
455 | else if (Items.TryGetValue(node.ParentID, out pnode)) |
||
456 | { |
||
457 | if (node.Data != null) |
||
458 | { |
||
459 | // If node is folder, and it does not exist in skeleton, mark it as |
||
460 | // dirty and don't process nodes that belong to it |
||
461 | if (node.Data is InventoryFolder && !(Items.ContainsKey(node.Data.UUID))) |
||
462 | { |
||
463 | dirty_folders.Add(node.Data.UUID); |
||
464 | } |
||
465 | |||
466 | //Only add new items, this is most likely to be run at login time before any inventory |
||
467 | //nodes other than the root are populated. Don't add non existing folders. |
||
468 | if (!Items.ContainsKey(node.Data.UUID) && !dirty_folders.Contains(pnode.Data.UUID) && !(node.Data is InventoryFolder)) |
||
469 | { |
||
470 | Items.Add(node.Data.UUID, node); |
||
471 | node.Parent = pnode; //Update this node with its parent |
||
472 | pnode.Nodes.Add(node.Data.UUID, node); // Add to the parents child list |
||
473 | item_count++; |
||
474 | } |
||
475 | } |
||
476 | |||
477 | del_nodes.Add(node); |
||
478 | } |
||
479 | } |
||
480 | |||
481 | if (del_nodes.Count == 0) |
||
482 | stuck++; |
||
483 | else |
||
484 | stuck = 0; |
||
485 | |||
486 | //Clean up processed nodes this loop around. |
||
487 | foreach (InventoryNode node in del_nodes) |
||
488 | nodes.Remove(node); |
||
489 | |||
490 | del_nodes.Clear(); |
||
491 | } |
||
492 | |||
493 | Logger.Log("Reassembled " + item_count.ToString() + " items from inventory cache file", Helpers.LogLevel.Info); |
||
494 | return item_count; |
||
495 | } |
||
496 | |||
497 | #region Operators |
||
498 | |||
499 | /// <summary> |
||
500 | /// By using the bracket operator on this class, the program can get the |
||
501 | /// InventoryObject designated by the specified uuid. If the value for the corresponding |
||
502 | /// UUID is null, the call is equivelant to a call to <code>RemoveNodeFor(this[uuid])</code>. |
||
503 | /// If the value is non-null, it is equivelant to a call to <code>UpdateNodeFor(value)</code>, |
||
504 | /// the uuid parameter is ignored. |
||
505 | /// </summary> |
||
506 | /// <param name="uuid">The UUID of the InventoryObject to get or set, ignored if set to non-null value.</param> |
||
507 | /// <returns>The InventoryObject corresponding to <code>uuid</code>.</returns> |
||
508 | public InventoryBase this[UUID uuid] |
||
509 | { |
||
510 | get |
||
511 | { |
||
512 | InventoryNode node = Items[uuid]; |
||
513 | return node.Data; |
||
514 | } |
||
515 | set |
||
516 | { |
||
517 | if (value != null) |
||
518 | { |
||
519 | // Log a warning if there is a UUID mismatch, this will cause problems |
||
520 | if (value.UUID != uuid) |
||
521 | Logger.Log("Inventory[uuid]: uuid " + uuid.ToString() + " is not equal to value.UUID " + |
||
522 | value.UUID.ToString(), Helpers.LogLevel.Warning, Client); |
||
523 | |||
524 | UpdateNodeFor(value); |
||
525 | } |
||
526 | else |
||
527 | { |
||
528 | InventoryNode node; |
||
529 | if (Items.TryGetValue(uuid, out node)) |
||
530 | { |
||
531 | RemoveNodeFor(node.Data); |
||
532 | } |
||
533 | } |
||
534 | } |
||
535 | } |
||
536 | |||
537 | #endregion Operators |
||
538 | |||
539 | } |
||
540 | #region EventArgs classes |
||
541 | |||
542 | public class InventoryObjectUpdatedEventArgs : EventArgs |
||
543 | { |
||
544 | private readonly InventoryBase m_OldObject; |
||
545 | private readonly InventoryBase m_NewObject; |
||
546 | |||
547 | public InventoryBase OldObject { get { return m_OldObject; } } |
||
548 | public InventoryBase NewObject { get { return m_NewObject; } } |
||
549 | |||
550 | public InventoryObjectUpdatedEventArgs(InventoryBase oldObject, InventoryBase newObject) |
||
551 | { |
||
552 | this.m_OldObject = oldObject; |
||
553 | this.m_NewObject = newObject; |
||
554 | } |
||
555 | } |
||
556 | |||
557 | public class InventoryObjectRemovedEventArgs : EventArgs |
||
558 | { |
||
559 | private readonly InventoryBase m_Obj; |
||
560 | |||
561 | public InventoryBase Obj { get { return m_Obj; } } |
||
562 | public InventoryObjectRemovedEventArgs(InventoryBase obj) |
||
563 | { |
||
564 | this.m_Obj = obj; |
||
565 | } |
||
566 | } |
||
567 | |||
568 | public class InventoryObjectAddedEventArgs : EventArgs |
||
569 | { |
||
570 | private readonly InventoryBase m_Obj; |
||
571 | |||
572 | public InventoryBase Obj { get { return m_Obj; } } |
||
573 | |||
574 | public InventoryObjectAddedEventArgs(InventoryBase obj) |
||
575 | { |
||
576 | this.m_Obj = obj; |
||
577 | } |
||
578 | } |
||
579 | #endregion EventArgs |
||
580 | } |