< Summary

Class:CatalogController
Assembly:CatalogController
File(s):/tmp/workspace/unity-renderer/unity-renderer/Assets/Scripts/MainScripts/DCL/Controllers/CatalogController/CatalogController.cs
Covered lines:63
Uncovered lines:87
Coverable lines:150
Total lines:368
Line coverage:42% (63 of 150)
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%
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%4.123050%
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 UnityEngine;
 8
 9public class CatalogController : MonoBehaviour
 10{
 111    public static bool VERBOSE = false;
 12    private const string OWNED_WEARABLES_CONTEXT = "OwnedWearables";
 13    private const string BASE_WEARABLES_CONTEXT = "BaseWearables";
 14    private const int FRAMES_TO_CHECK_FOR_SENDING_PENDING_REQUESTS = 1;
 15    private const float TIME_TO_CHECK_FOR_UNUSED_WEARABLES = 10f;
 16    private const float REQUESTS_TIME_OUT_SECONDS = 45;
 17
 018    public static CatalogController i { get; private set; }
 19
 603420    public static BaseDictionary<string, WearableItem> wearableCatalog => DataStore.i.common.wearables;
 21
 122    private static Dictionary<string, int> wearablesInUseCounters = new Dictionary<string, int>();
 123    private static Dictionary<string, Promise<WearableItem>> awaitingWearablePromises = new Dictionary<string, Promise<W
 124    private static Dictionary<string, float> pendingWearableRequestedTimes = new Dictionary<string, float>();
 125    private static Dictionary<string, Promise<WearableItem[]>> awaitingWearablesByContextPromises = new Dictionary<strin
 126    private static Dictionary<string, float> pendingWearablesByContextRequestedTimes = new Dictionary<string, float>();
 127    private static List<string> pendingRequestsToSend = new List<string>();
 28    private float timeSinceLastUnusedWearablesCheck = 0f;
 29
 10430    public void Awake() { i = this; }
 31
 32    private void Update()
 33    {
 34        // All the requests happened during the same frames interval are sent together
 12735        if (Time.frameCount % FRAMES_TO_CHECK_FOR_SENDING_PENDING_REQUESTS == 0)
 36        {
 12737            CheckForSendingPendingRequests();
 12738            CheckForRequestsTimeOuts();
 12739            CheckForRequestsByContextTimeOuts();
 40        }
 41
 42        // Check unused wearables (to be removed from our catalog) only every [TIME_TO_CHECK_FOR_UNUSED_WEARABLES] secon
 12743        timeSinceLastUnusedWearablesCheck += Time.deltaTime;
 12744        if (timeSinceLastUnusedWearablesCheck >= TIME_TO_CHECK_FOR_UNUSED_WEARABLES)
 45        {
 046            CheckForUnusedWearables();
 047            timeSinceLastUnusedWearablesCheck = 0f;
 48        }
 12749    }
 50
 51    public static void Clear()
 52    {
 70453        wearableCatalog.Clear();
 70454        wearablesInUseCounters.Clear();
 70455        awaitingWearablePromises.Clear();
 70456        pendingWearableRequestedTimes.Clear();
 70457        awaitingWearablesByContextPromises.Clear();
 70458        pendingWearablesByContextRequestedTimes.Clear();
 70459        pendingRequestsToSend.Clear();
 70460    }
 61
 62    public void AddWearablesToCatalog(string payload)
 63    {
 064        if (VERBOSE)
 065            Debug.Log("add wearables: " + payload);
 66
 067        WearablesRequestResponse request = null;
 68
 69        try
 70        {
 071            request = JsonUtility.FromJson<WearablesRequestResponse>(payload);
 072        }
 073        catch (Exception e)
 74        {
 075            Debug.LogError($"Fail to parse wearables json {e}");
 076        }
 77
 078        if (request == null)
 079            return;
 80
 081        AddWearablesToCatalog(request.wearables);
 82
 083        if (!string.IsNullOrEmpty(request.context))
 84        {
 085            ResolvePendingWearablesByContextPromise(request.context, request.wearables);
 086            pendingWearablesByContextRequestedTimes.Remove(request.context);
 87        }
 088    }
 89
 90    public void AddWearablesToCatalog(WearableItem[] wearableItems)
 91    {
 458892        foreach (WearableItem wearableItem in wearableItems)
 93        {
 223294            if (!wearableCatalog.ContainsKey(wearableItem.id))
 95            {
 223296                wearableCatalog.Add(wearableItem.id, wearableItem);
 97
 223298                if (!wearablesInUseCounters.ContainsKey(wearableItem.id))
 223299                    wearablesInUseCounters.Add(wearableItem.id, 1);
 100
 2232101                ResolvePendingWearablePromise(wearableItem.id, wearableItem);
 102
 2232103                pendingWearableRequestedTimes.Remove(wearableItem.id);
 104            }
 105        }
 62106    }
 107
 0108    public void AddWearablesToCatalog(List<WearableItem> wearableItems) { AddWearablesToCatalog(wearableItems.ToArray())
 109
 110    public void WearablesRequestFailed(string payload)
 111    {
 0112        WearablesRequestFailed requestFailedResponse = JsonUtility.FromJson<WearablesRequestFailed>(payload);
 113
 0114        if (requestFailedResponse.context == BASE_WEARABLES_CONTEXT ||
 115            requestFailedResponse.context == OWNED_WEARABLES_CONTEXT)
 116        {
 0117            ResolvePendingWearablesByContextPromise(
 118                requestFailedResponse.context,
 119                null,
 120                requestFailedResponse.error);
 0121        }
 122        else
 123        {
 0124            string[] failedWearablesIds = requestFailedResponse.context.Split(',');
 0125            for (int i = 0; i < failedWearablesIds.Length; i++)
 126            {
 0127                ResolvePendingWearablePromise(
 128                    failedWearablesIds[i],
 129                    null,
 130                    $"The request for the wearable '{failedWearablesIds[i]}' has failed: {requestFailedResponse.error}")
 131            }
 132        }
 0133    }
 134
 135    public void RemoveWearablesFromCatalog(string payload)
 136    {
 0137        string[] itemIDs = JsonUtility.FromJson<string[]>(payload);
 138
 0139        int count = itemIDs.Length;
 0140        for (int i = 0; i < count; ++i)
 141        {
 0142            wearableCatalog.Remove(itemIDs[i]);
 0143            wearablesInUseCounters.Remove(itemIDs[i]);
 144        }
 0145    }
 146
 147    public void ClearWearableCatalog()
 148    {
 0149        wearableCatalog?.Clear();
 0150        wearablesInUseCounters.Clear();
 0151    }
 152
 153    public static Promise<WearableItem> RequestWearable(string wearableId)
 154    {
 10155        if (VERBOSE)
 0156            Debug.Log("request wearables: " + wearableId);
 157
 158        Promise<WearableItem> promiseResult;
 159
 10160        if (wearableCatalog.TryGetValue(wearableId, out WearableItem wearable))
 161        {
 9162            wearablesInUseCounters[wearableId]++;
 9163            promiseResult = new Promise<WearableItem>();
 9164            promiseResult.Resolve(wearable);
 9165        }
 166        else
 167        {
 1168            if (!awaitingWearablePromises.ContainsKey(wearableId))
 169            {
 1170                promiseResult = new Promise<WearableItem>();
 1171                awaitingWearablePromises.Add(wearableId, promiseResult);
 172
 173                // We accumulate all the requests during the same frames interval to send them all together
 1174                pendingRequestsToSend.Add(wearableId);
 1175            }
 176            else
 177            {
 0178                awaitingWearablePromises.TryGetValue(wearableId, out promiseResult);
 179            }
 180        }
 181
 10182        return promiseResult;
 183    }
 184
 185    public static Promise<WearableItem[]> RequestOwnedWearables(string userId)
 186    {
 187        Promise<WearableItem[]> promiseResult;
 188
 2189        if (!awaitingWearablesByContextPromises.ContainsKey(OWNED_WEARABLES_CONTEXT))
 190        {
 1191            promiseResult = new Promise<WearableItem[]>();
 192
 1193            awaitingWearablesByContextPromises.Add(OWNED_WEARABLES_CONTEXT, promiseResult);
 194
 1195            if (!pendingWearablesByContextRequestedTimes.ContainsKey(OWNED_WEARABLES_CONTEXT))
 1196                pendingWearablesByContextRequestedTimes.Add(OWNED_WEARABLES_CONTEXT, Time.realtimeSinceStartup);
 197
 1198            WebInterface.RequestWearables(
 199                ownedByUser: userId,
 200                wearableIds: null,
 201                collectionIds: null,
 202                context: OWNED_WEARABLES_CONTEXT
 203            );
 1204        }
 205        else
 206        {
 1207            awaitingWearablesByContextPromises.TryGetValue(OWNED_WEARABLES_CONTEXT, out promiseResult);
 208        }
 209
 2210        return promiseResult;
 211    }
 212
 213    public static Promise<WearableItem[]> RequestBaseWearables()
 214    {
 215        Promise<WearableItem[]> promiseResult;
 216
 0217        if (!awaitingWearablesByContextPromises.ContainsKey(BASE_WEARABLES_CONTEXT))
 218        {
 0219            promiseResult = new Promise<WearableItem[]>();
 0220            awaitingWearablesByContextPromises.Add(BASE_WEARABLES_CONTEXT, promiseResult);
 221
 0222            if (!pendingWearablesByContextRequestedTimes.ContainsKey(BASE_WEARABLES_CONTEXT))
 0223                pendingWearablesByContextRequestedTimes.Add(BASE_WEARABLES_CONTEXT, Time.realtimeSinceStartup);
 224
 0225            WebInterface.RequestWearables(
 226                ownedByUser: null,
 227                wearableIds: null,
 228                collectionIds: new string[] { "urn:decentraland:off-chain:base-avatars" },
 229                context: BASE_WEARABLES_CONTEXT
 230            );
 0231        }
 232        else
 233        {
 0234            awaitingWearablesByContextPromises.TryGetValue(BASE_WEARABLES_CONTEXT, out promiseResult);
 235        }
 236
 0237        return promiseResult;
 238    }
 239
 240    public static void RemoveWearablesInUse(List<string> wearablesInUseToRemove)
 241    {
 4000242        foreach (var wearableToRemove in wearablesInUseToRemove)
 243        {
 0244            if (wearablesInUseCounters.ContainsKey(wearableToRemove))
 245            {
 0246                wearablesInUseCounters[wearableToRemove]--;
 247            }
 248        }
 2000249    }
 250
 251    private void ResolvePendingWearablePromise(string wearableId, WearableItem newWearableAddedIntoCatalog = null, strin
 252    {
 2232253        if (awaitingWearablePromises.TryGetValue(wearableId, out Promise<WearableItem> promise))
 254        {
 0255            awaitingWearablePromises.Remove(wearableId);
 256
 0257            if (string.IsNullOrEmpty(errorMessage))
 0258                promise.Resolve(newWearableAddedIntoCatalog);
 259            else
 0260                promise.Reject(errorMessage);
 261        }
 2232262    }
 263
 264    private void ResolvePendingWearablesByContextPromise(string context, WearableItem[] newWearablesAddedIntoCatalog = n
 265    {
 0266        if (awaitingWearablesByContextPromises.TryGetValue(context, out Promise<WearableItem[]> promise))
 267        {
 0268            awaitingWearablesByContextPromises.Remove(context);
 269
 0270            if (string.IsNullOrEmpty(errorMessage))
 0271                promise.Resolve(newWearablesAddedIntoCatalog);
 272            else
 0273                promise.Reject(errorMessage);
 274        }
 0275    }
 276
 277    private void CheckForSendingPendingRequests()
 278    {
 127279        if (pendingRequestsToSend.Count > 0)
 280        {
 0281            foreach (var request in pendingRequestsToSend)
 282            {
 0283                if (!pendingWearableRequestedTimes.ContainsKey(request))
 0284                    pendingWearableRequestedTimes.Add(request, Time.realtimeSinceStartup);
 285            }
 286
 0287            WebInterface.RequestWearables(
 288                ownedByUser: null,
 289                wearableIds: pendingRequestsToSend.ToArray(),
 290                collectionIds: null,
 291                context: string.Join(",", pendingRequestsToSend.ToArray())
 292            );
 293
 0294            pendingRequestsToSend.Clear();
 295        }
 127296    }
 297
 298    private void CheckForRequestsTimeOuts()
 299    {
 127300        if (pendingWearableRequestedTimes.Count > 0)
 301        {
 0302            List<string> expiredRequestes = new List<string>();
 0303            foreach (var promiseRequestedTime in pendingWearableRequestedTimes)
 304            {
 0305                if ((Time.realtimeSinceStartup - promiseRequestedTime.Value) > REQUESTS_TIME_OUT_SECONDS)
 306                {
 0307                    expiredRequestes.Add(promiseRequestedTime.Key);
 308                }
 309            }
 310
 0311            foreach (var expiredRequestToRemove in expiredRequestes)
 312            {
 0313                pendingWearableRequestedTimes.Remove(expiredRequestToRemove);
 314
 0315                ResolvePendingWearablePromise(
 316                    expiredRequestToRemove,
 317                    null,
 318                    $"The request for the wearable '{expiredRequestToRemove}' has exceed the set timeout!");
 319            }
 320        }
 127321    }
 322
 323    private void CheckForRequestsByContextTimeOuts()
 324    {
 127325        if (pendingWearablesByContextRequestedTimes.Count > 0)
 326        {
 0327            List<string> expiredRequests = new List<string>();
 0328            foreach (var promiseByContextRequestedTime in pendingWearablesByContextRequestedTimes)
 329            {
 0330                if ((Time.realtimeSinceStartup - promiseByContextRequestedTime.Value) > REQUESTS_TIME_OUT_SECONDS)
 331                {
 0332                    expiredRequests.Add(promiseByContextRequestedTime.Key);
 333                }
 334            }
 335
 0336            foreach (var expiredRequestToRemove in expiredRequests)
 337            {
 0338                pendingWearablesByContextRequestedTimes.Remove(expiredRequestToRemove);
 339
 0340                ResolvePendingWearablesByContextPromise(
 341                    expiredRequestToRemove,
 342                    null,
 343                    $"The request for the wearable context '{expiredRequestToRemove}' has exceed the set timeout!");
 344            }
 345        }
 127346    }
 347
 348    private void CheckForUnusedWearables()
 349    {
 0350        if (wearablesInUseCounters.Count > 0)
 351        {
 0352            List<string> wearablesToDestroy = new List<string>();
 0353            foreach (var wearableInUse in wearablesInUseCounters)
 354            {
 0355                if (wearableInUse.Value <= 0)
 356                {
 0357                    wearablesToDestroy.Add(wearableInUse.Key);
 358                }
 359            }
 360
 0361            foreach (var wearableToDestroy in wearablesToDestroy)
 362            {
 0363                wearableCatalog.Remove(wearableToDestroy);
 0364                wearablesInUseCounters.Remove(wearableToDestroy);
 365            }
 366        }
 0367    }
 368}