< 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:24
Coverable lines:70
Total lines:156
Line coverage:65.7% (46 of 70)
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%54.669017.39%
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
 19253            foreach (EmbeddedEmote embeddedEmote in embeddedEmotes.emotes)
 54            {
 9355                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.
 9359                    embeddedEmote.maleAnimation.name = embeddedEmote.id;
 9360                    dataStore.emotesOnUse.SetRefCount((MALE,  embeddedEmote.id), 5000);
 9361                    var clipData = new EmoteClipData(embeddedEmote.maleAnimation, embeddedEmote.emoteDataV0);
 9362                    dataStore.animations.Add((MALE, embeddedEmote.id), clipData);
 9363                    loaders.Add((MALE, embeddedEmote.id), emoteAnimationLoaderFactory.Get());
 64                }
 65
 9366                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.
 9370                    embeddedEmote.femaleAnimation.name = embeddedEmote.id;
 9371                    dataStore.emotesOnUse.SetRefCount((FEMALE,  embeddedEmote.id), 5000);
 9372                    var emoteClipData = new EmoteClipData(embeddedEmote.femaleAnimation, embeddedEmote.emoteDataV0);
 9373                    dataStore.animations.Add((FEMALE, embeddedEmote.id), emoteClipData);
 9374                    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        {
 37890            foreach (KeyValuePair<(string bodyshapeId, string emoteId), int> keyValuePair in refCounts)
 91            {
 18692                LoadEmote(keyValuePair.Key.bodyshapeId, keyValuePair.Key.emoteId, cts.Token);
 93            }
 394        }
 95
 96        private async UniTaskVoid LoadEmote(string bodyShapeId, string emoteId, CancellationToken ct)
 97        {
 18798            ct.ThrowIfCancellationRequested();
 99
 187100            if (loaders.ContainsKey((bodyShapeId, emoteId)))
 187101                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
 111                EmoteClipData emoteClipData;
 0112                if(emote is EmoteItem newEmoteItem)
 0113                    emoteClipData = new EmoteClipData(animationLoader.loadedAnimationClip, newEmoteItem.data.loop);
 114                else
 0115                    emoteClipData = new EmoteClipData(animationLoader.loadedAnimationClip, emote.emoteDataV0);
 116
 0117                dataStore.animations.Add((bodyShapeId, emoteId), emoteClipData);
 118
 0119                if (animationLoader.loadedAnimationClip == null)
 120                {
 0121                    Debug.LogError("Emote animation failed to load for emote " + emote.id);
 122                }
 0123            }
 124            catch (Exception e)
 125            {
 0126                loaders.Remove((bodyShapeId, emoteId));
 0127                dataStore.animations.Remove((bodyShapeId, emoteId));
 0128                ExceptionDispatchInfo.Capture(e).Throw();
 0129                throw;
 130            }
 187131        }
 132
 133        private void UnloadEmote(string bodyShapeId, string emoteId)
 134        {
 1135            dataStore.animations.Remove((bodyShapeId, emoteId));
 1136            if (!loaders.TryGetValue((bodyShapeId, emoteId), out IEmoteAnimationLoader loader))
 0137                return;
 138
 1139            loaders.Remove((bodyShapeId, emoteId));
 1140            loader?.Dispose();
 1141        }
 142
 143        public void Dispose()
 144        {
 0145            dataStore.emotesOnUse.OnRefCountUpdated -= OnRefCountUpdated;
 0146            (string bodyshapeId, string emoteId)[] keys = loaders.Keys.ToArray();
 0147            foreach ((string bodyshapeId, string emoteId) in keys)
 148            {
 0149                UnloadEmote(bodyshapeId, emoteId);
 150            }
 0151            loaders.Clear();
 0152            cts.Cancel();
 0153            Object.Destroy(animationsModelsContainer);
 0154        }
 155    }
 156}