< 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:228
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;
 538            if (promises.TryGetValue(emote.id, out var emotePromises))
 39            {
 2240                foreach (Promise<WearableItem> promise in emotePromises)
 41                {
 642                    promise.Resolve(emote);
 43                }
 544                promises.Remove(emote.id);
 45            }
 46        }
 647    }
 48
 49    private void OnOwnedEmotesReceived(WearableItem[] receivedEmotes, string userId)
 50    {
 051        if (!ownedEmotesPromisesByUser.TryGetValue(userId, out HashSet<Promise<WearableItem[]>> ownedEmotesPromises) || 
 052            ownedEmotesPromises = new HashSet<Promise<WearableItem[]>>();
 53
 54        //Update emotes on use
 055        for (var i = 0; i < receivedEmotes.Length; i++)
 56        {
 057            var emote = receivedEmotes[i];
 058            if (!emotesOnUse.ContainsKey(emote.id))
 059                emotesOnUse[emote.id] = 0;
 060            emotesOnUse[emote.id] += ownedEmotesPromises.Count;
 61        }
 62
 063        OnEmotesReceived(receivedEmotes);
 64
 65        //Resolve ownedEmotesPromise
 066        ownedEmotesPromisesByUser.Remove(userId);
 067        foreach (Promise<WearableItem[]> promise in ownedEmotesPromises)
 68        {
 069            promise.Resolve(receivedEmotes);
 70        }
 071    }
 72
 73    private void EmbedEmotes(WearableItem[] embeddedEmotes)
 74    {
 33275        foreach (WearableItem embeddedEmote in embeddedEmotes)
 76        {
 13677            emotes[embeddedEmote.id] = embeddedEmote;
 13678            emotesOnUse[embeddedEmote.id] = 5000;
 79        }
 3080    }
 81
 082    public bool TryGetLoadedEmote(string id, out WearableItem emote) { return emotes.TryGetValue(id, out emote); }
 83
 84    public Promise<WearableItem[]> RequestOwnedEmotes(string userId)
 85    {
 086        var promise = new Promise<WearableItem[]>();
 087        if (!ownedEmotesPromisesByUser.ContainsKey(userId) || ownedEmotesPromisesByUser[userId] == null)
 088            ownedEmotesPromisesByUser[userId] = new HashSet<Promise<WearableItem[]>>();
 089        ownedEmotesPromisesByUser[userId].Add(promise);
 090        bridge.RequestOwnedEmotes(userId);
 91
 092        return promise;
 93    }
 94
 95    public async UniTask<WearableItem[]> RequestOwnedEmotesAsync(string userId, CancellationToken ct = default)
 96    {
 97        const int TIMEOUT = 60;
 098        CancellationTokenSource timeoutCTS = new CancellationTokenSource();
 099        var timeout = timeoutCTS.CancelAfterSlim(TimeSpan.FromSeconds(TIMEOUT));
 0100        var promise = RequestOwnedEmotes(userId);
 101        try
 102        {
 0103            ct.ThrowIfCancellationRequested();
 0104            var linkedCt = CancellationTokenSource.CreateLinkedTokenSource(ct, timeoutCTS.Token);
 0105            await promise.WithCancellation(linkedCt.Token).AttachExternalCancellation(linkedCt.Token);
 0106        }
 0107        catch (OperationCanceledException e)
 108        {
 0109            return null;
 110        }
 111        finally
 112        {
 0113            timeout?.Dispose();
 0114            timeoutCTS?.Dispose();
 115        }
 116
 0117        return promise.value;
 0118    }
 119
 120    public Promise<WearableItem> RequestEmote(string id)
 121    {
 42122        var promise = new Promise<WearableItem>();
 42123        if (!emotesOnUse.ContainsKey(id))
 21124            emotesOnUse[id] = 0;
 42125        emotesOnUse[id]++;
 42126        if (emotes.TryGetValue(id, out var emote))
 127        {
 3128            promise.Resolve(emote);
 3129            return promise;
 130        }
 131
 39132        if (!promises.ContainsKey(id) || promises[id] == null)
 21133            promises[id] = new HashSet<Promise<WearableItem>>();
 39134        promises[id].Add(promise);
 39135        bridge.RequestEmote(id);
 39136        return promise;
 137    }
 138
 139    public List<Promise<WearableItem>> RequestEmotes(IList<string> ids)
 140    {
 6141        List<Promise<WearableItem>> requestedPromises = new List<Promise<WearableItem>>(ids.Count);
 142
 38143        for (int i = 0; i < ids.Count; i++)
 144        {
 13145            string id = ids[i];
 13146            requestedPromises.Add(RequestEmote(id));
 147        }
 148
 6149        return requestedPromises;
 150    }
 151
 152    public async UniTask<WearableItem> RequestEmoteAsync(string id, CancellationToken ct = default)
 153    {
 154        const int TIMEOUT = 60;
 19155        CancellationTokenSource timeoutCTS = new CancellationTokenSource();
 19156        var timeout = timeoutCTS.CancelAfterSlim(TimeSpan.FromSeconds(TIMEOUT));
 19157        ct.ThrowIfCancellationRequested();
 19158        Promise<WearableItem> promise = RequestEmote(id);
 159        try
 160        {
 19161            var linkedCt = CancellationTokenSource.CreateLinkedTokenSource(ct, timeoutCTS.Token);
 51162            await promise.WithCancellation(linkedCt.Token).AttachExternalCancellation(linkedCt.Token);
 3163        }
 16164        catch (OperationCanceledException e)
 165        {
 16166            if (promises.ContainsKey(id))
 167            {
 16168                promises[id].Remove(promise);
 16169                if (promises[id].Count == 0)
 6170                    promises.Remove(id);
 171            }
 172
 16173            return null;
 174        }
 175        finally
 176        {
 19177            timeout?.Dispose();
 19178            timeoutCTS?.Dispose();
 179        }
 180
 3181        return promise.value;
 19182    }
 183
 184    public async UniTask<WearableItem[]> RequestEmotesAsync(IList<string> ids, CancellationToken ct = default)
 185    {
 6186        ct.ThrowIfCancellationRequested();
 187        try
 188        {
 18189            var tasks = ids.Select(x => RequestEmoteAsync(x, ct));
 16190            return await UniTask.WhenAll(tasks).AttachExternalCancellation(ct);
 191        }
 5192        catch (OperationCanceledException e)
 193        {
 5194            return null;
 195        }
 6196    }
 197
 198    public void ForgetEmote(string id)
 199    {
 3200        if (emotesOnUse.ContainsKey(id))
 201        {
 2202            emotesOnUse[id]--;
 2203            if (emotesOnUse[id] > 0) //We are still using this emote
 1204                return;
 1205            emotesOnUse.Remove(id);
 206        }
 207
 2208        if (!emotes.TryGetValue(id, out WearableItem emote))
 1209            return;
 210
 1211        emotes.Remove(id);
 1212    }
 213
 214    public void ForgetEmotes(IList<string> ids)
 215    {
 0216        for (int i = 0; i < ids.Count; i++)
 217        {
 0218            string id = ids[i];
 0219            ForgetEmote(id);
 220        }
 0221    }
 222
 223    public void Dispose()
 224    {
 7225        bridge.OnEmotesReceived -= OnEmotesReceived;
 7226        bridge.OnOwnedEmotesReceived -= OnOwnedEmotesReceived;
 7227    }
 228}