< Summary

Class:CatalogController
Assembly:CatalogController
File(s):/tmp/workspace/unity-renderer/unity-renderer/Assets/Scripts/MainScripts/DCL/Controllers/CatalogController/CatalogController.cs
Covered lines:65
Uncovered lines:89
Coverable lines:154
Total lines:383
Line coverage:42.2% (65 of 154)
Covered branches:0
Total branches:0

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity NPath complexity Sequence coverage
CatalogController()0%110100%
Awake()0%110100%
Update()0%3.13077.78%
Clear()0%110100%
EmbedWearables(...)0%12300%
AddWearablesToCatalog(...)0%20400%
AddWearablesToCatalog(...)0%440100%
AddWearablesToCatalog(...)0%2100%
WearablesRequestFailed(...)0%20400%
RemoveWearablesFromCatalog(...)0%6200%
ClearWearableCatalog()0%6200%
RequestWearable(...)0%4.054085.71%
RequestOwnedWearables(...)0%330100%
RequestBaseWearables()0%12300%
RemoveWearablesInUse(...)0%330100%
ResolvePendingWearablePromise(...)0%5.673033.33%
ResolvePendingWearablesByContextPromise(...)0%12300%
CheckForSendingPendingRequests()0%11.534022.22%
CheckForRequestsTimeOuts()0%20.155015.38%
CheckForRequestsByContextTimeOuts()0%20.155015.38%
CheckForUnusedWearables()0%30500%

File(s)

/tmp/workspace/unity-renderer/unity-renderer/Assets/Scripts/MainScripts/DCL/Controllers/CatalogController/CatalogController.cs

#LineLine coverage
 1using System;
 2using DCL;
 3using DCL.Helpers;
 4using DCL.Interface;
 5using System.Collections.Generic;
 6using DCL.Configuration;
 7using DCL.Emotes;
 8using Newtonsoft.Json;
 9using UnityEngine;
 10
 11public class CatalogController : MonoBehaviour
 12{
 113    public static bool VERBOSE = false;
 14    private const string OWNED_WEARABLES_CONTEXT = "OwnedWearables";
 15    private const string BASE_WEARABLES_CONTEXT = "BaseWearables";
 16    private const int FRAMES_TO_CHECK_FOR_SENDING_PENDING_REQUESTS = 1;
 17    private const float TIME_TO_CHECK_FOR_UNUSED_WEARABLES = 10f;
 18    private const float REQUESTS_TIME_OUT_SECONDS = 45;
 19
 020    public static CatalogController i { get; private set; }
 21
 809722    public static BaseDictionary<string, WearableItem> wearableCatalog => DataStore.i.common.wearables;
 23
 124    private static Dictionary<string, int> wearablesInUseCounters = new Dictionary<string, int>();
 125    private static Dictionary<string, Promise<WearableItem>> awaitingWearablePromises = new Dictionary<string, Promise<W
 126    private static Dictionary<string, float> pendingWearableRequestedTimes = new Dictionary<string, float>();
 127    private static Dictionary<string, Promise<WearableItem[]>> awaitingWearablesByContextPromises = new Dictionary<strin
 128    private static Dictionary<string, float> pendingWearablesByContextRequestedTimes = new Dictionary<string, float>();
 129    private static List<string> pendingRequestsToSend = new List<string>();
 30    private float timeSinceLastUnusedWearablesCheck = 0f;
 31
 16032    public void Awake() { i = this; }
 33
 34    private void Update()
 35    {
 36        // All the requests happened during the same frames interval are sent together
 24137        if (Time.frameCount % FRAMES_TO_CHECK_FOR_SENDING_PENDING_REQUESTS == 0)
 38        {
 24139            CheckForSendingPendingRequests();
 24140            CheckForRequestsTimeOuts();
 24141            CheckForRequestsByContextTimeOuts();
 42        }
 43
 44        // Check unused wearables (to be removed from our catalog) only every [TIME_TO_CHECK_FOR_UNUSED_WEARABLES] secon
 24145        timeSinceLastUnusedWearablesCheck += Time.deltaTime;
 24146        if (timeSinceLastUnusedWearablesCheck >= TIME_TO_CHECK_FOR_UNUSED_WEARABLES)
 47        {
 048            CheckForUnusedWearables();
 049            timeSinceLastUnusedWearablesCheck = 0f;
 50        }
 24151    }
 52
 53    public static void Clear()
 54    {
 74755        wearableCatalog.Clear();
 74756        wearablesInUseCounters.Clear();
 74757        awaitingWearablePromises.Clear();
 74758        pendingWearableRequestedTimes.Clear();
 74759        awaitingWearablesByContextPromises.Clear();
 74760        pendingWearablesByContextRequestedTimes.Clear();
 74761        pendingRequestsToSend.Clear();
 74762    }
 63
 64    //This temporary until the emotes are in the content server
 65    public void EmbedWearables(IEnumerable<WearableItem> wearables)
 66    {
 067        foreach (WearableItem wearableItem in wearables)
 68        {
 069            wearableCatalog[wearableItem.id] = wearableItem;
 070            wearablesInUseCounters[wearableItem.id] = 10000; //A high value to ensure they are not removed
 71        }
 072    }
 73
 74    public void AddWearablesToCatalog(string payload)
 75    {
 076        if (VERBOSE)
 077            Debug.Log("add wearables: " + payload);
 78
 079        WearablesRequestResponse request = null;
 80
 81        try
 82        {
 83            // The new wearables paradigm is based on composing with optional field
 84            // i.e. the emotes will have an emotev0Data property with some values.
 85            // JsonUtility.FromJson doesn't allow null properties so we have to use Newtonsoft instead
 086            request = JsonConvert.DeserializeObject<WearablesRequestResponse>(payload);
 087        }
 088        catch (Exception e)
 89        {
 090            Debug.LogError($"Fail to parse wearables json {e}");
 091        }
 92
 093        if (request == null)
 094            return;
 95
 096        AddWearablesToCatalog(request.wearables);
 97
 098        if (!string.IsNullOrEmpty(request.context))
 99        {
 0100            ResolvePendingWearablesByContextPromise(request.context, request.wearables);
 0101            pendingWearablesByContextRequestedTimes.Remove(request.context);
 102        }
 0103    }
 104
 105    public void AddWearablesToCatalog(WearableItem[] wearableItems)
 106    {
 5920107        foreach (WearableItem wearableItem in wearableItems)
 108        {
 2880109            if (!wearableCatalog.ContainsKey(wearableItem.id))
 110            {
 2880111                wearableCatalog.Add(wearableItem.id, wearableItem);
 112
 2880113                if (!wearablesInUseCounters.ContainsKey(wearableItem.id))
 2880114                    wearablesInUseCounters.Add(wearableItem.id, 1);
 115
 2880116                ResolvePendingWearablePromise(wearableItem.id, wearableItem);
 117
 2880118                pendingWearableRequestedTimes.Remove(wearableItem.id);
 119            }
 120        }
 80121    }
 122
 0123    public void AddWearablesToCatalog(List<WearableItem> wearableItems) { AddWearablesToCatalog(wearableItems.ToArray())
 124
 125    public void WearablesRequestFailed(string payload)
 126    {
 0127        WearablesRequestFailed requestFailedResponse = JsonUtility.FromJson<WearablesRequestFailed>(payload);
 128
 0129        if (requestFailedResponse.context == BASE_WEARABLES_CONTEXT ||
 130            requestFailedResponse.context == OWNED_WEARABLES_CONTEXT)
 131        {
 0132            ResolvePendingWearablesByContextPromise(
 133                requestFailedResponse.context,
 134                null,
 135                requestFailedResponse.error);
 0136        }
 137        else
 138        {
 0139            string[] failedWearablesIds = requestFailedResponse.context.Split(',');
 0140            for (int i = 0; i < failedWearablesIds.Length; i++)
 141            {
 0142                ResolvePendingWearablePromise(
 143                    failedWearablesIds[i],
 144                    null,
 145                    $"The request for the wearable '{failedWearablesIds[i]}' has failed: {requestFailedResponse.error}")
 146            }
 147        }
 0148    }
 149
 150    public void RemoveWearablesFromCatalog(string payload)
 151    {
 0152        string[] itemIDs = JsonUtility.FromJson<string[]>(payload);
 153
 0154        int count = itemIDs.Length;
 0155        for (int i = 0; i < count; ++i)
 156        {
 0157            wearableCatalog.Remove(itemIDs[i]);
 0158            wearablesInUseCounters.Remove(itemIDs[i]);
 159        }
 0160    }
 161
 162    public void ClearWearableCatalog()
 163    {
 0164        wearableCatalog?.Clear();
 0165        wearablesInUseCounters.Clear();
 0166    }
 167
 168    public static Promise<WearableItem> RequestWearable(string wearableId)
 169    {
 609170        if (VERBOSE)
 0171            Debug.Log("request wearables: " + wearableId);
 172
 173        Promise<WearableItem> promiseResult;
 174
 609175        if (wearableCatalog.TryGetValue(wearableId, out WearableItem wearable))
 176        {
 571177            wearablesInUseCounters[wearableId]++;
 571178            promiseResult = new Promise<WearableItem>();
 571179            promiseResult.Resolve(wearable);
 571180        }
 181        else
 182        {
 38183            if (!awaitingWearablePromises.ContainsKey(wearableId))
 184            {
 38185                promiseResult = new Promise<WearableItem>();
 38186                awaitingWearablePromises.Add(wearableId, promiseResult);
 187
 188                // We accumulate all the requests during the same frames interval to send them all together
 38189                pendingRequestsToSend.Add(wearableId);
 38190            }
 191            else
 192            {
 0193                awaitingWearablePromises.TryGetValue(wearableId, out promiseResult);
 194            }
 195        }
 196
 609197        return promiseResult;
 198    }
 199
 200    public static Promise<WearableItem[]> RequestOwnedWearables(string userId)
 201    {
 202        Promise<WearableItem[]> promiseResult;
 203
 2204        if (!awaitingWearablesByContextPromises.ContainsKey(OWNED_WEARABLES_CONTEXT))
 205        {
 1206            promiseResult = new Promise<WearableItem[]>();
 207
 1208            awaitingWearablesByContextPromises.Add(OWNED_WEARABLES_CONTEXT, promiseResult);
 209
 1210            if (!pendingWearablesByContextRequestedTimes.ContainsKey(OWNED_WEARABLES_CONTEXT))
 1211                pendingWearablesByContextRequestedTimes.Add(OWNED_WEARABLES_CONTEXT, Time.realtimeSinceStartup);
 212
 1213            WebInterface.RequestWearables(
 214                ownedByUser: userId,
 215                wearableIds: null,
 216                collectionIds: null,
 217                context: OWNED_WEARABLES_CONTEXT
 218            );
 1219        }
 220        else
 221        {
 1222            awaitingWearablesByContextPromises.TryGetValue(OWNED_WEARABLES_CONTEXT, out promiseResult);
 223        }
 224
 2225        return promiseResult;
 226    }
 227
 228    public static Promise<WearableItem[]> RequestBaseWearables()
 229    {
 230        Promise<WearableItem[]> promiseResult;
 231
 0232        if (!awaitingWearablesByContextPromises.ContainsKey(BASE_WEARABLES_CONTEXT))
 233        {
 0234            promiseResult = new Promise<WearableItem[]>();
 0235            awaitingWearablesByContextPromises.Add(BASE_WEARABLES_CONTEXT, promiseResult);
 236
 0237            if (!pendingWearablesByContextRequestedTimes.ContainsKey(BASE_WEARABLES_CONTEXT))
 0238                pendingWearablesByContextRequestedTimes.Add(BASE_WEARABLES_CONTEXT, Time.realtimeSinceStartup);
 239
 0240            WebInterface.RequestWearables(
 241                ownedByUser: null,
 242                wearableIds: null,
 243                collectionIds: new string[] { "urn:decentraland:off-chain:base-avatars" },
 244                context: BASE_WEARABLES_CONTEXT
 245            );
 0246        }
 247        else
 248        {
 0249            awaitingWearablesByContextPromises.TryGetValue(BASE_WEARABLES_CONTEXT, out promiseResult);
 250        }
 251
 0252        return promiseResult;
 253    }
 254
 255    public static void RemoveWearablesInUse(List<string> wearablesInUseToRemove)
 256    {
 3952257        foreach (var wearableToRemove in wearablesInUseToRemove)
 258        {
 571259            if (wearablesInUseCounters.ContainsKey(wearableToRemove))
 260            {
 571261                wearablesInUseCounters[wearableToRemove]--;
 262            }
 263        }
 1405264    }
 265
 266    private void ResolvePendingWearablePromise(string wearableId, WearableItem newWearableAddedIntoCatalog = null, strin
 267    {
 2880268        if (awaitingWearablePromises.TryGetValue(wearableId, out Promise<WearableItem> promise))
 269        {
 0270            awaitingWearablePromises.Remove(wearableId);
 271
 0272            if (string.IsNullOrEmpty(errorMessage))
 0273                promise.Resolve(newWearableAddedIntoCatalog);
 274            else
 0275                promise.Reject(errorMessage);
 276        }
 2880277    }
 278
 279    private void ResolvePendingWearablesByContextPromise(string context, WearableItem[] newWearablesAddedIntoCatalog = n
 280    {
 0281        if (awaitingWearablesByContextPromises.TryGetValue(context, out Promise<WearableItem[]> promise))
 282        {
 0283            awaitingWearablesByContextPromises.Remove(context);
 284
 0285            if (string.IsNullOrEmpty(errorMessage))
 0286                promise.Resolve(newWearablesAddedIntoCatalog);
 287            else
 0288                promise.Reject(errorMessage);
 289        }
 0290    }
 291
 292    private void CheckForSendingPendingRequests()
 293    {
 241294        if (pendingRequestsToSend.Count > 0)
 295        {
 0296            foreach (var request in pendingRequestsToSend)
 297            {
 0298                if (!pendingWearableRequestedTimes.ContainsKey(request))
 0299                    pendingWearableRequestedTimes.Add(request, Time.realtimeSinceStartup);
 300            }
 301
 0302            WebInterface.RequestWearables(
 303                ownedByUser: null,
 304                wearableIds: pendingRequestsToSend.ToArray(),
 305                collectionIds: null,
 306                context: string.Join(",", pendingRequestsToSend.ToArray())
 307            );
 308
 0309            pendingRequestsToSend.Clear();
 310        }
 241311    }
 312
 313    private void CheckForRequestsTimeOuts()
 314    {
 241315        if (pendingWearableRequestedTimes.Count > 0)
 316        {
 0317            List<string> expiredRequestes = new List<string>();
 0318            foreach (var promiseRequestedTime in pendingWearableRequestedTimes)
 319            {
 0320                if ((Time.realtimeSinceStartup - promiseRequestedTime.Value) > REQUESTS_TIME_OUT_SECONDS)
 321                {
 0322                    expiredRequestes.Add(promiseRequestedTime.Key);
 323                }
 324            }
 325
 0326            foreach (var expiredRequestToRemove in expiredRequestes)
 327            {
 0328                pendingWearableRequestedTimes.Remove(expiredRequestToRemove);
 329
 0330                ResolvePendingWearablePromise(
 331                    expiredRequestToRemove,
 332                    null,
 333                    $"The request for the wearable '{expiredRequestToRemove}' has exceed the set timeout!");
 334            }
 335        }
 241336    }
 337
 338    private void CheckForRequestsByContextTimeOuts()
 339    {
 241340        if (pendingWearablesByContextRequestedTimes.Count > 0)
 341        {
 0342            List<string> expiredRequests = new List<string>();
 0343            foreach (var promiseByContextRequestedTime in pendingWearablesByContextRequestedTimes)
 344            {
 0345                if ((Time.realtimeSinceStartup - promiseByContextRequestedTime.Value) > REQUESTS_TIME_OUT_SECONDS)
 346                {
 0347                    expiredRequests.Add(promiseByContextRequestedTime.Key);
 348                }
 349            }
 350
 0351            foreach (var expiredRequestToRemove in expiredRequests)
 352            {
 0353                pendingWearablesByContextRequestedTimes.Remove(expiredRequestToRemove);
 354
 0355                ResolvePendingWearablesByContextPromise(
 356                    expiredRequestToRemove,
 357                    null,
 358                    $"The request for the wearable context '{expiredRequestToRemove}' has exceed the set timeout!");
 359            }
 360        }
 241361    }
 362
 363    private void CheckForUnusedWearables()
 364    {
 0365        if (wearablesInUseCounters.Count > 0)
 366        {
 0367            List<string> wearablesToDestroy = new List<string>();
 0368            foreach (var wearableInUse in wearablesInUseCounters)
 369            {
 0370                if (wearableInUse.Value <= 0)
 371                {
 0372                    wearablesToDestroy.Add(wearableInUse.Key);
 373                }
 374            }
 375
 0376            foreach (var wearableToDestroy in wearablesToDestroy)
 377            {
 0378                wearableCatalog.Remove(wearableToDestroy);
 0379                wearablesInUseCounters.Remove(wearableToDestroy);
 380            }
 381        }
 0382    }
 383}