< Summary

Class:DCL.Emotes.EmoteAnimationsTracker
Assembly:EmoteAnimationsPlugin
File(s):/tmp/workspace/unity-renderer/unity-renderer/Assets/DCLPlugins/EmoteAnimations/EmoteAnimationsTracker.cs
Covered lines:51
Uncovered lines:13
Coverable lines:64
Total lines:146
Line coverage:79.6% (51 of 64)
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%20.810052.38%
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
 421        internal Dictionary<(string bodyshapeId, string emoteId), IEmoteAnimationLoader> loaders = new Dictionary<(strin
 22
 423        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
 428        public EmoteAnimationsTracker(DataStore_Emotes dataStore, EmoteAnimationLoaderFactory emoteAnimationLoaderFactor
 29        {
 430            animationsModelsContainer = new GameObject("_EmoteAnimationsHolder");
 431            animationsModelsContainer.transform.position = EnvironmentSettings.MORDOR;
 432            this.dataStore = dataStore;
 433            this.emoteAnimationLoaderFactory = emoteAnimationLoaderFactory;
 434            this.wearableItemResolver = wearableItemResolver;
 435            this.emotesCatalogService = emotesCatalogService;
 436            this.dataStore.animations.Clear();
 37
 438            InitializeEmbeddedEmotes();
 439            InitializeEmotes(this.dataStore.emotesOnUse.GetAllRefCounts());
 40
 441            this.dataStore.emotesOnUse.OnRefCountUpdated += OnRefCountUpdated;
 442        }
 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
 451            EmbeddedEmotesSO embeddedEmotes = Resources.Load<EmbeddedEmotesSO>("EmbeddedEmotes");
 52
 16053            foreach (EmbeddedEmote embeddedEmote in embeddedEmotes.emotes)
 54            {
 7655                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.
 7659                    embeddedEmote.maleAnimation.name = embeddedEmote.id;
 7660                    dataStore.emotesOnUse.SetRefCount((MALE,  embeddedEmote.id), 5000);
 7661                    dataStore.animations.Add((MALE, embeddedEmote.id), embeddedEmote.maleAnimation);
 7662                    loaders.Add((MALE, embeddedEmote.id), emoteAnimationLoaderFactory.Get());
 63                }
 64
 7665                if (embeddedEmote.femaleAnimation != null)
 66                {
 67                    //We match the animation id with its name due to performance reasons
 68                    //Unity's Animation uses the name to play the clips.
 7669                    embeddedEmote.femaleAnimation.name = embeddedEmote.id;
 7670                    dataStore.emotesOnUse.SetRefCount((FEMALE,  embeddedEmote.id), 5000);
 7671                    dataStore.animations.Add((FEMALE, embeddedEmote.id), embeddedEmote.femaleAnimation);
 7672                    loaders.Add((FEMALE, embeddedEmote.id), emoteAnimationLoaderFactory.Get());
 73                }
 74            }
 475            CatalogController.i.EmbedWearables(embeddedEmotes.emotes);
 476        }
 77
 78        private void OnRefCountUpdated((string bodyshapeId, string emoteId) value, int refCount)
 79        {
 380            if (refCount > 0)
 281                LoadEmote(value.bodyshapeId, value.emoteId, cts.Token);
 82            else
 183                UnloadEmote(value.bodyshapeId, value.emoteId);
 184        }
 85
 86        private void InitializeEmotes(IEnumerable<KeyValuePair<(string bodyshapeId, string emoteId), int>> refCounts)
 87        {
 31288            foreach (KeyValuePair<(string bodyshapeId, string emoteId), int> keyValuePair in refCounts)
 89            {
 15290                LoadEmote(keyValuePair.Key.bodyshapeId, keyValuePair.Key.emoteId, cts.Token);
 91            }
 492        }
 93
 94        private async UniTaskVoid LoadEmote(string bodyShapeId, string emoteId, CancellationToken ct)
 95        {
 15496            ct.ThrowIfCancellationRequested();
 97
 15498            if (loaders.ContainsKey((bodyShapeId, emoteId)))
 15399                return;
 100
 101            try
 102            {
 103                WearableItem emote;
 1104                if (dataStore.newFlowEnabled.Get())
 0105                    emote = await emotesCatalogService.RequestEmoteAsync(emoteId, ct);
 106                else
 1107                    emote = await wearableItemResolver.Resolve(emoteId, ct);
 108
 1109                IEmoteAnimationLoader animationLoader = emoteAnimationLoaderFactory.Get();
 1110                loaders.Add((bodyShapeId, emoteId), animationLoader);
 1111                await animationLoader.LoadEmote(animationsModelsContainer, emote, bodyShapeId, ct);
 112
 1113                dataStore.animations.Add((bodyShapeId, emoteId), animationLoader.animation);
 1114            }
 115            catch (Exception e)
 116            {
 0117                loaders.Remove((bodyShapeId, emoteId));
 0118                ExceptionDispatchInfo.Capture(e).Throw();
 0119                throw;
 120            }
 154121        }
 122
 123        private void UnloadEmote(string bodyShapeId, string emoteId)
 124        {
 1125            if (!loaders.TryGetValue((bodyShapeId, emoteId), out IEmoteAnimationLoader loader))
 0126                return;
 127
 1128            dataStore.animations.Remove((bodyShapeId, emoteId));
 1129            loaders.Remove((bodyShapeId, emoteId));
 1130            loader?.Dispose();
 1131        }
 132
 133        public void Dispose()
 134        {
 0135            dataStore.emotesOnUse.OnRefCountUpdated -= OnRefCountUpdated;
 0136            (string bodyshapeId, string emoteId)[] keys = loaders.Keys.ToArray();
 0137            foreach ((string bodyshapeId, string emoteId) in keys)
 138            {
 0139                UnloadEmote(bodyshapeId, emoteId);
 140            }
 0141            loaders.Clear();
 0142            cts.Cancel();
 0143            Object.Destroy(animationsModelsContainer);
 0144        }
 145    }
 146}