< Summary

Class:DCLServices.EmotesCatalog.EmotesCatalogService.WebInterfaceEmotesCatalogService
Assembly:EmotesCatalog
File(s):/tmp/workspace/unity-renderer/unity-renderer/Assets/DCLServices/EmotesCatalog/EmotesCatalogService/WebInterfaceEmotesCatalogService.cs
Covered lines:9
Uncovered lines:148
Coverable lines:157
Total lines:326
Line coverage:5.7% (9 of 157)
Covered branches:0
Total branches:0
Covered methods:1
Total methods:23
Method coverage:4.3% (1 of 23)

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity NPath complexity Sequence coverage
WebInterfaceEmotesCatalogService(...)0%110100%
InitializeAsyncEmbeddedEmotes()0%20400%
Initialize()0%2100%
OnEmotesReceived(...)0%42600%
OnEmoteRejected(...)0%12300%
OnOwnedEmotesReceived(...)0%42600%
EmbedEmotes()0%12300%
TryGetLoadedEmote(...)0%2100%
RequestEmoteFromBuilderAsync(...)0%2100%
RequestOwnedEmotes(...)0%12300%
RequestOwnedEmotesAsync()0%56700%
RequestEmoteCollectionAsync(...)0%2100%
RequestEmote(...)0%30500%
RequestEmotes(...)0%6200%
RequestEmoteAsync()0%90900%
RequestEmotesAsync()0%20400%
ForgetEmote(...)0%20400%
ForgetEmotes(...)0%6200%
TryGetOwnedUrn(...)0%2100%
GetEmbeddedEmotes()0%20400%
RequestEmoteCollectionInBuilderAsync(...)0%2100%
Dispose()0%2100%
DisposeCT()0%6200%

File(s)

/tmp/workspace/unity-renderer/unity-renderer/Assets/DCLServices/EmotesCatalog/EmotesCatalogService/WebInterfaceEmotesCatalogService.cs

#LineLine coverage
 1using Cysharp.Threading.Tasks;
 2using DCL.Emotes;
 3using DCL.Helpers;
 4using DCL.Providers;
 5using System;
 6using System.Collections.Generic;
 7using System.Threading;
 8using UnityEngine;
 9using PromiseAsyncExtensions = DCL.Helpers.PromiseAsyncExtensions;
 10
 11namespace DCLServices.EmotesCatalog.EmotesCatalogService
 12{
 13    [Obsolete("This service will be deprecated by LambdasEmotesCatalogService in the future.")]
 14    public class WebInterfaceEmotesCatalogService : IEmotesCatalogService
 15    {
 16        private EmotesCatalogBridge bridge;
 17
 4018        private readonly Dictionary<string, WearableItem> emotes = new ();
 4019        private readonly Dictionary<string, HashSet<Promise<WearableItem>>> promises = new ();
 4020        private readonly Dictionary<string, int> emotesOnUse = new ();
 4021        private readonly Dictionary<string, HashSet<Promise<IReadOnlyList<WearableItem>>>> ownedEmotesPromisesByUser = n
 22        private readonly IAddressableResourceProvider addressableResourceProvider;
 23
 24        private EmbeddedEmotesSO embeddedEmotesSO;
 25        private CancellationTokenSource addressableCTS;
 4026        private int retryCount = 3;
 27
 4028        public WebInterfaceEmotesCatalogService(EmotesCatalogBridge bridge, IAddressableResourceProvider addressableReso
 29        {
 4030            this.bridge = bridge;
 4031            this.addressableResourceProvider = addressableResourceProvider;
 4032        }
 33
 34        private async UniTaskVoid InitializeAsyncEmbeddedEmotes()
 35        {
 36            try
 37            {
 038                addressableCTS = new CancellationTokenSource();
 039                embeddedEmotesSO = await addressableResourceProvider.GetAddressable<EmbeddedEmotesSO>("EmbeddedEmotes.as
 040            }
 041            catch (OperationCanceledException) { return; }
 042            catch (Exception e)
 43            {
 044                retryCount--;
 045                if (retryCount < 0)
 46                {
 047                    embeddedEmotesSO = ScriptableObject.CreateInstance<EmbeddedEmotesSO>();
 048                    embeddedEmotesSO.Clear();
 049                    throw new Exception("Embedded Emotes retry limit reached, they wont work correctly. Please check the
 50                }
 51
 052                Debug.LogWarning("Retrying embedded emotes addressables async request...");
 053                DisposeCT();
 054                InitializeAsyncEmbeddedEmotes().Forget();
 055            }
 56
 057            EmbedEmotes();
 058        }
 59
 60        public void Initialize()
 61        {
 062            InitializeAsyncEmbeddedEmotes().Forget();
 063            bridge.OnEmotesReceived += OnEmotesReceived;
 064            bridge.OnEmoteRejected += OnEmoteRejected;
 065            bridge.OnOwnedEmotesReceived += OnOwnedEmotesReceived;
 066        }
 67
 68        private void OnEmotesReceived(WearableItem[] receivedEmotes)
 69        {
 070            for (var i = 0; i < receivedEmotes.Length; i++)
 71            {
 072                WearableItem emote = receivedEmotes[i];
 73
 074                if (!emotesOnUse.ContainsKey(emote.id) || emotesOnUse[emote.id] <= 0)
 75                    continue;
 76
 077                emotes[emote.id] = emote;
 78
 079                if (promises.TryGetValue(emote.id, out var emotePromises))
 80                {
 081                    foreach (Promise<WearableItem> promise in emotePromises)
 82                    {
 083                        promise.Resolve(emote);
 84                    }
 85
 086                    promises.Remove(emote.id);
 87                }
 88            }
 089        }
 90
 91        private void OnEmoteRejected(string emoteId, string errorMessage)
 92        {
 093            if (promises.TryGetValue(emoteId, out var setOfPromises))
 94            {
 095                foreach(var promise in setOfPromises)
 096                    promise.Reject(errorMessage);
 97
 098                promises.Remove(emoteId);
 099                emotesOnUse.Remove(emoteId);
 0100                emotes.Remove(emoteId);
 101            }
 0102        }
 103
 104        private void OnOwnedEmotesReceived(WearableItem[] receivedEmotes, string userId)
 105        {
 0106            if (!ownedEmotesPromisesByUser.TryGetValue(userId, out HashSet<Promise<IReadOnlyList<WearableItem>>> ownedEm
 0107                ownedEmotesPromises = new HashSet<Promise<IReadOnlyList<WearableItem>>>();
 108
 109            //Update emotes on use
 0110            for (var i = 0; i < receivedEmotes.Length; i++)
 111            {
 0112                var emote = receivedEmotes[i];
 0113                if (!emotesOnUse.ContainsKey(emote.id))
 0114                    emotesOnUse[emote.id] = 0;
 0115                emotesOnUse[emote.id] += ownedEmotesPromises.Count;
 116            }
 117
 0118            OnEmotesReceived(receivedEmotes);
 119
 120            //Resolve ownedEmotesPromise
 0121            ownedEmotesPromisesByUser.Remove(userId);
 0122            foreach (Promise<IReadOnlyList<WearableItem>> promise in ownedEmotesPromises)
 123            {
 0124                promise.Resolve(receivedEmotes);
 125            }
 0126        }
 127
 128        private void EmbedEmotes()
 129        {
 0130            foreach (EmbeddedEmote embeddedEmote in embeddedEmotesSO.GetAllEmotes())
 131            {
 0132                emotes[embeddedEmote.id] = embeddedEmote;
 0133                emotesOnUse[embeddedEmote.id] = 5000;
 134            }
 0135        }
 136
 0137        public bool TryGetLoadedEmote(string id, out WearableItem emote) { return emotes.TryGetValue(id, out emote); }
 138
 139        public UniTask<WearableItem> RequestEmoteFromBuilderAsync(string emoteId, CancellationToken cancellationToken) =
 0140            throw new NotImplementedException("Implemented in LambdasEmotesCatalogService");
 141
 142        public Promise<IReadOnlyList<WearableItem>> RequestOwnedEmotes(string userId)
 143        {
 0144            var promise = new Promise<IReadOnlyList<WearableItem>>();
 0145            if (!ownedEmotesPromisesByUser.ContainsKey(userId) || ownedEmotesPromisesByUser[userId] == null)
 0146                ownedEmotesPromisesByUser[userId] = new HashSet<Promise<IReadOnlyList<WearableItem>>>();
 0147            ownedEmotesPromisesByUser[userId].Add(promise);
 0148            bridge.RequestOwnedEmotes(userId);
 149
 0150            return promise;
 151        }
 152
 153        public async UniTask<IReadOnlyList<WearableItem>> RequestOwnedEmotesAsync(string userId, CancellationToken ct = 
 154        {
 155            const int TIMEOUT = 60;
 0156            CancellationTokenSource timeoutCTS = new CancellationTokenSource();
 0157            var timeout = timeoutCTS.CancelAfterSlim(TimeSpan.FromSeconds(TIMEOUT));
 0158            var promise = RequestOwnedEmotes(userId);
 159            try
 160            {
 0161                ct.ThrowIfCancellationRequested();
 0162                var linkedCt = CancellationTokenSource.CreateLinkedTokenSource(ct, timeoutCTS.Token);
 0163                await PromiseAsyncExtensions.WithCancellation(promise, linkedCt.Token);
 0164            }
 0165            catch (OperationCanceledException e)
 166            {
 0167                return null;
 168            }
 169            finally
 170            {
 0171                timeout?.Dispose();
 0172                timeoutCTS?.Dispose();
 173            }
 174
 0175            return promise.value;
 0176        }
 177
 178        public UniTask<IReadOnlyList<WearableItem>> RequestEmoteCollectionAsync(IEnumerable<string> collectionIds,
 179            CancellationToken cancellationToken, List<WearableItem> emoteBuffer = null) =>
 0180            throw new NotImplementedException("Implemented in LambdasEmotesCatalogService");
 181
 182        public Promise<WearableItem> RequestEmote(string id)
 183        {
 0184            var promise = new Promise<WearableItem>();
 0185            if (!emotesOnUse.ContainsKey(id))
 0186                emotesOnUse[id] = 0;
 0187            emotesOnUse[id]++;
 0188            if (emotes.TryGetValue(id, out var emote))
 189            {
 0190                promise.Resolve(emote);
 0191                return promise;
 192            }
 193
 0194            if (!promises.ContainsKey(id) || promises[id] == null)
 0195                promises[id] = new HashSet<Promise<WearableItem>>();
 0196            promises[id].Add(promise);
 0197            bridge.RequestEmote(id);
 0198            return promise;
 199        }
 200
 201        public List<Promise<WearableItem>> RequestEmotes(IList<string> ids)
 202        {
 0203            List<Promise<WearableItem>> requestedPromises = new List<Promise<WearableItem>>(ids.Count);
 204
 0205            for (int i = 0; i < ids.Count; i++)
 206            {
 0207                string id = ids[i];
 0208                requestedPromises.Add(RequestEmote(id));
 209            }
 210
 0211            return requestedPromises;
 212        }
 213
 214        public async UniTask<WearableItem> RequestEmoteAsync(string id, CancellationToken ct = default)
 215        {
 216            const int TIMEOUT = 45;
 0217            CancellationTokenSource timeoutCTS = new CancellationTokenSource();
 0218            var timeout = timeoutCTS.CancelAfterSlim(TimeSpan.FromSeconds(TIMEOUT));
 0219            ct.ThrowIfCancellationRequested();
 0220            Promise<WearableItem> promise = RequestEmote(id);
 221            try
 222            {
 0223                var linkedCt = CancellationTokenSource.CreateLinkedTokenSource(ct, timeoutCTS.Token);
 0224                await PromiseAsyncExtensions.WithCancellation(promise, linkedCt.Token);
 0225            }
 0226            catch (PromiseException ex)
 227            {
 0228                Debug.LogWarning($"Emote with id:{id} was rejected");
 0229                return null;
 230            }
 0231            catch (OperationCanceledException ex)
 232            {
 0233                if (promises.ContainsKey(id))
 234                {
 0235                    promises[id].Remove(promise);
 0236                    if (promises[id].Count == 0)
 0237                        promises.Remove(id);
 238                }
 239
 0240                return null;
 241            }
 242            finally
 243            {
 0244                timeout?.Dispose();
 0245                timeoutCTS?.Dispose();
 246            }
 247
 0248            return promise.value;
 0249        }
 250
 251        public async UniTask<IReadOnlyList<WearableItem>> RequestEmotesAsync(IList<string> ids, CancellationToken ct = d
 252        {
 0253            ct.ThrowIfCancellationRequested();
 254            try
 255            {
 0256                var tasks = ids.Select(x => RequestEmoteAsync(x, ct));
 0257                return await UniTask.WhenAll(tasks).AttachExternalCancellation(ct);
 258            }
 0259            catch (OperationCanceledException e)
 260            {
 0261                return null;
 262            }
 0263        }
 264
 265        public void ForgetEmote(string id)
 266        {
 0267            if (emotesOnUse.ContainsKey(id))
 268            {
 0269                emotesOnUse[id]--;
 0270                if (emotesOnUse[id] > 0) //We are still using this emote
 0271                    return;
 0272                emotesOnUse.Remove(id);
 273            }
 274
 0275            if (!emotes.TryGetValue(id, out WearableItem emote))
 0276                return;
 277
 0278            emotes.Remove(id);
 0279        }
 280
 281        public void ForgetEmotes(IList<string> ids)
 282        {
 0283            for (int i = 0; i < ids.Count; i++)
 284            {
 0285                string id = ids[i];
 0286                ForgetEmote(id);
 287            }
 0288        }
 289
 290        public bool TryGetOwnedUrn(string shortenedUrn, out string extendedUrn)
 291        {
 0292            extendedUrn = "";
 0293            return false;
 294        }
 295
 296        public async UniTask<EmbeddedEmotesSO> GetEmbeddedEmotes()
 297        {
 0298            if(embeddedEmotesSO == null)
 0299                await UniTask.WaitUntil(() => embeddedEmotesSO != null);
 0300            return embeddedEmotesSO;
 0301        }
 302
 303        public UniTask<IReadOnlyList<WearableItem>> RequestEmoteCollectionInBuilderAsync(IEnumerable<string> collectionI
 304            CancellationToken cancellationToken, List<WearableItem> emoteBuffer = null) =>
 0305            throw new NotImplementedException("Implemented in LambdasEmotesCatalogService");
 306
 307        public void Dispose()
 308        {
 0309            bridge.OnEmotesReceived -= OnEmotesReceived;
 0310            bridge.OnEmoteRejected -= OnEmoteRejected;
 0311            bridge.OnOwnedEmotesReceived -= OnOwnedEmotesReceived;
 0312            DisposeCT();
 0313        }
 314
 315        private void DisposeCT()
 316        {
 0317            if (addressableCTS != null)
 318            {
 0319                addressableCTS.Cancel();
 0320                addressableCTS.Dispose();
 0321                addressableCTS = null;
 322            }
 0323        }
 324
 325    }
 326}