< Summary

Class:EmotesCatalogService
Assembly:EmotesCatalog
File(s):/tmp/workspace/unity-renderer/unity-renderer/Assets/DCLServices/EmotesCatalog/EmotesCatalogService.cs
Covered lines:76
Uncovered lines:36
Coverable lines:112
Total lines:231
Line coverage:67.8% (76 of 112)
Covered branches:0
Total branches:0

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity NPath complexity Sequence coverage
EmotesCatalogService(...)0%110100%
Initialize()0%110100%
OnEmotesReceived(...)0%660100%
OnOwnedEmotesReceived(...)0%42600%
EmbedEmotes(...)0%220100%
TryGetLoadedEmote(...)0%2100%
RequestOwnedEmotes(...)0%12300%
RequestOwnedEmotesAsync()0%56700%
RequestEmote(...)0%550100%
RequestEmotes(...)0%220100%
RequestEmoteAsync()0%990100%
RequestEmotesAsync()0%440100%
ForgetEmote(...)0%440100%
ForgetEmotes(...)0%6200%
Dispose()0%110100%

File(s)

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

#LineLine coverage
 1using System;
 2using System.Collections.Generic;
 3using System.Threading;
 4using Cysharp.Threading.Tasks;
 5using DCL.Helpers;
 6
 7public class EmotesCatalogService : IEmotesCatalogService
 8{
 9    private readonly IEmotesCatalogBridge bridge;
 10
 3011    internal readonly Dictionary<string, WearableItem> emotes = new Dictionary<string, WearableItem>();
 3012    internal readonly Dictionary<string, HashSet<Promise<WearableItem>>> promises = new Dictionary<string, HashSet<Promi
 3013    internal readonly Dictionary<string, int> emotesOnUse = new Dictionary<string, int>();
 3014    internal readonly Dictionary<string, HashSet<Promise<WearableItem[]>>> ownedEmotesPromisesByUser = new Dictionary<st
 15
 3016    public EmotesCatalogService(IEmotesCatalogBridge bridge, WearableItem[] embeddedEmotes)
 17    {
 3018        this.bridge = bridge;
 3019        EmbedEmotes(embeddedEmotes);
 3020    }
 21
 22    public void Initialize()
 23    {
 2924        bridge.OnEmotesReceived += OnEmotesReceived;
 2925        bridge.OnOwnedEmotesReceived += OnOwnedEmotesReceived;
 2926    }
 27
 28    private void OnEmotesReceived(WearableItem[] receivedEmotes)
 29    {
 2430        for (var i = 0; i < receivedEmotes.Length; i++)
 31        {
 632            WearableItem emote = receivedEmotes[i];
 33
 634            if (!emotesOnUse.ContainsKey(emote.id) || emotesOnUse[emote.id] <= 0)
 35                continue;
 36
 537            emotes[emote.id] = emote;
 38
 39
 40
 541            if (promises.TryGetValue(emote.id, out var emotePromises))
 42            {
 2243                foreach (Promise<WearableItem> promise in emotePromises)
 44                {
 645                    promise.Resolve(emote);
 46                }
 547                promises.Remove(emote.id);
 48            }
 49        }
 650    }
 51
 52    private void OnOwnedEmotesReceived(WearableItem[] receivedEmotes, string userId)
 53    {
 054        if (!ownedEmotesPromisesByUser.TryGetValue(userId, out HashSet<Promise<WearableItem[]>> ownedEmotesPromises) || 
 055            ownedEmotesPromises = new HashSet<Promise<WearableItem[]>>();
 56
 57        //Update emotes on use
 058        for (var i = 0; i < receivedEmotes.Length; i++)
 59        {
 060            var emote = receivedEmotes[i];
 061            if (!emotesOnUse.ContainsKey(emote.id))
 062                emotesOnUse[emote.id] = 0;
 063            emotesOnUse[emote.id] += ownedEmotesPromises.Count;
 64        }
 65
 066        OnEmotesReceived(receivedEmotes);
 67
 68        //Resolve ownedEmotesPromise
 069        ownedEmotesPromisesByUser.Remove(userId);
 070        foreach (Promise<WearableItem[]> promise in ownedEmotesPromises)
 71        {
 072            promise.Resolve(receivedEmotes);
 73        }
 074    }
 75
 76    private void EmbedEmotes(WearableItem[] embeddedEmotes)
 77    {
 33278        foreach (WearableItem embeddedEmote in embeddedEmotes)
 79        {
 13680            emotes[embeddedEmote.id] = embeddedEmote;
 13681            emotesOnUse[embeddedEmote.id] = 5000;
 82        }
 3083    }
 84
 085    public bool TryGetLoadedEmote(string id, out WearableItem emote) { return emotes.TryGetValue(id, out emote); }
 86
 87    public Promise<WearableItem[]> RequestOwnedEmotes(string userId)
 88    {
 089        var promise = new Promise<WearableItem[]>();
 090        if (!ownedEmotesPromisesByUser.ContainsKey(userId) || ownedEmotesPromisesByUser[userId] == null)
 091            ownedEmotesPromisesByUser[userId] = new HashSet<Promise<WearableItem[]>>();
 092        ownedEmotesPromisesByUser[userId].Add(promise);
 093        bridge.RequestOwnedEmotes(userId);
 94
 095        return promise;
 96    }
 97
 98    public async UniTask<WearableItem[]> RequestOwnedEmotesAsync(string userId, CancellationToken ct = default)
 99    {
 100        const int TIMEOUT = 60;
 0101        CancellationTokenSource timeoutCTS = new CancellationTokenSource();
 0102        var timeout = timeoutCTS.CancelAfterSlim(TimeSpan.FromSeconds(TIMEOUT));
 0103        var promise = RequestOwnedEmotes(userId);
 104        try
 105        {
 0106            ct.ThrowIfCancellationRequested();
 0107            var linkedCt = CancellationTokenSource.CreateLinkedTokenSource(ct, timeoutCTS.Token);
 0108            await promise.WithCancellation(linkedCt.Token).AttachExternalCancellation(linkedCt.Token);
 0109        }
 0110        catch (OperationCanceledException e)
 111        {
 0112            return null;
 113        }
 114        finally
 115        {
 0116            timeout?.Dispose();
 0117            timeoutCTS?.Dispose();
 118        }
 119
 0120        return promise.value;
 0121    }
 122
 123    public Promise<WearableItem> RequestEmote(string id)
 124    {
 42125        var promise = new Promise<WearableItem>();
 42126        if (!emotesOnUse.ContainsKey(id))
 21127            emotesOnUse[id] = 0;
 42128        emotesOnUse[id]++;
 42129        if (emotes.TryGetValue(id, out var emote))
 130        {
 3131            promise.Resolve(emote);
 3132            return promise;
 133        }
 134
 39135        if (!promises.ContainsKey(id) || promises[id] == null)
 21136            promises[id] = new HashSet<Promise<WearableItem>>();
 39137        promises[id].Add(promise);
 39138        bridge.RequestEmote(id);
 39139        return promise;
 140    }
 141
 142    public List<Promise<WearableItem>> RequestEmotes(IList<string> ids)
 143    {
 6144        List<Promise<WearableItem>> requestedPromises = new List<Promise<WearableItem>>(ids.Count);
 145
 38146        for (int i = 0; i < ids.Count; i++)
 147        {
 13148            string id = ids[i];
 13149            requestedPromises.Add(RequestEmote(id));
 150        }
 151
 6152        return requestedPromises;
 153    }
 154
 155    public async UniTask<WearableItem> RequestEmoteAsync(string id, CancellationToken ct = default)
 156    {
 157        const int TIMEOUT = 45;
 19158        CancellationTokenSource timeoutCTS = new CancellationTokenSource();
 19159        var timeout = timeoutCTS.CancelAfterSlim(TimeSpan.FromSeconds(TIMEOUT));
 19160        ct.ThrowIfCancellationRequested();
 19161        Promise<WearableItem> promise = RequestEmote(id);
 162        try
 163        {
 19164            var linkedCt = CancellationTokenSource.CreateLinkedTokenSource(ct, timeoutCTS.Token);
 51165            await promise.WithCancellation(linkedCt.Token).AttachExternalCancellation(linkedCt.Token);
 3166        }
 16167        catch (OperationCanceledException ex)
 168        {
 16169            if (promises.ContainsKey(id))
 170            {
 16171                promises[id].Remove(promise);
 16172                if (promises[id].Count == 0)
 6173                    promises.Remove(id);
 174            }
 175
 16176            return null;
 177        }
 178        finally
 179        {
 19180            timeout?.Dispose();
 19181            timeoutCTS?.Dispose();
 182        }
 183
 3184        return promise.value;
 19185    }
 186
 187    public async UniTask<WearableItem[]> RequestEmotesAsync(IList<string> ids, CancellationToken ct = default)
 188    {
 6189        ct.ThrowIfCancellationRequested();
 190        try
 191        {
 18192            var tasks = ids.Select(x => RequestEmoteAsync(x, ct));
 16193            return await UniTask.WhenAll(tasks).AttachExternalCancellation(ct);
 194        }
 5195        catch (OperationCanceledException e)
 196        {
 5197            return null;
 198        }
 6199    }
 200
 201    public void ForgetEmote(string id)
 202    {
 3203        if (emotesOnUse.ContainsKey(id))
 204        {
 2205            emotesOnUse[id]--;
 2206            if (emotesOnUse[id] > 0) //We are still using this emote
 1207                return;
 1208            emotesOnUse.Remove(id);
 209        }
 210
 2211        if (!emotes.TryGetValue(id, out WearableItem emote))
 1212            return;
 213
 1214        emotes.Remove(id);
 1215    }
 216
 217    public void ForgetEmotes(IList<string> ids)
 218    {
 0219        for (int i = 0; i < ids.Count; i++)
 220        {
 0221            string id = ids[i];
 0222            ForgetEmote(id);
 223        }
 0224    }
 225
 226    public void Dispose()
 227    {
 7228        bridge.OnEmotesReceived -= OnEmotesReceived;
 7229        bridge.OnOwnedEmotesReceived -= OnOwnedEmotesReceived;
 7230    }
 231}