< Summary

Class:DCL.AvatarShape
Assembly:AvatarShape
File(s):/tmp/workspace/unity-renderer/unity-renderer/Assets/Scripts/MainScripts/DCL/Components/Avatar/AvatarShape.cs
Covered lines:72
Uncovered lines:121
Coverable lines:193
Total lines:436
Line coverage:37.3% (72 of 193)
Covered branches:0
Total branches:0

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity NPath complexity Sequence coverage
AvatarShape()0%110100%
Awake()0%330100%
GetStandardAvatar()0%110100%
GetAvatarWithHologram()0%110100%
Start()0%330100%
PlayerClicked()0%6200%
OnDestroy()0%330100%
ApplyChanges()0%116.1315023.4%
SetImpostor(...)0%20400%
PlayerPointerExit()0%6200%
PlayerPointerEnter()0%6200%
UpdatePlayerStatus(...)0%1821300%
Update()0%2.862040%
OnEntityTransformChanged(...)0%2100%
OnEntityTransformChanged(...)0%6200%
OnPoolGet()0%110100%
ApplyHideAvatarModifier()0%6200%
RemoveHideAvatarModifier()0%6200%
ApplyHidePassportModifier()0%3.043083.33%
RemoveHidePassportModifier()0%12300%
Cleanup()0%8.068090%
GetClassId()0%110100%
PrintCurrentProfile()0%2100%

File(s)

/tmp/workspace/unity-renderer/unity-renderer/Assets/Scripts/MainScripts/DCL/Components/Avatar/AvatarShape.cs

#LineLine coverage
 1using System;
 2using DCL.Components;
 3using DCL.Interface;
 4using System.Collections;
 5using System.Collections.Generic;
 6using System.Linq;
 7using System.Threading;
 8using AvatarSystem;
 9using DCL.Configuration;
 10using DCL.Emotes;
 11using DCL.Helpers;
 12using DCL.Models;
 13using GPUSkinning;
 14using UnityEngine;
 15using Avatar = AvatarSystem.Avatar;
 16using LOD = AvatarSystem.LOD;
 17
 18namespace DCL
 19{
 20    public class AvatarShape : BaseComponent, IHideAvatarAreaHandler, IHidePassportAreaHandler
 21    {
 22        private const string CURRENT_PLAYER_ID = "CurrentPlayerInfoCardId";
 23        private const float MINIMUM_PLAYERNAME_HEIGHT = 2.7f;
 24        private const float AVATAR_PASSPORT_TOGGLE_ALPHA_THRESHOLD = 0.9f;
 25        private const string IN_HIDE_AREA = "IN_HIDE_AREA";
 26
 27        public static event Action<IDCLEntity, AvatarShape> OnAvatarShapeUpdated;
 28
 29        public GameObject avatarContainer;
 30        public Collider avatarCollider;
 31        public AvatarMovementController avatarMovementController;
 32        public StickersController stickersControllers;
 33        [SerializeField] private Transform avatarRevealContainer;
 34        [SerializeField] private GameObject armatureContainer;
 35
 36        [SerializeField] internal AvatarOnPointerDown onPointerDown;
 37        [SerializeField] internal GameObject playerNameContainer;
 38        internal IPlayerName playerName;
 39        internal IAvatarReporterController avatarReporterController;
 40
 41        private StringVariable currentPlayerInfoCardId;
 42
 43        public bool everythingIsLoaded;
 44
 45        bool initializedPosition = false;
 46
 47        private Player player = null;
 048        private BaseDictionary<string, Player> otherPlayers => DataStore.i.player.otherPlayers;
 49
 40250        private IAvatarAnchorPoints anchorPoints = new AvatarAnchorPoints();
 51        internal IAvatar avatar;
 40252        private readonly AvatarModel currentAvatar = new AvatarModel { wearables = new List<string>() };
 53        private CancellationTokenSource loadingCts;
 54        private ILazyTextureObserver currentLazyObserver;
 40255        private bool isGlobalSceneAvatar = true;
 56        private BaseRefCounter<AvatarModifierAreaID> currentActiveModifiers;
 57
 458        public override string componentName => "avatarShape";
 59
 60        private void Awake()
 61        {
 40062            model = new AvatarModel();
 40063            currentPlayerInfoCardId = Resources.Load<StringVariable>(CURRENT_PLAYER_ID);
 64
 40065            if (DataStore.i.avatarConfig.useHologramAvatar.Get())
 266                avatar = GetAvatarWithHologram();
 67            else
 39868                avatar = GetStandardAvatar();
 69
 40070            if (avatarReporterController == null)
 71            {
 40072                avatarReporterController = new AvatarReporterController(Environment.i.world.state);
 73            }
 40074        }
 75
 76        private Avatar GetStandardAvatar()
 77        {
 39878            Visibility visibility = new Visibility();
 39879            LOD avatarLOD = new LOD(avatarContainer, visibility, avatarMovementController);
 39880            AvatarAnimatorLegacy animator = GetComponentInChildren<AvatarAnimatorLegacy>();
 39881            return new Avatar(
 82                new AvatarCurator(new WearableItemResolver()),
 83                new Loader(new WearableLoaderFactory(), avatarContainer, new AvatarMeshCombinerHelper()),
 84                animator,
 85                visibility,
 86                avatarLOD,
 87                new SimpleGPUSkinning(),
 88                new GPUSkinningThrottler(),
 89                new EmoteAnimationEquipper(animator, DataStore.i.emotes));
 90        }
 91
 92        private AvatarWithHologram GetAvatarWithHologram()
 93        {
 294            Visibility visibility = new Visibility();
 295            LOD avatarLOD = new LOD(avatarContainer, visibility, avatarMovementController);
 296            AvatarAnimatorLegacy animator = GetComponentInChildren<AvatarAnimatorLegacy>();
 297            BaseAvatar baseAvatar = new BaseAvatar(avatarRevealContainer, armatureContainer, avatarLOD);
 298            return new AvatarWithHologram(
 99                    baseAvatar,
 100                    new AvatarCurator(new WearableItemResolver()),
 101                    new Loader(new WearableLoaderFactory(), avatarContainer, new AvatarMeshCombinerHelper()),
 102                    animator,
 103                    visibility,
 104                    avatarLOD,
 105                    new SimpleGPUSkinning(),
 106                    new GPUSkinningThrottler(),
 107                    new EmoteAnimationEquipper(animator, DataStore.i.emotes));
 108        }
 109
 110        private void Start()
 111        {
 1112            playerName = GetComponentInChildren<IPlayerName>();
 1113            playerName?.Hide(true);
 1114            currentActiveModifiers ??= new BaseRefCounter<AvatarModifierAreaID>();
 1115        }
 116
 117        private void PlayerClicked()
 118        {
 0119            if (model == null)
 0120                return;
 0121            currentPlayerInfoCardId.Set(((AvatarModel) model).id);
 0122        }
 123
 124        public void OnDestroy()
 125        {
 400126            Cleanup();
 127
 400128            if (poolableObject != null && poolableObject.isInsidePool)
 5129                poolableObject.RemoveFromPool();
 400130        }
 131
 132        public override IEnumerator ApplyChanges(BaseModel newModel)
 133        {
 5134            isGlobalSceneAvatar = scene.sceneData.id == EnvironmentSettings.AVATAR_GLOBAL_SCENE_ID;
 5135            currentActiveModifiers ??= new BaseRefCounter<AvatarModifierAreaID>();
 136
 5137            ApplyHidePassportModifier();
 138
 5139            var model = (AvatarModel) newModel;
 140
 5141            bool needsLoading = !model.HaveSameWearablesAndColors(currentAvatar);
 5142            currentAvatar.CopyFrom(model);
 143
 5144            if (string.IsNullOrEmpty(model.bodyShape) || model.wearables.Count == 0)
 1145                yield break;
 146#if UNITY_EDITOR
 4147            gameObject.name = $"Avatar Shape {model.name}";
 148#endif
 4149            everythingIsLoaded = false;
 150
 151            bool avatarDone = false;
 152            bool avatarFailed = false;
 153
 4154            yield return null; //NOTE(Brian): just in case we have a Object.Destroy waiting to be resolved.
 155
 156            // To deal with the cases in which the entity transform was configured before the AvatarShape
 0157            if (!initializedPosition && scene.componentsManagerLegacy.HasComponent(entity, CLASS_ID_COMPONENT.TRANSFORM)
 158            {
 0159                initializedPosition = true;
 0160                OnEntityTransformChanged(entity.gameObject.transform.localPosition,
 161                    entity.gameObject.transform.localRotation, true);
 162            }
 163
 164            // NOTE: we subscribe here to transform changes since we might "lose" the message
 165            // if we subscribe after a any yield
 0166            entity.OnTransformChange -= OnEntityTransformChanged;
 0167            entity.OnTransformChange += OnEntityTransformChanged;
 168
 0169            var wearableItems = model.wearables.ToList();
 0170            wearableItems.Add(model.bodyShape);
 171
 172            //temporarily hardcoding the embedded emotes until the user profile provides the equipped ones
 0173            var embeddedEmotesSo = Resources.Load<EmbeddedEmotesSO>("EmbeddedEmotes");
 0174            wearableItems.AddRange(embeddedEmotesSo.emotes.Select(x => x.id));
 175
 0176            if (avatar.status != IAvatar.Status.Loaded || needsLoading)
 177            {
 178                //TODO Add Collider to the AvatarSystem
 179                //TODO Without this the collider could get triggered disabling the avatar container,
 180                // this would stop the loading process due to the underlying coroutines of the AssetLoader not starting
 0181                avatarCollider.gameObject.SetActive(false);
 182
 0183                SetImpostor(model.id);
 0184                loadingCts?.Cancel();
 0185                loadingCts?.Dispose();
 0186                loadingCts = new CancellationTokenSource();
 0187                if (DataStore.i.avatarConfig.useHologramAvatar.Get())
 188                {
 0189                    playerName.SetName(model.name);
 0190                    playerName.Show(true);
 191                }
 0192                avatar.Load(wearableItems, new AvatarSettings
 193                {
 194                    playerName = model.name,
 195                    bodyshapeId = model.bodyShape,
 196                    eyesColor = model.eyeColor,
 197                    skinColor = model.skinColor,
 198                    hairColor = model.hairColor,
 199                }, loadingCts.Token);
 200
 201                // Yielding a UniTask doesn't do anything, we manually wait until the avatar is ready
 0202                yield return new WaitUntil(() => avatar.status == IAvatar.Status.Loaded);
 203            }
 204
 0205            avatar.PlayEmote(model.expressionTriggerId, model.expressionTriggerTimestamp);
 206
 0207            onPointerDown.OnPointerDownReport -= PlayerClicked;
 0208            onPointerDown.OnPointerDownReport += PlayerClicked;
 0209            onPointerDown.OnPointerEnterReport -= PlayerPointerEnter;
 0210            onPointerDown.OnPointerEnterReport += PlayerPointerEnter;
 0211            onPointerDown.OnPointerExitReport -= PlayerPointerExit;
 0212            onPointerDown.OnPointerExitReport += PlayerPointerExit;
 213
 0214            UpdatePlayerStatus(model);
 215
 0216            onPointerDown.Initialize(
 217                new OnPointerDown.Model()
 218                {
 219                    type = OnPointerDown.NAME,
 220                    button = WebInterface.ACTION_BUTTON.POINTER.ToString(),
 221                    hoverText = "view profile"
 222                },
 223                entity, player
 224            );
 225
 0226            avatarCollider.gameObject.SetActive(true);
 227
 0228            everythingIsLoaded = true;
 0229            OnAvatarShapeUpdated?.Invoke(entity, this);
 230
 0231            RemoveHidePassportModifier();
 232
 0233            onPointerDown.SetColliderEnabled(isGlobalSceneAvatar);
 0234            onPointerDown.SetOnClickReportEnabled(isGlobalSceneAvatar);
 0235        }
 236
 237        public void SetImpostor(string userId)
 238        {
 0239            currentLazyObserver?.RemoveListener(avatar.SetImpostorTexture);
 0240            if (string.IsNullOrEmpty(userId))
 0241                return;
 242
 0243            UserProfile userProfile = UserProfileController.GetProfileByUserId(userId);
 0244            if (userProfile == null)
 0245                return;
 246
 0247            currentLazyObserver = userProfile.bodySnapshotObserver;
 0248            currentLazyObserver.AddListener(avatar.SetImpostorTexture);
 0249        }
 250
 0251        private void PlayerPointerExit() { playerName?.SetForceShow(false); }
 0252        private void PlayerPointerEnter() { playerName?.SetForceShow(true); }
 253
 254        private void UpdatePlayerStatus(AvatarModel model)
 255        {
 256            // Remove the player status if the userId changes
 0257            if (player != null && (player.id != model.id || player.name != model.name))
 0258                otherPlayers.Remove(player.id);
 259
 0260            if (isGlobalSceneAvatar && string.IsNullOrEmpty(model?.id))
 0261                return;
 262
 0263            bool isNew = player == null;
 0264            if (isNew)
 265            {
 0266                player = new Player();
 267            }
 268
 0269            bool isNameDirty = player.name != model.name;
 270
 0271            player.id = model.id;
 0272            player.name = model.name;
 0273            player.isTalking = model.talking;
 0274            player.worldPosition = entity.gameObject.transform.position;
 0275            player.avatar = avatar;
 0276            player.onPointerDownCollider = onPointerDown;
 0277            player.collider = avatarCollider;
 278
 0279            if (isNew)
 280            {
 0281                player.playerName = playerName;
 0282                player.playerName.Show();
 0283                player.anchorPoints = anchorPoints;
 0284                if (isGlobalSceneAvatar)
 285                {
 286                    // TODO: Note: This is having a problem, sometimes the users has been detected as new 2 times and it
 287                    // we should investigate this
 0288                    if (otherPlayers.ContainsKey(player.id))
 0289                        otherPlayers.Remove(player.id);
 0290                    otherPlayers.Add(player.id, player);
 291                }
 0292                avatarReporterController.ReportAvatarRemoved();
 293            }
 294
 0295            avatarReporterController.SetUp(entity.scene.sceneData.id, player.id);
 296
 0297            float height = AvatarSystemUtils.AVATAR_Y_OFFSET + avatar.extents.y;
 298
 0299            anchorPoints.Prepare(avatarContainer.transform, avatar.GetBones(), height);
 300
 0301            player.playerName.SetIsTalking(model.talking);
 0302            player.playerName.SetYOffset(Mathf.Max(MINIMUM_PLAYERNAME_HEIGHT, height));
 0303            if (isNameDirty)
 0304                player.playerName.SetName(model.name);
 0305        }
 306
 307        private void Update()
 308        {
 1309            if (player != null)
 310            {
 0311                player.worldPosition = entity.gameObject.transform.position;
 0312                player.forwardDirection = entity.gameObject.transform.forward;
 0313                avatarReporterController.ReportAvatarPosition(player.worldPosition);
 314            }
 1315        }
 316
 317        private void OnEntityTransformChanged(object newModel)
 318        {
 0319            DCLTransform.Model newTransformModel = (DCLTransform.Model)newModel;
 0320            OnEntityTransformChanged(newTransformModel.position, newTransformModel.rotation, !initializedPosition);
 0321        }
 322
 323        private void OnEntityTransformChanged(in Vector3 position, in Quaternion rotation, bool inmediate)
 324        {
 0325            if (isGlobalSceneAvatar)
 326            {
 0327                avatarMovementController.OnTransformChanged(position, rotation, inmediate);
 0328            }
 329            else
 330            {
 0331                var scenePosition = Utils.GridToWorldPosition(entity.scene.sceneData.basePosition.x, entity.scene.sceneD
 0332                avatarMovementController.OnTransformChanged(scenePosition + position, rotation, inmediate);
 333            }
 0334            initializedPosition = true;
 0335        }
 336
 337        public override void OnPoolGet()
 338        {
 5339            base.OnPoolGet();
 340
 5341            everythingIsLoaded = false;
 5342            initializedPosition = false;
 5343            model = new AvatarModel();
 5344            player = null;
 5345        }
 346
 347        public void ApplyHideAvatarModifier()
 348        {
 0349            if (!currentActiveModifiers.ContainsKey(AvatarModifierAreaID.HIDE_AVATAR))
 350            {
 0351                avatar.AddVisibilityConstrain(IN_HIDE_AREA);
 0352                onPointerDown.gameObject.SetActive(false);
 0353                playerNameContainer.SetActive(false);
 0354                stickersControllers.ToggleHideArea(true);
 355            }
 0356            currentActiveModifiers.AddRefCount(AvatarModifierAreaID.HIDE_AVATAR);
 0357        }
 358
 359        public void RemoveHideAvatarModifier()
 360        {
 0361            currentActiveModifiers.RemoveRefCount(AvatarModifierAreaID.HIDE_AVATAR);
 0362            if (!currentActiveModifiers.ContainsKey(AvatarModifierAreaID.HIDE_AVATAR))
 363            {
 0364                avatar.RemoveVisibilityConstrain(IN_HIDE_AREA);
 0365                onPointerDown.gameObject.SetActive(true);
 0366                playerNameContainer.SetActive(true);
 0367                stickersControllers.ToggleHideArea(false);
 368            }
 0369        }
 370
 371        public void ApplyHidePassportModifier()
 372        {
 5373            if (!currentActiveModifiers.ContainsKey(AvatarModifierAreaID.DISABLE_PASSPORT))
 374            {
 5375                if (onPointerDown.collider == null)
 0376                    return;
 377
 5378                onPointerDown.SetPassportEnabled(false);
 379            }
 5380            currentActiveModifiers.AddRefCount(AvatarModifierAreaID.DISABLE_PASSPORT);
 5381        }
 382
 383        public void RemoveHidePassportModifier()
 384        {
 0385            currentActiveModifiers.RemoveRefCount(AvatarModifierAreaID.DISABLE_PASSPORT);
 0386            if (!currentActiveModifiers.ContainsKey(AvatarModifierAreaID.DISABLE_PASSPORT))
 387            {
 0388                if (onPointerDown.collider == null)
 0389                    return;
 390
 0391                onPointerDown.SetPassportEnabled(true);
 392            }
 0393        }
 394
 395        public override void Cleanup()
 396        {
 410397            base.Cleanup();
 398
 410399            playerName?.Hide(true);
 410400            if (player != null)
 401            {
 0402                otherPlayers.Remove(player.id);
 0403                player = null;
 404            }
 405
 410406            loadingCts?.Cancel();
 410407            loadingCts?.Dispose();
 410408            loadingCts = null;
 410409            currentLazyObserver?.RemoveListener(avatar.SetImpostorTexture);
 410410            avatar.Dispose();
 411
 410412            if (poolableObject != null)
 413            {
 15414                poolableObject.OnRelease -= Cleanup;
 415            }
 416
 410417            onPointerDown.OnPointerDownReport -= PlayerClicked;
 410418            onPointerDown.OnPointerEnterReport -= PlayerPointerEnter;
 410419            onPointerDown.OnPointerExitReport -= PlayerPointerExit;
 420
 410421            if (entity != null)
 422            {
 5423                entity.OnTransformChange = null;
 5424                entity = null;
 425            }
 426
 410427            avatarReporterController.ReportAvatarRemoved();
 410428        }
 429
 1430        public override int GetClassId() { return (int) CLASS_ID_COMPONENT.AVATAR_SHAPE; }
 431
 432        [ContextMenu("Print current profile")]
 0433        private void PrintCurrentProfile() { Debug.Log(JsonUtility.ToJson(model)); }
 434
 435    }
 436}