< Summary

Class:CatalogController
Assembly:CatalogController
File(s):/tmp/workspace/unity-renderer/unity-renderer/Assets/Scripts/MainScripts/DCL/Controllers/CatalogController/CatalogController.cs
Covered lines:47
Uncovered lines:102
Coverable lines:149
Total lines:367
Line coverage:31.5% (47 of 149)
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%330100%
Clear()0%110100%
AddWearablesToCatalog(...)0%20400%
AddWearablesToCatalog(...)0%20400%
AddWearablesToCatalog(...)0%2100%
WearablesRequestFailed(...)0%20400%
RemoveWearablesFromCatalog(...)0%6200%
ClearWearableCatalog()0%6200%
RequestWearable(...)0%20400%
RequestOwnedWearables(...)0%330100%
RequestBaseWearables()0%12300%
RemoveWearablesInUse(...)0%4.123050%
ResolvePendingWearablePromise(...)0%12300%
ResolvePendingWearablesByContextPromise(...)0%12300%
CheckForSendingPendingRequests()0%11.534022.22%
CheckForRequestsTimeOuts()0%20.155015.38%
CheckForRequestsByContextTimeOuts()0%5.735069.23%
CheckForUnusedWearables()0%20.155015.38%

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
 020    public static BaseDictionary<string, WearableItem> wearableCatalog => DataStore.i.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
 25030    public void Awake() { i = this; }
 31
 32    private void Update()
 33    {
 34        // All the requests happened during the same frames interval are sent together
 2144735        if (Time.frameCount % FRAMES_TO_CHECK_FOR_SENDING_PENDING_REQUESTS == 0)
 36        {
 2144737            CheckForSendingPendingRequests();
 2144738            CheckForRequestsTimeOuts();
 2144739            CheckForRequestsByContextTimeOuts();
 40        }
 41
 42        // Check unused wearables (to be removed from our catalog) only every [TIME_TO_CHECK_FOR_UNUSED_WEARABLES] secon
 2144743        timeSinceLastUnusedWearablesCheck += Time.deltaTime;
 2144744        if (timeSinceLastUnusedWearablesCheck >= TIME_TO_CHECK_FOR_UNUSED_WEARABLES)
 45        {
 346            CheckForUnusedWearables();
 347            timeSinceLastUnusedWearablesCheck = 0f;
 48        }
 2144749    }
 50
 51    public static void Clear()
 52    {
 66453        wearablesInUseCounters.Clear();
 66454        awaitingWearablePromises.Clear();
 66455        pendingWearableRequestedTimes.Clear();
 66456        awaitingWearablesByContextPromises.Clear();
 66457        pendingWearablesByContextRequestedTimes.Clear();
 66458        pendingRequestsToSend.Clear();
 66459    }
 60
 61    public void AddWearablesToCatalog(string payload)
 62    {
 063        if (VERBOSE)
 064            Debug.Log("add wearables: " + payload);
 65
 066        WearablesRequestResponse request = null;
 67
 68        try
 69        {
 070            request = JsonUtility.FromJson<WearablesRequestResponse>(payload);
 071        }
 072        catch (Exception e)
 73        {
 074            Debug.LogError($"Fail to parse wearables json {e}");
 075        }
 76
 077        if (request == null)
 078            return;
 79
 080        AddWearablesToCatalog(request.wearables);
 81
 082        if (!string.IsNullOrEmpty(request.context))
 83        {
 084            ResolvePendingWearablesByContextPromise(request.context, request.wearables);
 085            pendingWearablesByContextRequestedTimes.Remove(request.context);
 86        }
 087    }
 88
 89    public void AddWearablesToCatalog(WearableItem[] wearableItems)
 90    {
 091        foreach (WearableItem wearableItem in wearableItems)
 92        {
 093            if (!wearableCatalog.ContainsKey(wearableItem.id))
 94            {
 095                wearableCatalog.Add(wearableItem.id, wearableItem);
 96
 097                if (!wearablesInUseCounters.ContainsKey(wearableItem.id))
 098                    wearablesInUseCounters.Add(wearableItem.id, 1);
 99
 0100                ResolvePendingWearablePromise(wearableItem.id, wearableItem);
 101
 0102                pendingWearableRequestedTimes.Remove(wearableItem.id);
 103            }
 104        }
 0105    }
 106
 0107    public void AddWearablesToCatalog(List<WearableItem> wearableItems) { AddWearablesToCatalog(wearableItems.ToArray())
 108
 109    public void WearablesRequestFailed(string payload)
 110    {
 0111        WearablesRequestFailed requestFailedResponse = JsonUtility.FromJson<WearablesRequestFailed>(payload);
 112
 0113        if (requestFailedResponse.context == BASE_WEARABLES_CONTEXT ||
 114            requestFailedResponse.context == OWNED_WEARABLES_CONTEXT)
 115        {
 0116            ResolvePendingWearablesByContextPromise(
 117                requestFailedResponse.context,
 118                null,
 119                requestFailedResponse.error);
 0120        }
 121        else
 122        {
 0123            string[] failedWearablesIds = requestFailedResponse.context.Split(',');
 0124            for (int i = 0; i < failedWearablesIds.Length; i++)
 125            {
 0126                ResolvePendingWearablePromise(
 127                    failedWearablesIds[i],
 128                    null,
 129                    $"The request for the wearable '{failedWearablesIds[i]}' has failed: {requestFailedResponse.error}")
 130            }
 131        }
 0132    }
 133
 134    public void RemoveWearablesFromCatalog(string payload)
 135    {
 0136        string[] itemIDs = JsonUtility.FromJson<string[]>(payload);
 137
 0138        int count = itemIDs.Length;
 0139        for (int i = 0; i < count; ++i)
 140        {
 0141            wearableCatalog.Remove(itemIDs[i]);
 0142            wearablesInUseCounters.Remove(itemIDs[i]);
 143        }
 0144    }
 145
 146    public void ClearWearableCatalog()
 147    {
 0148        wearableCatalog?.Clear();
 0149        wearablesInUseCounters.Clear();
 0150    }
 151
 152    public static Promise<WearableItem> RequestWearable(string wearableId)
 153    {
 0154        if (VERBOSE)
 0155            Debug.Log("request wearables: " + wearableId);
 156
 157        Promise<WearableItem> promiseResult;
 158
 0159        if (wearableCatalog.TryGetValue(wearableId, out WearableItem wearable))
 160        {
 0161            wearablesInUseCounters[wearableId]++;
 0162            promiseResult = new Promise<WearableItem>();
 0163            promiseResult.Resolve(wearable);
 0164        }
 165        else
 166        {
 0167            if (!awaitingWearablePromises.ContainsKey(wearableId))
 168            {
 0169                promiseResult = new Promise<WearableItem>();
 0170                awaitingWearablePromises.Add(wearableId, promiseResult);
 171
 172                // We accumulate all the requests during the same frames interval to send them all together
 0173                pendingRequestsToSend.Add(wearableId);
 0174            }
 175            else
 176            {
 0177                awaitingWearablePromises.TryGetValue(wearableId, out promiseResult);
 178            }
 179        }
 180
 0181        return promiseResult;
 182    }
 183
 184    public static Promise<WearableItem[]> RequestOwnedWearables(string userId)
 185    {
 186        Promise<WearableItem[]> promiseResult;
 187
 2188        if (!awaitingWearablesByContextPromises.ContainsKey(OWNED_WEARABLES_CONTEXT))
 189        {
 1190            promiseResult = new Promise<WearableItem[]>();
 191
 1192            awaitingWearablesByContextPromises.Add(OWNED_WEARABLES_CONTEXT, promiseResult);
 193
 1194            if (!pendingWearablesByContextRequestedTimes.ContainsKey(OWNED_WEARABLES_CONTEXT))
 1195                pendingWearablesByContextRequestedTimes.Add(OWNED_WEARABLES_CONTEXT, Time.realtimeSinceStartup);
 196
 1197            WebInterface.RequestWearables(
 198                ownedByUser: userId,
 199                wearableIds: null,
 200                collectionIds: null,
 201                context: OWNED_WEARABLES_CONTEXT
 202            );
 1203        }
 204        else
 205        {
 1206            awaitingWearablesByContextPromises.TryGetValue(OWNED_WEARABLES_CONTEXT, out promiseResult);
 207        }
 208
 2209        return promiseResult;
 210    }
 211
 212    public static Promise<WearableItem[]> RequestBaseWearables()
 213    {
 214        Promise<WearableItem[]> promiseResult;
 215
 0216        if (!awaitingWearablesByContextPromises.ContainsKey(BASE_WEARABLES_CONTEXT))
 217        {
 0218            promiseResult = new Promise<WearableItem[]>();
 0219            awaitingWearablesByContextPromises.Add(BASE_WEARABLES_CONTEXT, promiseResult);
 220
 0221            if (!pendingWearablesByContextRequestedTimes.ContainsKey(BASE_WEARABLES_CONTEXT))
 0222                pendingWearablesByContextRequestedTimes.Add(BASE_WEARABLES_CONTEXT, Time.realtimeSinceStartup);
 223
 0224            WebInterface.RequestWearables(
 225                ownedByUser: null,
 226                wearableIds: null,
 227                collectionIds: new string[] { "urn:decentraland:off-chain:base-avatars" },
 228                context: BASE_WEARABLES_CONTEXT
 229            );
 0230        }
 231        else
 232        {
 0233            awaitingWearablesByContextPromises.TryGetValue(BASE_WEARABLES_CONTEXT, out promiseResult);
 234        }
 235
 0236        return promiseResult;
 237    }
 238
 239    public static void RemoveWearablesInUse(List<string> wearablesInUseToRemove)
 240    {
 3072241        foreach (var wearableToRemove in wearablesInUseToRemove)
 242        {
 0243            if (wearablesInUseCounters.ContainsKey(wearableToRemove))
 244            {
 0245                wearablesInUseCounters[wearableToRemove]--;
 246            }
 247        }
 1536248    }
 249
 250    private void ResolvePendingWearablePromise(string wearableId, WearableItem newWearableAddedIntoCatalog = null, strin
 251    {
 0252        if (awaitingWearablePromises.TryGetValue(wearableId, out Promise<WearableItem> promise))
 253        {
 0254            awaitingWearablePromises.Remove(wearableId);
 255
 0256            if (string.IsNullOrEmpty(errorMessage))
 0257                promise.Resolve(newWearableAddedIntoCatalog);
 258            else
 0259                promise.Reject(errorMessage);
 260        }
 0261    }
 262
 263    private void ResolvePendingWearablesByContextPromise(string context, WearableItem[] newWearablesAddedIntoCatalog = n
 264    {
 0265        if (awaitingWearablesByContextPromises.TryGetValue(context, out Promise<WearableItem[]> promise))
 266        {
 0267            awaitingWearablesByContextPromises.Remove(context);
 268
 0269            if (string.IsNullOrEmpty(errorMessage))
 0270                promise.Resolve(newWearablesAddedIntoCatalog);
 271            else
 0272                promise.Reject(errorMessage);
 273        }
 0274    }
 275
 276    private void CheckForSendingPendingRequests()
 277    {
 21447278        if (pendingRequestsToSend.Count > 0)
 279        {
 0280            foreach (var request in pendingRequestsToSend)
 281            {
 0282                if (!pendingWearableRequestedTimes.ContainsKey(request))
 0283                    pendingWearableRequestedTimes.Add(request, Time.realtimeSinceStartup);
 284            }
 285
 0286            WebInterface.RequestWearables(
 287                ownedByUser: null,
 288                wearableIds: pendingRequestsToSend.ToArray(),
 289                collectionIds: null,
 290                context: string.Join(",", pendingRequestsToSend.ToArray())
 291            );
 292
 0293            pendingRequestsToSend.Clear();
 294        }
 21447295    }
 296
 297    private void CheckForRequestsTimeOuts()
 298    {
 21447299        if (pendingWearableRequestedTimes.Count > 0)
 300        {
 0301            List<string> expiredRequestes = new List<string>();
 0302            foreach (var promiseRequestedTime in pendingWearableRequestedTimes)
 303            {
 0304                if ((Time.realtimeSinceStartup - promiseRequestedTime.Value) > REQUESTS_TIME_OUT_SECONDS)
 305                {
 0306                    expiredRequestes.Add(promiseRequestedTime.Key);
 307                }
 308            }
 309
 0310            foreach (var expiredRequestToRemove in expiredRequestes)
 311            {
 0312                pendingWearableRequestedTimes.Remove(expiredRequestToRemove);
 313
 0314                ResolvePendingWearablePromise(
 315                    expiredRequestToRemove,
 316                    null,
 317                    $"The request for the wearable '{expiredRequestToRemove}' has exceed the set timeout!");
 318            }
 319        }
 21447320    }
 321
 322    private void CheckForRequestsByContextTimeOuts()
 323    {
 21447324        if (pendingWearablesByContextRequestedTimes.Count > 0)
 325        {
 2326            List<string> expiredRequests = new List<string>();
 8327            foreach (var promiseByContextRequestedTime in pendingWearablesByContextRequestedTimes)
 328            {
 2329                if ((Time.realtimeSinceStartup - promiseByContextRequestedTime.Value) > REQUESTS_TIME_OUT_SECONDS)
 330                {
 0331                    expiredRequests.Add(promiseByContextRequestedTime.Key);
 332                }
 333            }
 334
 4335            foreach (var expiredRequestToRemove in expiredRequests)
 336            {
 0337                pendingWearablesByContextRequestedTimes.Remove(expiredRequestToRemove);
 338
 0339                ResolvePendingWearablesByContextPromise(
 340                    expiredRequestToRemove,
 341                    null,
 342                    $"The request for the wearable context '{expiredRequestToRemove}' has exceed the set timeout!");
 343            }
 344        }
 21447345    }
 346
 347    private void CheckForUnusedWearables()
 348    {
 3349        if (wearablesInUseCounters.Count > 0)
 350        {
 0351            List<string> wearablesToDestroy = new List<string>();
 0352            foreach (var wearableInUse in wearablesInUseCounters)
 353            {
 0354                if (wearableInUse.Value <= 0)
 355                {
 0356                    wearablesToDestroy.Add(wearableInUse.Key);
 357                }
 358            }
 359
 0360            foreach (var wearableToDestroy in wearablesToDestroy)
 361            {
 0362                wearableCatalog.Remove(wearableToDestroy);
 0363                wearablesInUseCounters.Remove(wearableToDestroy);
 364            }
 365        }
 3366    }
 367}