diff --git a/LibreMetaverse/AppearanceManager.cs b/LibreMetaverse/AppearanceManager.cs index 42b813bf..fe0fe858 100644 --- a/LibreMetaverse/AppearanceManager.cs +++ b/LibreMetaverse/AppearanceManager.cs @@ -29,6 +29,7 @@ using System.Collections.Generic; using System.Threading; using System.Linq; using LibreMetaverse; +using Microsoft.Collections.Extensions; using OpenMetaverse.Packets; using OpenMetaverse.Imaging; using OpenMetaverse.Assets; @@ -664,7 +665,8 @@ namespace OpenMetaverse WearableType type = WEARABLE_BAKE_MAP[bakedIndex][wearableIndex]; if (type == WearableType.Invalid) continue; - hash = Wearables.GetValues(type, true).Aggregate(hash, (current, worn) => current ^ worn.AssetID); + hash = Wearables.Where(e => e.Key == type) + .SelectMany(e => e.Value).Aggregate(hash, (current, worn) => current ^ worn.AssetID); } if (hash != UUID.Zero) @@ -714,16 +716,15 @@ namespace OpenMetaverse [Obsolete] public UUID GetWearableAsset(WearableType type) { - IList wearableList; - return Wearables.TryGetValue(type, out wearableList) + return Wearables.TryGetValue(type, out var wearableList) ? wearableList.First().AssetID : UUID.Zero; } public IEnumerable GetWearableAssets(WearableType type) { - var wearables = Wearables.GetValues(type, true); - return wearables.Select(wearable => wearable.AssetID).ToList(); + return Wearables.Where(e => e.Key == type).SelectMany(e => e.Value) + .Select(wearable => wearable.AssetID); } /// @@ -765,23 +766,17 @@ namespace OpenMetaverse /// Should existing item on the same point or of the same type be replaced public void AddToOutfit(List wearableItems, bool replace) { - var wearables = new List(); - var attachments = new List(); - - foreach (InventoryItem item in wearableItems) - { - if (item is InventoryWearable wearable) - wearables.Add(wearable); - else if (item is InventoryAttachment || item is InventoryObject) - attachments.Add(item); - } + var wearables = wearableItems.OfType() + .ToList(); + var attachments = wearableItems.Where(item => item is InventoryAttachment || item is InventoryObject) + .ToList(); lock (Wearables) { // Add the given wearables to the wearables collection foreach (InventoryWearable wearableItem in wearables) { - WearableData wd = new WearableData + var wd = new WearableData { AssetID = wearableItem.AssetUUID, AssetType = wearableItem.AssetType, @@ -796,12 +791,12 @@ namespace OpenMetaverse } } - if (attachments.Count > 0) + if (attachments.Any()) { - AddAttachments(attachments, false, replace); + AddAttachments(attachments.ToList(), false, replace); } - if (wearables.Count > 0) + if (wearables.Any()) { SendAgentIsNowWearing(); DelayedRequestSetAppearance(); @@ -826,16 +821,10 @@ namespace OpenMetaverse /// be removed from the outfit public void RemoveFromOutfit(List wearableItems) { - var wearables = new List(); - var attachments = new List(); - - foreach (var item in wearableItems) - { - if (item is InventoryWearable wearable) - wearables.Add(wearable); - else if (item is InventoryAttachment || item is InventoryObject) - attachments.Add(item); - } + var wearables = wearableItems.OfType() + .ToList(); + var attachments = wearableItems.Where(item => item is InventoryAttachment || item is InventoryObject) + .ToList(); bool needSetAppearance = false; lock (Wearables) @@ -846,7 +835,8 @@ namespace OpenMetaverse if (wearable.AssetType != AssetType.Bodypart // Remove if it's not a body part && Wearables.ContainsKey(wearable.WearableType)) // And we have that wearable type { - var worn = Wearables.GetValues(wearable.WearableType, true); + var worn = Wearables.Where(e => e.Key == wearable.WearableType) + .SelectMany(e => e.Value); WearableData wearableData = worn.FirstOrDefault(item => item.ItemID == wearable.UUID); if (wearableData == null) continue; @@ -887,16 +877,10 @@ namespace OpenMetaverse /// if you know what you're doing public void ReplaceOutfit(List wearableItems, bool safe) { - var wearables = new List(); - var attachments = new List(); - - foreach (InventoryItem item in wearableItems) - { - if (item is InventoryWearable wearable) - wearables.Add(wearable); - else if (item is InventoryAttachment || item is InventoryObject) - attachments.Add(item); - } + var wearables = wearableItems.OfType() + .ToList(); + var attachments = wearableItems.Where(item => item is InventoryAttachment || item is InventoryObject) + .ToList(); if (safe) { @@ -979,12 +963,8 @@ namespace OpenMetaverse { lock (Wearables) { - var wearables = new List(); - foreach (var wearableType in Wearables.Values) - { - wearables.AddRange(wearableType); - } - return wearables; + // ToList will copy the IEnumerable + return Wearables.SelectMany(e => e.Value).ToList(); } } @@ -992,9 +972,7 @@ namespace OpenMetaverse { lock (Wearables) { - var wearables = new MultiValueDictionary(); - wearables.Merge(Wearables); - return wearables; + return new MultiValueDictionary(Wearables); } } @@ -1009,8 +987,7 @@ namespace OpenMetaverse /// new list of items, false to add these items to the existing outfit public void WearOutfit(List wearables, bool replaceItems) { - var wearableItems = new List(wearables.Count); - wearableItems.AddRange(wearables.OfType()); + var wearableItems = wearables.OfType().ToList(); if (replaceItems) ReplaceOutfit(wearableItems); @@ -1022,17 +999,6 @@ namespace OpenMetaverse #region Attachments - /// - /// Adds a list of attachments to our agent - /// - /// A List containing the attachments to add - /// If true, tells simulator to remove existing attachment - /// first - public void AddAttachments(List attachments, bool removeExistingFirst) - { - AddAttachments(attachments, removeExistingFirst, true); - } - /// /// Adds a list of attachments to our agent /// @@ -1040,7 +1006,7 @@ namespace OpenMetaverse /// If true, tells simulator to remove existing attachment /// If true replace existing attachment on this attachment point, otherwise add to it (multi-attachments) /// first - public void AddAttachments(List attachments, bool removeExistingFirst, bool replace) + public void AddAttachments(List attachments, bool removeExistingFirst, bool replace = true) { // Use RezMultipleAttachmentsFromInv to clear out current attachments, and attach new ones RezMultipleAttachmentsFromInvPacket attachmentsPacket = new RezMultipleAttachmentsFromInvPacket @@ -1205,15 +1171,14 @@ namespace OpenMetaverse /// The inventory itemID of the item to detach public void Detach(UUID itemID) { - DetachAttachmentIntoInvPacket detach = - new DetachAttachmentIntoInvPacket + var detach = new DetachAttachmentIntoInvPacket + { + ObjectData = { - ObjectData = - { - AgentID = Client.Self.AgentID, - ItemID = itemID - } - }; + AgentID = Client.Self.AgentID, + ItemID = itemID + } + }; Client.Network.SendPacket(detach); } @@ -1246,10 +1211,8 @@ namespace OpenMetaverse { WearableType = (byte) i, // This appears to be hacked on SL server side to support multi-layers - ItemID = Wearables.ContainsKey(type) ? - (Wearables[type].First() != null ? - Wearables[type].First().ItemID - : UUID.Zero) + ItemID = Wearables.ContainsKey(type) ? + (Wearables[type].First()?.ItemID ?? UUID.Zero) : UUID.Zero }; } @@ -1701,27 +1664,16 @@ namespace OpenMetaverse if (index == AvatarTextureIndex.Skirt && !Wearables.ContainsKey(WearableType.Skirt)) continue; - AddTextureDownload(index, textures); + TextureData textureData = Textures[(int)index]; + // Add the textureID to the list if this layer has a valid textureID set, it has not already + // been downloaded, and it is not already in the download list + if (textureData.TextureID != UUID.Zero && textureData.Texture == null && !textures.Contains(textureData.TextureID)) + textures.Add(textureData.TextureID); } return textures; } - /// - /// Helper method to lookup the TextureID for a single layer and add it - /// to a list if it is not already present - /// - /// - /// - private void AddTextureDownload(AvatarTextureIndex index, List textures) - { - TextureData textureData = Textures[(int)index]; - // Add the textureID to the list if this layer has a valid textureID set, it has not already - // been downloaded, and it is not already in the download list - if (textureData.TextureID != UUID.Zero && textureData.Texture == null && !textures.Contains(textureData.TextureID)) - textures.Add(textureData.TextureID); - } - /// /// Blocking method to download all of the textures needed for baking /// the given bake layers @@ -1813,7 +1765,7 @@ namespace OpenMetaverse } } - if (pendingBakes.Count > 0) + if (pendingBakes.Any()) { DownloadTextures(pendingBakes); @@ -1831,11 +1783,7 @@ namespace OpenMetaverse { Textures[i].Texture = null; } - - // We just allocated and freed a ridiculous amount of memory while - // baking. Signal to the GC to clean up - GC.Collect(); - + return success; } @@ -2185,7 +2133,8 @@ namespace OpenMetaverse WearableType type = WEARABLE_BAKE_MAP[bakedIndex][wearableIndex]; if (type == WearableType.Invalid) continue; - hash = Wearables.GetValues(type, true).Aggregate(hash, (current, worn) => current ^ worn.AssetID); + hash = Wearables.Where(e => e.Key == type) + .SelectMany(e => e.Value).Aggregate(hash, (current, worn) => current ^ worn.AssetID); } if (hash != UUID.Zero) @@ -2328,7 +2277,7 @@ namespace OpenMetaverse { // HACK: I'm so tired and this is so bad. bool match = false; - foreach (var wearable in Wearables.GetValues(type, true)) + foreach (var wearable in Wearables.Where(w => w.Key == type).SelectMany(w => w.Value)) { if (wearable.AssetID == block.AssetID || wearable.ItemID == block.ItemID) { diff --git a/LibreMetaverse/LibreMetaverse.csproj b/LibreMetaverse/LibreMetaverse.csproj index 7e62f8dc..6f07a77f 100644 --- a/LibreMetaverse/LibreMetaverse.csproj +++ b/LibreMetaverse/LibreMetaverse.csproj @@ -52,6 +52,7 @@ + diff --git a/LibreMetaverse/MultiValueDict.cs b/LibreMetaverse/MultiValueDict.cs deleted file mode 100644 index 14bd08ad..00000000 --- a/LibreMetaverse/MultiValueDict.cs +++ /dev/null @@ -1,141 +0,0 @@ -/* - * Copyright(c) 2008 Solutions Design. - * Copyright(c) 2017 openmetaverse.co. - * All rights reserved. - * - * - Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * - Redistributions of source code must retain the above copyright notice, this - - * list of conditions and the following disclaimer. - * - Neither the name of the openmetaverse.co nor the names - * of its contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT(INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -using System; -using System.Collections.Generic; - -namespace LibreMetaverse -{ - /// - /// Extension to the normal Dictionary. This class can store more than one value for every key. It keeps a HashSet for every Key value. - /// Calling Add with the same Key and multiple values will store each value under the same Key in the Dictionary. Obtaining the values - /// for a Key will return the HashSet with the Values of the Key. - /// - /// The type of the key. - /// The type of the value. - public class MultiValueDictionary : Dictionary> - { - /// - /// Adds the specified value under the specified key - /// - /// The key. - /// The value. - public void Add(TKey key, TValue value) - { - if (key == null) throw new ArgumentNullException(nameof(key)); - IList container; - if (!TryGetValue(key, out container)) - { - container = new List(); - base.Add(key, container); - } - container.Add(value); - } - - - /// - /// Determines whether this dictionary contains the specified value for the specified key - /// - /// The key. - /// The value. - /// true if the value is stored for the specified key in this dictionary, false otherwise - public bool ContainsValue(TKey key, TValue value) - { - if (key == null) throw new ArgumentNullException(nameof(key)); - bool toReturn = false; - IList values; - if (TryGetValue(key, out values)) - { - toReturn = values.Contains(value); - } - return toReturn; - } - - - /// - /// Removes the specified value for the specified key. It will leave the key in the dictionary. - /// - /// The key. - /// The value. - public void Remove(TKey key, TValue value) - { - if (key == null) throw new ArgumentNullException(nameof(key)); - IList container; - if (TryGetValue(key, out container)) - { - container.Remove(value); - if (container.Count <= 0) - { - Remove(key); - } - } - } - - - /// - /// Merges the specified multivaluedictionary into this instance. - /// - /// To merge with. - public void Merge(MultiValueDictionary toMergeWith) - { - if (toMergeWith == null) - { - return; - } - - foreach (var pair in toMergeWith) - { - foreach (TValue value in pair.Value) - { - Add(pair.Key, value); - } - } - } - - - /// - /// Gets the values for the key specified. This method is useful if you want to avoid an exception for key value retrieval and you can't use TryGetValue - /// (e.g. in lambdas) - /// - /// The key. - /// if set to true and the key isn't found, an empty hashset is returned, otherwise, if the key isn't found, null is returned - /// - /// This method will return null (or an empty set if returnEmptySet is true) if the key wasn't found, or - /// the values if key was found. - /// - public IList GetValues(TKey key, bool returnEmptySet) - { - IList toReturn; - if (!TryGetValue(key, out toReturn) && returnEmptySet) - { - toReturn = new List(); - } - return toReturn; - } - } -}