< 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:105
Coverable lines:168
Total lines:422
Line coverage:37.5% (63 of 168)
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%30500%
RemoveWearablesFromCatalog(...)0%6200%
ClearWearableCatalog()0%6200%
RequestWearable(...)0%5.264057.14%
RequestOwnedWearables(...)0%12300%
RequestBaseWearables()0%12300%
RequestThirdPartyWearablesByCollection(...)0%3.013088.89%
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
 022    public static CatalogController i { get; private set; }
 23
 811224    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
 18436    public void Awake() { i = this; }
 37
 38    private void Update()
 39    {
 40        // All the requests happened during the same frames interval are sent together
 22941        if (Time.frameCount % FRAMES_TO_CHECK_FOR_SENDING_PENDING_REQUESTS == 0)
 42        {
 22943            CheckForSendingPendingRequests();
 22944            CheckForRequestsTimeOuts();
 22945            CheckForRequestsByContextTimeOuts();
 46        }
 47
 48        // Check unused wearables (to be removed from our catalog) only every [TIME_TO_CHECK_FOR_UNUSED_WEARABLES] secon
 22949        timeSinceLastUnusedWearablesCheck += Time.deltaTime;
 22950        if (timeSinceLastUnusedWearablesCheck >= TIME_TO_CHECK_FOR_UNUSED_WEARABLES)
 51        {
 052            CheckForUnusedWearables();
 053            timeSinceLastUnusedWearablesCheck = 0f;
 54        }
 22955    }
 56
 57    public static void Clear()
 58    {
 75359        wearableCatalog.Clear();
 75360        wearablesInUseCounters.Clear();
 75361        awaitingWearablePromises.Clear();
 75362        pendingWearableRequestedTimes.Clear();
 75363        awaitingWearablesByContextPromises.Clear();
 75364        pendingWearablesByContextRequestedTimes.Clear();
 75365        pendingRequestsToSend.Clear();
 75366    }
 67
 68    //This temporary until the emotes are in the content server
 69    public void EmbedWearables(IEnumerable<WearableItem> wearables)
 70    {
 16071        foreach (WearableItem wearableItem in wearables)
 72        {
 7673            wearableCatalog[wearableItem.id] = wearableItem;
 7674            wearablesInUseCounters[wearableItem.id] = 10000; //A high value to ensure they are not removed
 75        }
 476    }
 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    {
 6364111        foreach (WearableItem wearableItem in wearableItems)
 112        {
 3096113            if (!wearableCatalog.ContainsKey(wearableItem.id))
 114            {
 3096115                wearableItem.SanitizeHidesLists();
 3096116                wearableCatalog.Add(wearableItem.id, wearableItem);
 117
 3096118                if (!wearablesInUseCounters.ContainsKey(wearableItem.id))
 3096119                    wearablesInUseCounters.Add(wearableItem.id, 1);
 120
 3096121                ResolvePendingWearablePromise(wearableItem.id, wearableItem);
 122
 3096123                pendingWearableRequestedTimes.Remove(wearableItem.id);
 124            }
 125        }
 86126    }
 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 == BASE_WEARABLES_CONTEXT ||
 135            requestFailedResponse.context.Contains(THIRD_PARTY_WEARABLES_CONTEXT) ||
 136            requestFailedResponse.context.Contains(OWNED_WEARABLES_CONTEXT))
 137        {
 0138            ResolvePendingWearablesByContextPromise(
 139                requestFailedResponse.context,
 140                null,
 141                requestFailedResponse.error);
 0142        }
 143        else
 144        {
 0145            string[] failedWearablesIds = requestFailedResponse.context.Split(',');
 0146            for (int i = 0; i < failedWearablesIds.Length; i++)
 147            {
 0148                ResolvePendingWearablePromise(
 149                    failedWearablesIds[i],
 150                    null,
 151                    $"The request for the wearable '{failedWearablesIds[i]}' has failed: {requestFailedResponse.error}")
 152            }
 153        }
 0154    }
 155
 156    public void RemoveWearablesFromCatalog(string payload)
 157    {
 0158        string[] itemIDs = JsonUtility.FromJson<string[]>(payload);
 159
 0160        int count = itemIDs.Length;
 0161        for (int i = 0; i < count; ++i)
 162        {
 0163            wearableCatalog.Remove(itemIDs[i]);
 0164            wearablesInUseCounters.Remove(itemIDs[i]);
 165        }
 0166    }
 167
 168    public void ClearWearableCatalog()
 169    {
 0170        wearableCatalog?.Clear();
 0171        wearablesInUseCounters.Clear();
 0172    }
 173
 174    public static Promise<WearableItem> RequestWearable(string wearableId)
 175    {
 15176        if (VERBOSE)
 0177            Debug.Log("request wearables: " + wearableId);
 178
 179        Promise<WearableItem> promiseResult;
 180
 15181        if (wearableCatalog.TryGetValue(wearableId, out WearableItem wearable))
 182        {
 0183            wearablesInUseCounters[wearableId]++;
 0184            promiseResult = new Promise<WearableItem>();
 0185            promiseResult.Resolve(wearable);
 0186        }
 187        else
 188        {
 15189            if (!awaitingWearablePromises.ContainsKey(wearableId))
 190            {
 15191                promiseResult = new Promise<WearableItem>();
 15192                awaitingWearablePromises.Add(wearableId, promiseResult);
 193
 194                // We accumulate all the requests during the same frames interval to send them all together
 15195                pendingRequestsToSend.Add(wearableId);
 15196            }
 197            else
 198            {
 0199                awaitingWearablePromises.TryGetValue(wearableId, out promiseResult);
 200            }
 201        }
 202
 15203        return promiseResult;
 204    }
 205
 206    public static Promise<WearableItem[]> RequestOwnedWearables(string userId)
 207    {
 208        Promise<WearableItem[]> promiseResult;
 209
 0210        if (!awaitingWearablesByContextPromises.ContainsKey($"{OWNED_WEARABLES_CONTEXT}{userId}"))
 211        {
 0212            promiseResult = new Promise<WearableItem[]>();
 213
 0214            awaitingWearablesByContextPromises.Add($"{OWNED_WEARABLES_CONTEXT}{userId}", promiseResult);
 215
 0216            if (!pendingWearablesByContextRequestedTimes.ContainsKey($"{OWNED_WEARABLES_CONTEXT}{userId}"))
 0217                pendingWearablesByContextRequestedTimes.Add($"{OWNED_WEARABLES_CONTEXT}{userId}", Time.realtimeSinceStar
 218
 0219            WebInterface.RequestWearables(
 220                ownedByUser: userId,
 221                wearableIds: null,
 222                collectionIds: null,
 223                context: $"{OWNED_WEARABLES_CONTEXT}{userId}"
 224            );
 0225        }
 226        else
 227        {
 0228            awaitingWearablesByContextPromises.TryGetValue($"{OWNED_WEARABLES_CONTEXT}{userId}", out promiseResult);
 229        }
 230
 0231        return promiseResult;
 232    }
 233
 234    public static Promise<WearableItem[]> RequestBaseWearables()
 235    {
 236        Promise<WearableItem[]> promiseResult;
 237
 0238        if (!awaitingWearablesByContextPromises.ContainsKey(BASE_WEARABLES_CONTEXT))
 239        {
 0240            promiseResult = new Promise<WearableItem[]>();
 0241            awaitingWearablesByContextPromises.Add(BASE_WEARABLES_CONTEXT, promiseResult);
 242
 0243            if (!pendingWearablesByContextRequestedTimes.ContainsKey(BASE_WEARABLES_CONTEXT))
 0244                pendingWearablesByContextRequestedTimes.Add(BASE_WEARABLES_CONTEXT, Time.realtimeSinceStartup);
 245
 0246            WebInterface.RequestWearables(
 247                ownedByUser: null,
 248                wearableIds: null,
 249                collectionIds: new string[] { "urn:decentraland:off-chain:base-avatars" },
 250                context: BASE_WEARABLES_CONTEXT
 251            );
 0252        }
 253        else
 254        {
 0255            awaitingWearablesByContextPromises.TryGetValue(BASE_WEARABLES_CONTEXT, out promiseResult);
 256        }
 257
 0258        return promiseResult;
 259    }
 260
 261    public static Promise<WearableItem[]> RequestThirdPartyWearablesByCollection(string userId, string collectionId)
 262    {
 263        Promise<WearableItem[]> promiseResult;
 264
 1265        if (!awaitingWearablesByContextPromises.ContainsKey($"{THIRD_PARTY_WEARABLES_CONTEXT}_{collectionId}"))
 266        {
 1267            promiseResult = new Promise<WearableItem[]>();
 268
 1269            awaitingWearablesByContextPromises.Add($"{THIRD_PARTY_WEARABLES_CONTEXT}_{collectionId}", promiseResult);
 270
 1271            if (!pendingWearablesByContextRequestedTimes.ContainsKey($"{THIRD_PARTY_WEARABLES_CONTEXT}_{collectionId}"))
 1272                pendingWearablesByContextRequestedTimes.Add($"{THIRD_PARTY_WEARABLES_CONTEXT}_{collectionId}", Time.real
 273
 1274            WebInterface.RequestThirdPartyWearables(
 275                userId,
 276                collectionId,
 277                $"{THIRD_PARTY_WEARABLES_CONTEXT}_{collectionId}"
 278            );
 1279        }
 280        else
 281        {
 0282            awaitingWearablesByContextPromises.TryGetValue($"{THIRD_PARTY_WEARABLES_CONTEXT}_{collectionId}", out promis
 283        }
 284
 1285        return promiseResult;
 286    }
 287
 288    public static void RemoveWearablesInUse(List<string> wearablesInUseToRemove)
 289    {
 2096290        foreach (var wearableToRemove in wearablesInUseToRemove)
 291        {
 0292            if (wearablesInUseCounters.ContainsKey(wearableToRemove))
 293            {
 0294                wearablesInUseCounters[wearableToRemove]--;
 295            }
 296        }
 1048297    }
 298
 299    private void ResolvePendingWearablePromise(string wearableId, WearableItem newWearableAddedIntoCatalog = null, strin
 300    {
 3096301        if (awaitingWearablePromises.TryGetValue(wearableId, out Promise<WearableItem> promise))
 302        {
 0303            awaitingWearablePromises.Remove(wearableId);
 304
 0305            if (string.IsNullOrEmpty(errorMessage))
 0306                promise.Resolve(newWearableAddedIntoCatalog);
 307            else
 0308                promise.Reject(errorMessage);
 309        }
 3096310    }
 311
 312    private void ResolvePendingWearablesByContextPromise(string context, WearableItem[] newWearablesAddedIntoCatalog = n
 313    {
 0314        if (awaitingWearablesByContextPromises.TryGetValue(context, out Promise<WearableItem[]> promise))
 315        {
 0316            awaitingWearablesByContextPromises.Remove(context);
 317
 0318            if (string.IsNullOrEmpty(errorMessage))
 0319                promise.Resolve(newWearablesAddedIntoCatalog);
 320            else
 0321                promise.Reject(errorMessage);
 322        }
 0323    }
 324
 325    private void CheckForSendingPendingRequests()
 326    {
 229327        if (pendingRequestsToSend.Count > 0)
 328        {
 0329            foreach (var request in pendingRequestsToSend)
 330            {
 0331                if (!pendingWearableRequestedTimes.ContainsKey(request))
 0332                    pendingWearableRequestedTimes.Add(request, Time.realtimeSinceStartup);
 333            }
 334
 0335            WebInterface.RequestWearables(
 336                ownedByUser: null,
 337                wearableIds: pendingRequestsToSend.ToArray(),
 338                collectionIds: null,
 339                context: string.Join(",", pendingRequestsToSend.ToArray())
 340            );
 341
 0342            pendingRequestsToSend.Clear();
 343        }
 229344    }
 345
 346    private void CheckForRequestsTimeOuts()
 347    {
 229348        if (pendingWearableRequestedTimes.Count > 0)
 349        {
 0350            List<string> expiredRequestes = new List<string>();
 0351            foreach (var promiseRequestedTime in pendingWearableRequestedTimes)
 352            {
 0353                if ((Time.realtimeSinceStartup - promiseRequestedTime.Value) > REQUESTS_TIME_OUT_SECONDS)
 354                {
 0355                    expiredRequestes.Add(promiseRequestedTime.Key);
 356                }
 357            }
 358
 0359            foreach (var expiredRequestToRemove in expiredRequestes)
 360            {
 0361                pendingWearableRequestedTimes.Remove(expiredRequestToRemove);
 362
 0363                ResolvePendingWearablePromise(
 364                    expiredRequestToRemove,
 365                    null,
 366                    $"The request for the wearable '{expiredRequestToRemove}' has exceed the set timeout!");
 367            }
 368        }
 229369    }
 370
 371    private void CheckForRequestsByContextTimeOuts()
 372    {
 229373        if (pendingWearablesByContextRequestedTimes.Count > 0)
 374        {
 0375            List<string> expiredRequests = new List<string>();
 0376            foreach (var promiseByContextRequestedTime in pendingWearablesByContextRequestedTimes)
 377            {
 0378                if ((Time.realtimeSinceStartup - promiseByContextRequestedTime.Value) > REQUESTS_TIME_OUT_SECONDS)
 379                {
 0380                    expiredRequests.Add(promiseByContextRequestedTime.Key);
 381                }
 382            }
 383
 0384            foreach (var expiredRequestToRemove in expiredRequests)
 385            {
 0386                pendingWearablesByContextRequestedTimes.Remove(expiredRequestToRemove);
 387
 0388                ResolvePendingWearablesByContextPromise(
 389                    expiredRequestToRemove,
 390                    null,
 391                    $"The request for the wearable context '{expiredRequestToRemove}' has exceed the set timeout!");
 392            }
 393        }
 229394    }
 395
 396    private void CheckForUnusedWearables()
 397    {
 0398        if (wearablesInUseCounters.Count > 0)
 399        {
 0400            List<string> wearablesToDestroy = new List<string>();
 0401            foreach (var wearableInUse in wearablesInUseCounters)
 402            {
 0403                if (wearableInUse.Value <= 0)
 404                {
 0405                    wearablesToDestroy.Add(wearableInUse.Key);
 406                }
 407            }
 408
 0409            foreach (var wearableToDestroy in wearablesToDestroy)
 410            {
 0411                wearableCatalog.Remove(wearableToDestroy);
 0412                wearablesInUseCounters.Remove(wearableToDestroy);
 413            }
 414        }
 0415    }
 416
 417    public void Remove(IEnumerable<string> wearableIds)
 418    {
 0419        foreach (var wearableId in wearableIds)
 0420            wearableCatalog.Remove(wearableId);
 0421    }
 422}