< Summary

Class:DCL.Emotes.EmoteAnimationsTracker
Assembly:EmoteAnimationsPlugin
File(s):/tmp/workspace/unity-renderer/unity-renderer/Assets/DCLPlugins/EmoteAnimations/EmoteAnimationsTracker.cs
Covered lines:53
Uncovered lines:12
Coverable lines:65
Total lines:147
Line coverage:81.5% (53 of 65)
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%19.1110055%
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                    var clipData = new EmoteClipData(embeddedEmote.maleAnimation);
 7662                    dataStore.animations.Add((MALE, embeddedEmote.id), clipData);
 7663                    loaders.Add((MALE, embeddedEmote.id), emoteAnimationLoaderFactory.Get());
 64                }
 65
 7666                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.
 7670                    embeddedEmote.femaleAnimation.name = embeddedEmote.id;
 7671                    dataStore.emotesOnUse.SetRefCount((FEMALE,  embeddedEmote.id), 5000);
 7672                    var emoteClipData = new EmoteClipData(embeddedEmote.femaleAnimation);
 7673                    dataStore.animations.Add((FEMALE, embeddedEmote.id), emoteClipData);
 7674                    loaders.Add((FEMALE, embeddedEmote.id), emoteAnimationLoaderFactory.Get());
 75                }
 76            }
 477            CatalogController.i.EmbedWearables(embeddedEmotes.emotes);
 478        }
 79
 80        private void OnRefCountUpdated((string bodyshapeId, string emoteId) value, int refCount)
 81        {
 382            if (refCount > 0)
 283                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        {
 31290            foreach (KeyValuePair<(string bodyshapeId, string emoteId), int> keyValuePair in refCounts)
 91            {
 15292                LoadEmote(keyValuePair.Key.bodyshapeId, keyValuePair.Key.emoteId, cts.Token);
 93            }
 494        }
 95
 96        private async UniTaskVoid LoadEmote(string bodyShapeId, string emoteId, CancellationToken ct)
 97        {
 15498            ct.ThrowIfCancellationRequested();
 99
 154100            if (loaders.ContainsKey((bodyShapeId, emoteId)))
 153101                return;
 102
 103            try
 104            {
 1105                var emote = await emotesCatalogService.RequestEmoteAsync(emoteId, ct) ??
 106                                       await wearableItemResolver.Resolve(emoteId, ct);
 107
 1108                IEmoteAnimationLoader animationLoader = emoteAnimationLoaderFactory.Get();
 1109                loaders.Add((bodyShapeId, emoteId), animationLoader);
 1110                await animationLoader.LoadEmote(animationsModelsContainer, emote, bodyShapeId, ct);
 111
 1112                var emoteClipData = new EmoteClipData(animationLoader.animation, emote.emoteDataV0);
 113
 1114                dataStore.animations.Add((bodyShapeId, emoteId), emoteClipData);
 1115            }
 116            catch (Exception e)
 117            {
 0118                loaders.Remove((bodyShapeId, emoteId));
 0119                ExceptionDispatchInfo.Capture(e).Throw();
 0120                throw;
 121            }
 154122        }
 123
 124        private void UnloadEmote(string bodyShapeId, string emoteId)
 125        {
 1126            if (!loaders.TryGetValue((bodyShapeId, emoteId), out IEmoteAnimationLoader loader))
 0127                return;
 128
 1129            dataStore.animations.Remove((bodyShapeId, emoteId));
 1130            loaders.Remove((bodyShapeId, emoteId));
 1131            loader?.Dispose();
 1132        }
 133
 134        public void Dispose()
 135        {
 0136            dataStore.emotesOnUse.OnRefCountUpdated -= OnRefCountUpdated;
 0137            (string bodyshapeId, string emoteId)[] keys = loaders.Keys.ToArray();
 0138            foreach ((string bodyshapeId, string emoteId) in keys)
 139            {
 0140                UnloadEmote(bodyshapeId, emoteId);
 141            }
 0142            loaders.Clear();
 0143            cts.Cancel();
 0144            Object.Destroy(animationsModelsContainer);
 0145        }
 146    }
 147}