< Summary

Class:DCL.Emotes.EmoteAnimationsTracker
Assembly:EmoteAnimationsPlugin
File(s):/tmp/workspace/unity-renderer/unity-renderer/Assets/DCLPlugins/EmoteAnimations/EmoteAnimationsTracker.cs
Covered lines:46
Uncovered lines:21
Coverable lines:67
Total lines:149
Line coverage:68.6% (46 of 67)
Covered branches:0
Total branches:0

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity NPath complexity Sequence coverage
EmoteAnimationsTracker(...)0%110100%
InitializeEmbeddedEmotes()0%440100%
OnRefCountUpdated(...)0%220100%
InitializeEmotes(...)0%330100%
LoadEmote()0%40.778020%
UnloadEmote(...)0%3.043083.33%
Dispose()0%6200%

File(s)

/tmp/workspace/unity-renderer/unity-renderer/Assets/DCLPlugins/EmoteAnimations/EmoteAnimationsTracker.cs

#LineLine coverage
 1using System;
 2using System.Collections.Generic;
 3using System.Linq;
 4using System.Runtime.ExceptionServices;
 5using System.Threading;
 6using AvatarSystem;
 7using Cysharp.Threading.Tasks;
 8using DCL.Configuration;
 9using UnityEngine;
 10using Object = UnityEngine.Object;
 11
 12namespace DCL.Emotes
 13{
 14    public class EmoteAnimationsTracker : IDisposable
 15    {
 16        internal readonly DataStore_Emotes dataStore;
 17        internal readonly EmoteAnimationLoaderFactory emoteAnimationLoaderFactory;
 18        internal readonly IWearableItemResolver wearableItemResolver;
 19        private readonly IEmotesCatalogService emotesCatalogService;
 20
 321        internal Dictionary<(string bodyshapeId, string emoteId), IEmoteAnimationLoader> loaders = new Dictionary<(strin
 22
 323        private CancellationTokenSource cts = new CancellationTokenSource();
 24
 25        internal GameObject animationsModelsContainer;
 26
 27        // Alex: While we are supporting the old Emotes flow, we need the wearableItemResolver
 328        public EmoteAnimationsTracker(DataStore_Emotes dataStore, EmoteAnimationLoaderFactory emoteAnimationLoaderFactor
 29        {
 330            animationsModelsContainer = new GameObject("_EmoteAnimationsHolder");
 331            animationsModelsContainer.transform.position = EnvironmentSettings.MORDOR;
 332            this.dataStore = dataStore;
 333            this.emoteAnimationLoaderFactory = emoteAnimationLoaderFactory;
 334            this.wearableItemResolver = wearableItemResolver;
 335            this.emotesCatalogService = emotesCatalogService;
 336            this.dataStore.animations.Clear();
 37
 338            InitializeEmbeddedEmotes();
 339            InitializeEmotes(this.dataStore.emotesOnUse.GetAllRefCounts());
 40
 341            this.dataStore.emotesOnUse.OnRefCountUpdated += OnRefCountUpdated;
 342        }
 43
 44        private void InitializeEmbeddedEmotes()
 45        {
 46            //To avoid circular references in assemblies we hardcode this here instead of using WearableLiterals
 47            //Embedded Emotes are only temporary until they can be retrieved from the content server
 48            const string FEMALE = "urn:decentraland:off-chain:base-avatars:BaseFemale";
 49            const string MALE = "urn:decentraland:off-chain:base-avatars:BaseMale";
 50
 351            EmbeddedEmotesSO embeddedEmotes = Resources.Load<EmbeddedEmotesSO>("EmbeddedEmotes");
 52
 12053            foreach (EmbeddedEmote embeddedEmote in embeddedEmotes.emotes)
 54            {
 5755                if (embeddedEmote.maleAnimation != null)
 56                {
 57                    //We match the animation id with its name due to performance reasons
 58                    //Unity's Animation uses the name to play the clips.
 5759                    embeddedEmote.maleAnimation.name = embeddedEmote.id;
 5760                    dataStore.emotesOnUse.SetRefCount((MALE,  embeddedEmote.id), 5000);
 5761                    var clipData = new EmoteClipData(embeddedEmote.maleAnimation, embeddedEmote.emoteDataV0);
 5762                    dataStore.animations.Add((MALE, embeddedEmote.id), clipData);
 5763                    loaders.Add((MALE, embeddedEmote.id), emoteAnimationLoaderFactory.Get());
 64                }
 65
 5766                if (embeddedEmote.femaleAnimation != null)
 67                {
 68                    //We match the animation id with its name due to performance reasons
 69                    //Unity's Animation uses the name to play the clips.
 5770                    embeddedEmote.femaleAnimation.name = embeddedEmote.id;
 5771                    dataStore.emotesOnUse.SetRefCount((FEMALE,  embeddedEmote.id), 5000);
 5772                    var emoteClipData = new EmoteClipData(embeddedEmote.femaleAnimation, embeddedEmote.emoteDataV0);
 5773                    dataStore.animations.Add((FEMALE, embeddedEmote.id), emoteClipData);
 5774                    loaders.Add((FEMALE, embeddedEmote.id), emoteAnimationLoaderFactory.Get());
 75                }
 76            }
 377            CatalogController.i.EmbedWearables(embeddedEmotes.emotes);
 378        }
 79
 80        private void OnRefCountUpdated((string bodyshapeId, string emoteId) value, int refCount)
 81        {
 282            if (refCount > 0)
 183                LoadEmote(value.bodyshapeId, value.emoteId, cts.Token);
 84            else
 185                UnloadEmote(value.bodyshapeId, value.emoteId);
 186        }
 87
 88        private void InitializeEmotes(IEnumerable<KeyValuePair<(string bodyshapeId, string emoteId), int>> refCounts)
 89        {
 23490            foreach (KeyValuePair<(string bodyshapeId, string emoteId), int> keyValuePair in refCounts)
 91            {
 11492                LoadEmote(keyValuePair.Key.bodyshapeId, keyValuePair.Key.emoteId, cts.Token);
 93            }
 394        }
 95
 96        private async UniTaskVoid LoadEmote(string bodyShapeId, string emoteId, CancellationToken ct)
 97        {
 11598            ct.ThrowIfCancellationRequested();
 99
 115100            if (loaders.ContainsKey((bodyShapeId, emoteId)))
 115101                return;
 102
 103            try
 104            {
 0105                var emote = await(emotesCatalogService.RequestEmoteAsync(emoteId, ct));
 106
 0107                IEmoteAnimationLoader animationLoader = emoteAnimationLoaderFactory.Get();
 0108                loaders.Add((bodyShapeId, emoteId), animationLoader);
 0109                await animationLoader.LoadEmote(animationsModelsContainer, emote, bodyShapeId, ct);
 110                EmoteClipData emoteClipData;
 0111                if(emote is EmoteItem newEmoteItem)
 0112                    emoteClipData = new EmoteClipData(animationLoader.animation, newEmoteItem.data.loop);
 113                else
 0114                    emoteClipData = new EmoteClipData(animationLoader.animation, emote.emoteDataV0);
 115
 0116                dataStore.animations.Add((bodyShapeId, emoteId), emoteClipData);
 0117            }
 118            catch (Exception e)
 119            {
 0120                loaders.Remove((bodyShapeId, emoteId));
 0121                ExceptionDispatchInfo.Capture(e).Throw();
 0122                throw;
 123            }
 115124        }
 125
 126        private void UnloadEmote(string bodyShapeId, string emoteId)
 127        {
 1128            if (!loaders.TryGetValue((bodyShapeId, emoteId), out IEmoteAnimationLoader loader))
 0129                return;
 130
 1131            dataStore.animations.Remove((bodyShapeId, emoteId));
 1132            loaders.Remove((bodyShapeId, emoteId));
 1133            loader?.Dispose();
 1134        }
 135
 136        public void Dispose()
 137        {
 0138            dataStore.emotesOnUse.OnRefCountUpdated -= OnRefCountUpdated;
 0139            (string bodyshapeId, string emoteId)[] keys = loaders.Keys.ToArray();
 0140            foreach ((string bodyshapeId, string emoteId) in keys)
 141            {
 0142                UnloadEmote(bodyshapeId, emoteId);
 143            }
 0144            loaders.Clear();
 0145            cts.Cancel();
 0146            Object.Destroy(animationsModelsContainer);
 0147        }
 148    }
 149}