< Summary

Class:WearableItem
Assembly:AvatarAssets
File(s):/tmp/workspace/unity-renderer/unity-renderer/Assets/Scripts/MainScripts/DCL/Models/AvatarAssets/WearableItem.cs
Covered lines:61
Uncovered lines:7
Coverable lines:68
Total lines:277
Line coverage:89.7% (61 of 68)
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%6200%
SupportsBodyShape(...)0%6.076087.5%
GetReplacesList(...)0%550100%
GetHidesList(...)0%770100%
DoesHide(...)0%110100%
IsCollectible()0%2100%
IsSkin()0%110100%
IsSmart()0%9.089090%
GetName(...)0%440100%
GetIssuedCountFromRarity(...)0%7.237083.33%
ComposeThumbnailUrl()0%110100%
ComposeHiddenCategories(...)0%440100%
IsInL2()0%3.333066.67%

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;
 5
 6[Serializable]
 7public class WearableItem
 8{
 9    [Serializable]
 10    public class MappingPair
 11    {
 12        public string key;
 13        public string hash;
 14    }
 15
 16    [Serializable]
 17    public class Representation
 18    {
 19        public string[] bodyShapes;
 20        public string mainFile;
 21        public MappingPair[] contents;
 22        public string[] overrideHides;
 23        public string[] overrideReplaces;
 24    }
 25
 26    [Serializable]
 27    public class Data
 28    {
 29        public Representation[] representations;
 30        public string category;
 31        public string[] tags;
 32        public string[] replaces;
 33        public string[] hides;
 34    }
 35
 36    public Data data;
 37    public string id;
 38
 39    public string baseUrl;
 40    public string baseUrlBundles;
 41
 42    public i18n[] i18n;
 43    public string thumbnail;
 44
 45    //This fields are temporary, once Kernel is finished we must move them to wherever they are placed
 46    public string rarity;
 47    public string description;
 48    public int issuedId;
 49
 322150    private readonly Dictionary<string, string> cachedI18n = new Dictionary<string, string>();
 322151    private readonly Dictionary<string, ContentProvider> cachedContentProviers =
 52        new Dictionary<string, ContentProvider>();
 53
 322154    private readonly string[] skinImplicitCategories =
 55    {
 56        WearableLiterals.Categories.EYES,
 57        WearableLiterals.Categories.MOUTH,
 58        WearableLiterals.Categories.EYEBROWS,
 59        WearableLiterals.Categories.HAIR,
 60        WearableLiterals.Categories.UPPER_BODY,
 61        WearableLiterals.Categories.LOWER_BODY,
 62        WearableLiterals.Categories.FEET,
 63        WearableLiterals.Misc.HEAD,
 64        WearableLiterals.Categories.FACIAL_HAIR
 65    };
 66
 67    public bool TryGetRepresentation(string bodyshapeId, out Representation representation)
 68    {
 57669        representation = GetRepresentation(bodyshapeId);
 57670        return representation != null;
 71    }
 72
 73    public Representation GetRepresentation(string bodyShapeType)
 74    {
 508675        if (data?.representations == null)
 276            return null;
 77
 1036278        for (int i = 0; i < data.representations.Length; i++)
 79        {
 517980            if (data.representations[i].bodyShapes.Contains(bodyShapeType))
 81            {
 508282                return data.representations[i];
 83            }
 84        }
 85
 286        return null;
 87    }
 88
 89    public ContentProvider GetContentProvider(string bodyShapeType)
 90    {
 10191        var representation = GetRepresentation(bodyShapeType);
 92
 10193        if (representation == null)
 094            return null;
 95
 10196        if (!cachedContentProviers.ContainsKey(bodyShapeType))
 97        {
 7298            var contentProvider = CreateContentProvider(baseUrl, representation.contents);
 7299            contentProvider.BakeHashes();
 72100            cachedContentProviers.Add(bodyShapeType, contentProvider);
 101        }
 102
 101103        return cachedContentProviers[bodyShapeType];
 104    }
 105
 106    protected virtual ContentProvider CreateContentProvider(string baseUrl, MappingPair[] contents)
 107    {
 0108        return new ContentProvider
 109        {
 110            baseUrl = baseUrl,
 0111            contents = contents.Select(mapping => new ContentServerUtils.MappingPair()
 112                {file = mapping.key, hash = mapping.hash}).ToList()
 113        };
 114    }
 115
 116    public bool SupportsBodyShape(string bodyShapeType)
 117    {
 3740118        if (data?.representations == null)
 0119            return false;
 120
 10096121        for (int i = 0; i < data.representations.Length; i++)
 122        {
 4153123            if (data.representations[i].bodyShapes.Contains(bodyShapeType))
 124            {
 2845125                return true;
 126            }
 127        }
 128
 895129        return false;
 130    }
 131
 132    public string[] GetReplacesList(string bodyShapeType)
 133    {
 3711134        var representation = GetRepresentation(bodyShapeType);
 135
 3711136        if (representation?.overrideReplaces == null || representation.overrideReplaces.Length == 0)
 3709137            return data.replaces;
 138
 2139        return representation.overrideReplaces;
 140    }
 141
 142    public string[] GetHidesList(string bodyShapeType)
 143    {
 595144        var representation = GetRepresentation(bodyShapeType);
 145
 146        string[] hides;
 147
 595148        if (representation?.overrideHides == null || representation.overrideHides.Length == 0)
 594149            hides = data.hides;
 150        else
 1151            hides = representation.overrideHides;
 152
 595153        if (IsSkin())
 154        {
 11155            hides = hides == null
 156                ? skinImplicitCategories
 157                : hides.Concat(skinImplicitCategories).Distinct().ToArray();
 158        }
 159
 595160        return hides;
 161    }
 162
 54163    public bool DoesHide(string category, string bodyShape) => GetHidesList(bodyShape).Any(s => s == category);
 164
 165    public bool IsCollectible()
 166    {
 0167        return !string.IsNullOrEmpty(rarity);
 168    }
 169
 675170    public bool IsSkin() => data.category == WearableLiterals.Categories.SKIN;
 171
 172    public bool IsSmart()
 173    {
 36174        if (data?.representations == null) return false;
 175
 140176        for (var i = 0; i < data.representations.Length; i++)
 177        {
 36178            var representation = data.representations[i];
 98179            var containsGameJs = representation.contents?.Any(pair => pair.key.EndsWith("game.js")) ?? false;
 38180            if (containsGameJs) return true;
 181        }
 182
 34183        return false;
 184    }
 185
 186    public string GetName(string langCode = "en")
 187    {
 37188        if (!cachedI18n.ContainsKey(langCode))
 189        {
 36190            cachedI18n.Add(langCode, i18n.FirstOrDefault(x => x.code == langCode)?.text);
 191        }
 192
 37193        return cachedI18n[langCode];
 194    }
 195
 196    public int GetIssuedCountFromRarity(string rarity)
 197    {
 198        switch (rarity)
 199        {
 200            case WearableLiterals.ItemRarity.RARE:
 4201                return 5000;
 202            case WearableLiterals.ItemRarity.EPIC:
 27203                return 1000;
 204            case WearableLiterals.ItemRarity.LEGENDARY:
 2205                return 100;
 206            case WearableLiterals.ItemRarity.MYTHIC:
 2207                return 10;
 208            case WearableLiterals.ItemRarity.UNIQUE:
 2209                return 1;
 210        }
 211
 0212        return int.MaxValue;
 213    }
 214
 215    public string ComposeThumbnailUrl()
 216    {
 198217        return baseUrl + thumbnail;
 218    }
 219
 220    public static HashSet<string> ComposeHiddenCategories(string bodyShapeId, WearableItem[] wearables)
 221    {
 78222        HashSet<string> result = new HashSet<string>();
 223        //Last wearable added has priority over the rest
 1298224        for (int index = 0; index < wearables.Length; index++)
 225        {
 571226            WearableItem wearableItem = wearables[index];
 571227            if (result.Contains(wearableItem.data.category)) //Skip hidden elements to avoid two elements hiding each ot
 228                continue;
 229
 571230            string[] wearableHidesList = wearableItem.GetHidesList(bodyShapeId);
 571231            if (wearableHidesList != null)
 232            {
 562233                result.UnionWith(wearableHidesList);
 234            }
 235        }
 236
 78237        return result;
 238    }
 239
 240    //Workaround to know the net of a wearable.
 241    //Once wearables are allowed to be moved from Ethereum to Polygon this method wont be reliable anymore
 242    //To retrieve this properly first we need the catalyst to send the net of each wearable, not just the ID
 243    public bool IsInL2()
 244    {
 36245        if (id.StartsWith("urn:decentraland:matic") || id.StartsWith("urn:decentraland:mumbai"))
 0246            return true;
 36247        return false;
 248    }
 249}
 250
 251[Serializable]
 252public class WearablesRequestResponse
 253{
 254    public WearableItem[] wearables;
 255    public string context;
 256}
 257
 258[Serializable]
 259public class WearablesRequestFailed
 260{
 261    public string error;
 262    public string context;
 263}
 264
 265[Serializable]
 266public class WearableContent
 267{
 268    public string file;
 269    public string hash;
 270}
 271
 272[Serializable]
 273public class i18n
 274{
 275    public string code;
 276    public string text;
 277}