< Summary

Class:WearableItem
Assembly:AvatarAssets
File(s):/tmp/workspace/unity-renderer/unity-renderer/Assets/Scripts/MainScripts/DCL/Models/AvatarAssets/WearableItem.cs
Covered lines:75
Uncovered lines:11
Coverable lines:86
Total lines:319
Line coverage:87.2% (75 of 86)
Covered branches:0
Total branches:0

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity NPath complexity Sequence coverage
WearableItem()0%110100%
TryGetRepresentation(...)0%110100%
GetRepresentation(...)0%660100%
GetContentProvider(...)0%3.023087.5%
CreateContentProvider(...)0%220100%
SupportsBodyShape(...)0%6.076087.5%
GetReplacesList(...)0%550100%
GetHidesList(...)0%770100%
SanitizeHidesLists()0%440100%
DoesHide(...)0%110100%
IsCollectible()0%2.152066.67%
IsSkin()0%110100%
IsSmart()0%9.089090%
GetName(...)0%440100%
GetIssuedCountFromRarity(...)0%770100%
ComposeThumbnailUrl()0%110100%
ComposeHiddenCategories(...)0%4.024090%
IsInL2()0%3.333066.67%
IsEmote()0%2100%
ToString()0%2100%

File(s)

/tmp/workspace/unity-renderer/unity-renderer/Assets/Scripts/MainScripts/DCL/Models/AvatarAssets/WearableItem.cs

#LineLine coverage
 1using System;
 2using System.Collections.Generic;
 3using System.Linq;
 4using DCL;
 5using DCL.Emotes;
 6using UnityEngine;
 7
 8[Serializable]
 9public class WearableItem
 10{
 11    private const string THIRD_PARTY_COLLECTIONS_PATH = "collections-thirdparty";
 12
 13    [Serializable]
 14    public class MappingPair
 15    {
 16        public string key;
 17        public string hash;
 18    }
 19
 20    [Serializable]
 21    public class Representation
 22    {
 23        public string[] bodyShapes;
 24        public string mainFile;
 25        public MappingPair[] contents;
 26        public string[] overrideHides;
 27        public string[] overrideReplaces;
 28    }
 29
 30    [Serializable]
 31    public class Data
 32    {
 33        public Representation[] representations;
 34        public string category;
 35        public string[] tags;
 36        public string[] replaces;
 37        public string[] hides;
 38    }
 39
 40    public Data data;
 41    public EmoteDataV0 emoteDataV0;
 42    public string id;
 43
 44    public string baseUrl;
 45    public string baseUrlBundles;
 46
 47    public i18n[] i18n;
 48    public string thumbnail;
 49
 50    private string thirdPartyCollectionId;
 51    public string ThirdPartyCollectionId
 52    {
 53        get
 54        {
 843955            if (!string.IsNullOrEmpty(thirdPartyCollectionId)) return thirdPartyCollectionId;
 1687856            if (!id.Contains(THIRD_PARTY_COLLECTIONS_PATH)) return "";
 057            var paths = id.Split(':');
 058            var thirdPartyIndex = Array.IndexOf(paths, THIRD_PARTY_COLLECTIONS_PATH);
 059            thirdPartyCollectionId = string.Join(":", paths, 0, thirdPartyIndex + 2);
 060            return thirdPartyCollectionId;
 61        }
 62    }
 63
 843064    public bool IsFromThirdPartyCollection => !string.IsNullOrEmpty(ThirdPartyCollectionId);
 65
 66    public Sprite thumbnailSprite;
 67
 68    //This fields are temporary, once Kernel is finished we must move them to wherever they are placed
 69    public string rarity;
 70    public string description;
 71    public int issuedId;
 72
 365773    private readonly Dictionary<string, string> cachedI18n = new Dictionary<string, string>();
 365774    private readonly Dictionary<string, ContentProvider> cachedContentProviers =
 75        new Dictionary<string, ContentProvider>();
 76
 365777    private readonly string[] skinImplicitCategories =
 78    {
 79        WearableLiterals.Categories.EYES,
 80        WearableLiterals.Categories.MOUTH,
 81        WearableLiterals.Categories.EYEBROWS,
 82        WearableLiterals.Categories.HAIR,
 83        WearableLiterals.Categories.UPPER_BODY,
 84        WearableLiterals.Categories.LOWER_BODY,
 85        WearableLiterals.Categories.FEET,
 86        WearableLiterals.Misc.HEAD,
 87        WearableLiterals.Categories.FACIAL_HAIR
 88    };
 89
 90    public bool TryGetRepresentation(string bodyshapeId, out Representation representation)
 91    {
 1492        representation = GetRepresentation(bodyshapeId);
 1493        return representation != null;
 94    }
 95
 96    public Representation GetRepresentation(string bodyShapeType)
 97    {
 434898        if (data?.representations == null)
 399            return null;
 100
 8818101        for (int i = 0; i < data.representations.Length; i++)
 102        {
 4407103            if (data.representations[i].bodyShapes.Contains(bodyShapeType))
 104            {
 4343105                return data.representations[i];
 106            }
 107        }
 108
 2109        return null;
 110    }
 111
 112    public ContentProvider GetContentProvider(string bodyShapeType)
 113    {
 26114        var representation = GetRepresentation(bodyShapeType);
 115
 26116        if (representation == null)
 0117            return null;
 118
 26119        if (!cachedContentProviers.ContainsKey(bodyShapeType))
 120        {
 23121            var contentProvider = CreateContentProvider(baseUrl, representation.contents);
 23122            contentProvider.BakeHashes();
 23123            cachedContentProviers.Add(bodyShapeType, contentProvider);
 124        }
 125
 26126        return cachedContentProviers[bodyShapeType];
 127    }
 128
 129    protected virtual ContentProvider CreateContentProvider(string baseUrl, MappingPair[] contents)
 130    {
 1131        return new ContentProvider
 132        {
 133            baseUrl = baseUrl,
 1134            contents = contents.Select(mapping => new ContentServerUtils.MappingPair()
 135                                   { file = mapping.key, hash = mapping.hash })
 136                               .ToList()
 137        };
 138    }
 139
 140    public bool SupportsBodyShape(string bodyShapeType)
 141    {
 6993142        if (data?.representations == null)
 0143            return false;
 144
 23228145        for (int i = 0; i < data.representations.Length; i++)
 146        {
 7785147            if (data.representations[i].bodyShapes.Contains(bodyShapeType))
 148            {
 3164149                return true;
 150            }
 151        }
 152
 3829153        return false;
 154    }
 155
 156    public string[] GetReplacesList(string bodyShapeType)
 157    {
 4235158        var representation = GetRepresentation(bodyShapeType);
 159
 4235160        if (representation?.overrideReplaces == null || representation.overrideReplaces.Length == 0)
 4233161            return data.replaces;
 162
 2163        return representation.overrideReplaces;
 164    }
 165
 166    public string[] GetHidesList(string bodyShapeType)
 167    {
 44168        var representation = GetRepresentation(bodyShapeType);
 169
 170        string[] hides;
 171
 44172        if (representation?.overrideHides == null || representation.overrideHides.Length == 0)
 42173            hides = data.hides;
 174        else
 2175            hides = representation.overrideHides;
 176
 44177        if (IsSkin())
 178        {
 11179            hides = hides == null
 180                ? skinImplicitCategories
 181                : hides.Concat(skinImplicitCategories).Distinct().ToArray();
 182        }
 183
 44184        return hides;
 185    }
 186
 187    public void SanitizeHidesLists()
 188    {
 189        //remove bodyshape from hides list
 3097190        if (data.hides != null)
 3097191            data.hides = data.hides.Except(new [] { WearableLiterals.Categories.BODY_SHAPE }).ToArray();
 14280192        for (int i = 0; i < data.representations.Length; i++)
 193        {
 4043194            Representation representation = data.representations[i];
 4043195            if (representation.overrideHides != null)
 4043196                representation.overrideHides = representation.overrideHides.Except(new [] { WearableLiterals.Categories.
 197
 198        }
 3097199    }
 200
 54201    public bool DoesHide(string category, string bodyShape) => GetHidesList(bodyShape).Any(s => s == category);
 202
 203    public bool IsCollectible()
 204    {
 7951205        if (id == null)
 0206            return false;
 207
 7951208        return !id.StartsWith("urn:decentraland:off-chain:base-avatars:");
 209    }
 210
 124211    public bool IsSkin() => data.category == WearableLiterals.Categories.SKIN;
 212
 213    public bool IsSmart()
 214    {
 2824215        if (data?.representations == null) return false;
 216
 12318217        for (var i = 0; i < data.representations.Length; i++)
 218        {
 3338219            var representation = data.representations[i];
 11903220            var containsGameJs = representation.contents?.Any(pair => pair.key.EndsWith("game.js")) ?? false;
 3341221            if (containsGameJs) return true;
 222        }
 223
 2821224        return false;
 225    }
 226
 227    public string GetName(string langCode = "en")
 228    {
 3742229        if (!cachedI18n.ContainsKey(langCode))
 230        {
 2244231            cachedI18n.Add(langCode, i18n.FirstOrDefault(x => x.code == langCode)?.text);
 232        }
 233
 3742234        return cachedI18n[langCode];
 235    }
 236
 237    public int GetIssuedCountFromRarity(string rarity)
 238    {
 239        switch (rarity)
 240        {
 241            case WearableLiterals.ItemRarity.RARE:
 6242                return 5000;
 243            case WearableLiterals.ItemRarity.EPIC:
 37244                return 1000;
 245            case WearableLiterals.ItemRarity.LEGENDARY:
 3246                return 100;
 247            case WearableLiterals.ItemRarity.MYTHIC:
 3248                return 10;
 249            case WearableLiterals.ItemRarity.UNIQUE:
 3250                return 1;
 251        }
 252
 2764253        return int.MaxValue;
 254    }
 255
 1043256    public string ComposeThumbnailUrl() { return baseUrl + thumbnail; }
 257
 258    public static HashSet<string> ComposeHiddenCategories(string bodyShapeId, List<WearableItem> wearables)
 259    {
 2260        HashSet<string> result = new HashSet<string>();
 261        //Last wearable added has priority over the rest
 22262        for (int index = 0; index < wearables.Count; index++)
 263        {
 9264            WearableItem wearableItem = wearables[index];
 9265            if (result.Contains(wearableItem.data.category)) //Skip hidden elements to avoid two elements hiding each ot
 266                continue;
 267
 9268            string[] wearableHidesList = wearableItem.GetHidesList(bodyShapeId);
 9269            if (wearableHidesList != null)
 270            {
 0271                result.UnionWith(wearableHidesList);
 272            }
 273        }
 274
 2275        return result;
 276    }
 277
 278    //Workaround to know the net of a wearable.
 279    //Once wearables are allowed to be moved from Ethereum to Polygon this method wont be reliable anymore
 280    //To retrieve this properly first we need the catalyst to send the net of each wearable, not just the ID
 281    public bool IsInL2()
 282    {
 3739283        if (id.StartsWith("urn:decentraland:matic") || id.StartsWith("urn:decentraland:mumbai"))
 0284            return true;
 3739285        return false;
 286    }
 287
 0288    public bool IsEmote() { return emoteDataV0 != null; }
 289
 0290    public override string ToString() { return id; }
 291}
 292
 293[Serializable]
 294public class WearablesRequestResponse
 295{
 296    public WearableItem[] wearables;
 297    public string context;
 298}
 299
 300[Serializable]
 301public class WearablesRequestFailed
 302{
 303    public string error;
 304    public string context;
 305}
 306
 307[Serializable]
 308public class WearableContent
 309{
 310    public string file;
 311    public string hash;
 312}
 313
 314[Serializable]
 315public class i18n
 316{
 317    public string code;
 318    public string text;
 319}