< Summary

Class:CatalogController
Assembly:CatalogController
File(s):/tmp/workspace/unity-renderer/unity-renderer/Assets/Scripts/MainScripts/DCL/Controllers/CatalogController/CatalogController.cs
Covered lines:62
Uncovered lines:102
Coverable lines:164
Total lines:425
Line coverage:37.8% (62 of 164)
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%330100%
AddWearablesToCatalog(...)0%20400%
AddWearablesToCatalog(...)0%440100%
AddWearablesToCatalog(...)0%2100%
WearablesRequestFailed(...)0%56700%
RemoveWearablesFromCatalog(...)0%6200%
ClearWearableCatalog()0%6200%
RequestWearable(...)0%5.164058.33%
RequestOwnedWearables(...)0%12300%
RequestBaseWearables()0%12300%
RequestThirdPartyWearablesByCollection(...)0%3.023087.5%
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%
Remove(...)0%12300%

File(s)

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

#LineLine coverage
 1using DCL;
 2using DCL.Helpers;
 3using DCL.Interface;
 4using System;
 5using System.Collections.Generic;
 6using System.Linq;
 7using DCL.Configuration;
 8using DCL.Emotes;
 9using Newtonsoft.Json;
 10using UnityEngine;
 11
 12public class CatalogController : MonoBehaviour
 13{
 114    public static bool VERBOSE = false;
 15    private const string OWNED_WEARABLES_CONTEXT = "OwnedWearables";
 16    private const string BASE_WEARABLES_CONTEXT = "BaseWearables";
 17    private const string THIRD_PARTY_WEARABLES_CONTEXT = "ThirdPartyWearables";
 18    private const int FRAMES_TO_CHECK_FOR_SENDING_PENDING_REQUESTS = 1;
 19    private const float TIME_TO_CHECK_FOR_UNUSED_WEARABLES = 10f;
 20    private const float REQUESTS_TIME_OUT_SECONDS = 45;
 21
 26122    public static CatalogController i { get; private set; }
 23
 847224    public static BaseDictionary<string, WearableItem> wearableCatalog => DataStore.i.common.wearables;
 25
 126    private static Dictionary<string, int> wearablesInUseCounters = new Dictionary<string, int>();
 127    private static Dictionary<string, Promise<WearableItem>> awaitingWearablePromises = new Dictionary<string, Promise<W
 128    private static Dictionary<string, float> pendingWearableRequestedTimes = new Dictionary<string, float>();
 129    private static Dictionary<string, Promise<WearableItem[]>> awaitingWearablesByContextPromises = new Dictionary<strin
 130    private static Dictionary<string, float> pendingWearablesByContextRequestedTimes = new Dictionary<string, float>();
 131    private static List<string> pendingRequestsToSend = new List<string>();
 32    private float timeSinceLastUnusedWearablesCheck = 0f;
 33
 034    public BaseDictionary<string, WearableItem> Wearables => DataStore.i.common.wearables;
 35
 20036    public void Awake() { i = this; }
 37
 38    private void Update()
 39    {
 40        // All the requests happened during the same frames interval are sent together
 321441        if (Time.frameCount % FRAMES_TO_CHECK_FOR_SENDING_PENDING_REQUESTS == 0)
 42        {
 321443            CheckForSendingPendingRequests();
 321444            CheckForRequestsTimeOuts();
 321445            CheckForRequestsByContextTimeOuts();
 46        }
 47
 48        // Check unused wearables (to be removed from our catalog) only every [TIME_TO_CHECK_FOR_UNUSED_WEARABLES] secon
 321449        timeSinceLastUnusedWearablesCheck += Time.deltaTime;
 321450        if (timeSinceLastUnusedWearablesCheck >= TIME_TO_CHECK_FOR_UNUSED_WEARABLES)
 51        {
 052            CheckForUnusedWearables();
 053            timeSinceLastUnusedWearablesCheck = 0f;
 54        }
 321455    }
 56
 57    public static void Clear()
 58    {
 57559        wearableCatalog.Clear();
 57560        wearablesInUseCounters.Clear();
 57561        awaitingWearablePromises.Clear();
 57562        pendingWearableRequestedTimes.Clear();
 57563        awaitingWearablesByContextPromises.Clear();
 57564        pendingWearablesByContextRequestedTimes.Clear();
 57565        pendingRequestsToSend.Clear();
 57566    }
 67
 68    // TODO: when emotes get published to the content server, remove this
 69    public void EmbedWearables(IEnumerable<WearableItem> wearables)
 70    {
 19271        foreach (WearableItem wearableItem in wearables)
 72        {
 9373            wearableCatalog[wearableItem.id] = wearableItem;
 9374            wearablesInUseCounters[wearableItem.id] = 10000; //A high value to ensure they are not removed
 75        }
 376    }
 77
 78    public void AddWearablesToCatalog(string payload)
 79    {
 080        if (VERBOSE)
 081            Debug.Log("add wearables: " + payload);
 82
 083        WearablesRequestResponse request = null;
 84
 85        try
 86        {
 87            // The new wearables paradigm is based on composing with optional field
 88            // i.e. the emotes will have an emotev0Data property with some values.
 89            // JsonUtility.FromJson doesn't allow null properties so we have to use Newtonsoft instead
 090            request = JsonConvert.DeserializeObject<WearablesRequestResponse>(payload);
 091        }
 092        catch (Exception e)
 93        {
 094            Debug.LogError($"Fail to parse wearables json {e}");
 095        }
 96
 097        if (request == null)
 098            return;
 99
 0100        AddWearablesToCatalog(request.wearables);
 101
 0102        if (!string.IsNullOrEmpty(request.context))
 103        {
 0104            ResolvePendingWearablesByContextPromise(request.context, request.wearables);
 0105            pendingWearablesByContextRequestedTimes.Remove(request.context);
 106        }
 0107    }
 108
 109    public void AddWearablesToCatalog(WearableItem[] wearableItems)
 110    {
 7030111        foreach (WearableItem wearableItem in wearableItems)
 112        {
 3420113            if (!wearableCatalog.ContainsKey(wearableItem.id))
 114            {
 3420115                wearableItem.SanitizeHidesLists();
 3420116                wearableCatalog.Add(wearableItem.id, wearableItem);
 117
 3420118                if (!wearablesInUseCounters.ContainsKey(wearableItem.id))
 3420119                    wearablesInUseCounters.Add(wearableItem.id, 1);
 120
 3420121                ResolvePendingWearablePromise(wearableItem.id, wearableItem);
 122
 3420123                pendingWearableRequestedTimes.Remove(wearableItem.id);
 124            }
 125        }
 95126    }
 127
 0128    public void AddWearablesToCatalog(List<WearableItem> wearableItems) { AddWearablesToCatalog(wearableItems.ToArray())
 129
 130    public void WearablesRequestFailed(string payload)
 131    {
 0132        WearablesRequestFailed requestFailedResponse = JsonUtility.FromJson<WearablesRequestFailed>(payload);
 133
 0134    if (requestFailedResponse?.context == null)
 0135            return;
 136
 0137        if (requestFailedResponse.context == BASE_WEARABLES_CONTEXT ||
 138            requestFailedResponse.context.Contains(THIRD_PARTY_WEARABLES_CONTEXT) ||
 139            requestFailedResponse.context.Contains(OWNED_WEARABLES_CONTEXT))
 140        {
 0141            ResolvePendingWearablesByContextPromise(
 142                requestFailedResponse.context,
 143                null,
 144                requestFailedResponse.error);
 145        }
 146        else
 147        {
 0148            string[] failedWearablesIds = requestFailedResponse.context.Split(',');
 0149            for (int i = 0; i < failedWearablesIds.Length; i++)
 150            {
 0151                ResolvePendingWearablePromise(
 152                    failedWearablesIds[i],
 153                    null,
 154                    $"The request for the wearable '{failedWearablesIds[i]}' has failed: {requestFailedResponse.error}")
 155            }
 156        }
 0157    }
 158
 159    public void RemoveWearablesFromCatalog(string payload)
 160    {
 0161        string[] itemIDs = JsonUtility.FromJson<string[]>(payload);
 162
 0163        int count = itemIDs.Length;
 0164        for (int i = 0; i < count; ++i)
 165        {
 0166            wearableCatalog.Remove(itemIDs[i]);
 0167            wearablesInUseCounters.Remove(itemIDs[i]);
 168        }
 0169    }
 170
 171    public void ClearWearableCatalog()
 172    {
 0173        wearableCatalog?.Clear();
 0174        wearablesInUseCounters.Clear();
 0175    }
 176
 177    public static Promise<WearableItem> RequestWearable(string wearableId)
 178    {
 15179        if (VERBOSE)
 0180            Debug.Log("request wearables: " + wearableId);
 181
 182        Promise<WearableItem> promiseResult;
 183
 15184        if (wearableCatalog.TryGetValue(wearableId, out WearableItem wearable))
 185        {
 0186            wearablesInUseCounters[wearableId]++;
 0187            promiseResult = new Promise<WearableItem>();
 0188            promiseResult.Resolve(wearable);
 189        }
 190        else
 191        {
 15192            if (!awaitingWearablePromises.ContainsKey(wearableId))
 193            {
 15194                promiseResult = new Promise<WearableItem>();
 15195                awaitingWearablePromises.Add(wearableId, promiseResult);
 196
 197                // We accumulate all the requests during the same frames interval to send them all together
 15198                pendingRequestsToSend.Add(wearableId);
 199            }
 200            else
 201            {
 0202                awaitingWearablePromises.TryGetValue(wearableId, out promiseResult);
 203            }
 204        }
 205
 15206        return promiseResult;
 207    }
 208
 209    public static Promise<WearableItem[]> RequestOwnedWearables(string userId)
 210    {
 211        Promise<WearableItem[]> promiseResult;
 212
 0213        if (!awaitingWearablesByContextPromises.ContainsKey($"{OWNED_WEARABLES_CONTEXT}{userId}"))
 214        {
 0215            promiseResult = new Promise<WearableItem[]>();
 216
 0217            awaitingWearablesByContextPromises.Add($"{OWNED_WEARABLES_CONTEXT}{userId}", promiseResult);
 218
 0219            if (!pendingWearablesByContextRequestedTimes.ContainsKey($"{OWNED_WEARABLES_CONTEXT}{userId}"))
 0220                pendingWearablesByContextRequestedTimes.Add($"{OWNED_WEARABLES_CONTEXT}{userId}", Time.realtimeSinceStar
 221
 0222            WebInterface.RequestWearables(
 223                ownedByUser: userId,
 224                wearableIds: null,
 225                collectionIds: null,
 226                context: $"{OWNED_WEARABLES_CONTEXT}{userId}"
 227            );
 228        }
 229        else
 230        {
 0231            awaitingWearablesByContextPromises.TryGetValue($"{OWNED_WEARABLES_CONTEXT}{userId}", out promiseResult);
 232        }
 233
 0234        return promiseResult;
 235    }
 236
 237    public static Promise<WearableItem[]> RequestBaseWearables()
 238    {
 239        Promise<WearableItem[]> promiseResult;
 240
 0241        if (!awaitingWearablesByContextPromises.ContainsKey(BASE_WEARABLES_CONTEXT))
 242        {
 0243            promiseResult = new Promise<WearableItem[]>();
 0244            awaitingWearablesByContextPromises.Add(BASE_WEARABLES_CONTEXT, promiseResult);
 245
 0246            if (!pendingWearablesByContextRequestedTimes.ContainsKey(BASE_WEARABLES_CONTEXT))
 0247                pendingWearablesByContextRequestedTimes.Add(BASE_WEARABLES_CONTEXT, Time.realtimeSinceStartup);
 248
 0249            WebInterface.RequestWearables(
 250                ownedByUser: null,
 251                wearableIds: null,
 252                collectionIds: new string[] { "urn:decentraland:off-chain:base-avatars" },
 253                context: BASE_WEARABLES_CONTEXT
 254            );
 255        }
 256        else
 257        {
 0258            awaitingWearablesByContextPromises.TryGetValue(BASE_WEARABLES_CONTEXT, out promiseResult);
 259        }
 260
 0261        return promiseResult;
 262    }
 263
 264    public static Promise<WearableItem[]> RequestThirdPartyWearablesByCollection(string userId, string collectionId)
 265    {
 266        Promise<WearableItem[]> promiseResult;
 267
 1268        if (!awaitingWearablesByContextPromises.ContainsKey($"{THIRD_PARTY_WEARABLES_CONTEXT}_{collectionId}"))
 269        {
 1270            promiseResult = new Promise<WearableItem[]>();
 271
 1272            awaitingWearablesByContextPromises.Add($"{THIRD_PARTY_WEARABLES_CONTEXT}_{collectionId}", promiseResult);
 273
 1274            if (!pendingWearablesByContextRequestedTimes.ContainsKey($"{THIRD_PARTY_WEARABLES_CONTEXT}_{collectionId}"))
 1275                pendingWearablesByContextRequestedTimes.Add($"{THIRD_PARTY_WEARABLES_CONTEXT}_{collectionId}", Time.real
 276
 1277            WebInterface.RequestThirdPartyWearables(
 278                userId,
 279                collectionId,
 280                $"{THIRD_PARTY_WEARABLES_CONTEXT}_{collectionId}"
 281            );
 282        }
 283        else
 284        {
 0285            awaitingWearablesByContextPromises.TryGetValue($"{THIRD_PARTY_WEARABLES_CONTEXT}_{collectionId}", out promis
 286        }
 287
 1288        return promiseResult;
 289    }
 290
 291    public static void RemoveWearablesInUse(List<string> wearablesInUseToRemove)
 292    {
 1336293        foreach (var wearableToRemove in wearablesInUseToRemove)
 294        {
 0295            if (wearablesInUseCounters.ContainsKey(wearableToRemove))
 296            {
 0297                wearablesInUseCounters[wearableToRemove]--;
 298            }
 299        }
 668300    }
 301
 302    private void ResolvePendingWearablePromise(string wearableId, WearableItem newWearableAddedIntoCatalog = null, strin
 303    {
 3420304        if (awaitingWearablePromises.TryGetValue(wearableId, out Promise<WearableItem> promise))
 305        {
 0306            awaitingWearablePromises.Remove(wearableId);
 307
 0308            if (string.IsNullOrEmpty(errorMessage))
 0309                promise.Resolve(newWearableAddedIntoCatalog);
 310            else
 0311                promise.Reject(errorMessage);
 312        }
 3420313    }
 314
 315    private void ResolvePendingWearablesByContextPromise(string context, WearableItem[] newWearablesAddedIntoCatalog = n
 316    {
 0317        if (awaitingWearablesByContextPromises.TryGetValue(context, out Promise<WearableItem[]> promise))
 318        {
 0319            awaitingWearablesByContextPromises.Remove(context);
 320
 0321            if (string.IsNullOrEmpty(errorMessage))
 0322                promise.Resolve(newWearablesAddedIntoCatalog);
 323            else
 0324                promise.Reject(errorMessage);
 325        }
 0326    }
 327
 328    private void CheckForSendingPendingRequests()
 329    {
 3214330        if (pendingRequestsToSend.Count > 0)
 331        {
 0332            foreach (var request in pendingRequestsToSend)
 333            {
 0334                if (!pendingWearableRequestedTimes.ContainsKey(request))
 0335                    pendingWearableRequestedTimes.Add(request, Time.realtimeSinceStartup);
 336            }
 337
 0338            WebInterface.RequestWearables(
 339                ownedByUser: null,
 340                wearableIds: pendingRequestsToSend.ToArray(),
 341                collectionIds: null,
 342                context: string.Join(",", pendingRequestsToSend.ToArray())
 343            );
 344
 0345            pendingRequestsToSend.Clear();
 346        }
 3214347    }
 348
 349    private void CheckForRequestsTimeOuts()
 350    {
 3214351        if (pendingWearableRequestedTimes.Count > 0)
 352        {
 0353            List<string> expiredRequestes = new List<string>();
 0354            foreach (var promiseRequestedTime in pendingWearableRequestedTimes)
 355            {
 0356                if ((Time.realtimeSinceStartup - promiseRequestedTime.Value) > REQUESTS_TIME_OUT_SECONDS)
 357                {
 0358                    expiredRequestes.Add(promiseRequestedTime.Key);
 359                }
 360            }
 361
 0362            foreach (var expiredRequestToRemove in expiredRequestes)
 363            {
 0364                pendingWearableRequestedTimes.Remove(expiredRequestToRemove);
 365
 0366                ResolvePendingWearablePromise(
 367                    expiredRequestToRemove,
 368                    null,
 369                    $"The request for the wearable '{expiredRequestToRemove}' has exceed the set timeout!");
 370            }
 371        }
 3214372    }
 373
 374    private void CheckForRequestsByContextTimeOuts()
 375    {
 3214376        if (pendingWearablesByContextRequestedTimes.Count > 0)
 377        {
 0378            List<string> expiredRequests = new List<string>();
 0379            foreach (var promiseByContextRequestedTime in pendingWearablesByContextRequestedTimes)
 380            {
 0381                if ((Time.realtimeSinceStartup - promiseByContextRequestedTime.Value) > REQUESTS_TIME_OUT_SECONDS)
 382                {
 0383                    expiredRequests.Add(promiseByContextRequestedTime.Key);
 384                }
 385            }
 386
 0387            foreach (var expiredRequestToRemove in expiredRequests)
 388            {
 0389                pendingWearablesByContextRequestedTimes.Remove(expiredRequestToRemove);
 390
 0391                ResolvePendingWearablesByContextPromise(
 392                    expiredRequestToRemove,
 393                    null,
 394                    $"The request for the wearable context '{expiredRequestToRemove}' has exceed the set timeout!");
 395            }
 396        }
 3214397    }
 398
 399    private void CheckForUnusedWearables()
 400    {
 0401        if (wearablesInUseCounters.Count > 0)
 402        {
 0403            List<string> wearablesToDestroy = new List<string>();
 0404            foreach (var wearableInUse in wearablesInUseCounters)
 405            {
 0406                if (wearableInUse.Value <= 0)
 407                {
 0408                    wearablesToDestroy.Add(wearableInUse.Key);
 409                }
 410            }
 411
 0412            foreach (var wearableToDestroy in wearablesToDestroy)
 413            {
 0414                wearableCatalog.Remove(wearableToDestroy);
 0415                wearablesInUseCounters.Remove(wearableToDestroy);
 416            }
 417        }
 0418    }
 419
 420    public void Remove(IEnumerable<string> wearableIds)
 421    {
 0422        foreach (var wearableId in wearableIds)
 0423            wearableCatalog.Remove(wearableId);
 0424    }
 425}