< Summary

Class:DCL.AvatarShape
Assembly:AvatarShape
File(s):/tmp/workspace/unity-renderer/unity-renderer/Assets/Scripts/MainScripts/DCL/Components/Avatar/AvatarShape.cs
Covered lines:69
Uncovered lines:131
Coverable lines:200
Total lines:452
Line coverage:34.5% (69 of 200)
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%169.2817019.23%
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%6200%
RemoveHidePassportModifier()0%6200%
EnablePasssport()0%6200%
DisablePassport()0%2.062075%
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
 40350        private IAvatarAnchorPoints anchorPoints = new AvatarAnchorPoints();
 51        internal IAvatar avatar;
 40352        private readonly AvatarModel currentAvatar = new AvatarModel { wearables = new List<string>() };
 53        private CancellationTokenSource loadingCts;
 54        private ILazyTextureObserver currentLazyObserver;
 40355        private bool isGlobalSceneAvatar = true;
 56        private BaseRefCounter<AvatarModifierAreaID> currentActiveModifiers;
 57
 458        public override string componentName => "avatarShape";
 59
 60        private void Awake()
 61        {
 40162            model = new AvatarModel();
 40163            currentPlayerInfoCardId = Resources.Load<StringVariable>(CURRENT_PLAYER_ID);
 64
 40165            if (DataStore.i.avatarConfig.useHologramAvatar.Get())
 266                avatar = GetAvatarWithHologram();
 67            else
 39968                avatar = GetStandardAvatar();
 69
 40170            if (avatarReporterController == null)
 71            {
 40172                avatarReporterController = new AvatarReporterController(Environment.i.world.state);
 73            }
 40174        }
 75
 76        private Avatar GetStandardAvatar()
 77        {
 39978            Visibility visibility = new Visibility();
 39979            LOD avatarLOD = new LOD(avatarContainer, visibility, avatarMovementController);
 39980            AvatarAnimatorLegacy animator = GetComponentInChildren<AvatarAnimatorLegacy>();
 39981            return new Avatar(
 82                new AvatarCurator(new WearableItemResolver(), Environment.i.serviceLocator.Get<IEmotesCatalogService>())
 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(), Environment.i.serviceLocator.Get<IEmotesCatalogService
 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        {
 401126            Cleanup();
 127
 401128            if (poolableObject != null && poolableObject.isInsidePool)
 5129                poolableObject.RemoveFromPool();
 401130        }
 131
 132        public override IEnumerator ApplyChanges(BaseModel newModel)
 133        {
 5134            isGlobalSceneAvatar = scene.sceneData.id == EnvironmentSettings.AVATAR_GLOBAL_SCENE_ID;
 135
 5136            DisablePassport();
 137
 5138            var model = (AvatarModel) newModel;
 139
 5140            bool needsLoading = !model.HaveSameWearablesAndColors(currentAvatar);
 5141            currentAvatar.CopyFrom(model);
 142
 5143            if (string.IsNullOrEmpty(model.bodyShape) || model.wearables.Count == 0)
 1144                yield break;
 145#if UNITY_EDITOR
 4146            gameObject.name = $"Avatar Shape {model.name}";
 147#endif
 4148            everythingIsLoaded = false;
 149
 150            bool avatarDone = false;
 151            bool avatarFailed = false;
 152
 4153            yield return null; //NOTE(Brian): just in case we have a Object.Destroy waiting to be resolved.
 154
 155            // To deal with the cases in which the entity transform was configured before the AvatarShape
 0156            if (!initializedPosition && scene.componentsManagerLegacy.HasComponent(entity, CLASS_ID_COMPONENT.TRANSFORM)
 157            {
 0158                initializedPosition = true;
 0159                OnEntityTransformChanged(entity.gameObject.transform.localPosition,
 160                    entity.gameObject.transform.localRotation, true);
 161            }
 162
 163            // NOTE: we subscribe here to transform changes since we might "lose" the message
 164            // if we subscribe after a any yield
 0165            entity.OnTransformChange -= OnEntityTransformChanged;
 0166            entity.OnTransformChange += OnEntityTransformChanged;
 167
 0168            var wearableItems = model.wearables.ToList();
 0169            wearableItems.Add(model.bodyShape);
 170
 0171            if (avatar.status != IAvatar.Status.Loaded || needsLoading)
 172            {
 0173                HashSet<string> emotes = new HashSet<string>(currentAvatar.emotes.Select(x => x.urn));
 0174                var embeddedEmotesSo = Resources.Load<EmbeddedEmotesSO>("EmbeddedEmotes");
 0175                var embeddedEmoteIds = embeddedEmotesSo.emotes.Select(x => x.id);
 176                //here we add emote ids to both new and old emote loading flow to merge the results later
 177                //because some users might have emotes as wearables and others only as emotes
 0178                foreach (var emoteId in embeddedEmoteIds)
 179                {
 0180                    emotes.Add(emoteId);
 0181                    wearableItems.Add(emoteId);
 182                }
 183
 184                //TODO Add Collider to the AvatarSystem
 185                //TODO Without this the collider could get triggered disabling the avatar container,
 186                // this would stop the loading process due to the underlying coroutines of the AssetLoader not starting
 0187                avatarCollider.gameObject.SetActive(false);
 188
 0189                SetImpostor(model.id);
 0190                loadingCts?.Cancel();
 0191                loadingCts?.Dispose();
 0192                loadingCts = new CancellationTokenSource();
 0193                if (DataStore.i.avatarConfig.useHologramAvatar.Get())
 194                {
 0195                    playerName.SetName(model.name);
 0196                    playerName.Show(true);
 197                }
 0198                avatar.Load(wearableItems, emotes.ToList(), new AvatarSettings
 199                {
 200                    playerName = model.name,
 201                    bodyshapeId = model.bodyShape,
 202                    eyesColor = model.eyeColor,
 203                    skinColor = model.skinColor,
 204                    hairColor = model.hairColor,
 205                }, loadingCts.Token);
 206
 207                // Yielding a UniTask doesn't do anything, we manually wait until the avatar is ready
 0208                yield return new WaitUntil(() => avatar.status == IAvatar.Status.Loaded);
 209            }
 210
 0211            avatar.PlayEmote(model.expressionTriggerId, model.expressionTriggerTimestamp);
 212
 0213            onPointerDown.OnPointerDownReport -= PlayerClicked;
 0214            onPointerDown.OnPointerDownReport += PlayerClicked;
 0215            onPointerDown.OnPointerEnterReport -= PlayerPointerEnter;
 0216            onPointerDown.OnPointerEnterReport += PlayerPointerEnter;
 0217            onPointerDown.OnPointerExitReport -= PlayerPointerExit;
 0218            onPointerDown.OnPointerExitReport += PlayerPointerExit;
 219
 0220            UpdatePlayerStatus(model);
 221
 0222            onPointerDown.Initialize(
 223                new OnPointerDown.Model()
 224                {
 225                    type = OnPointerDown.NAME,
 226                    button = WebInterface.ACTION_BUTTON.POINTER.ToString(),
 227                    hoverText = "view profile"
 228                },
 229                entity, player
 230            );
 231
 0232            avatarCollider.gameObject.SetActive(true);
 233
 0234            everythingIsLoaded = true;
 0235            OnAvatarShapeUpdated?.Invoke(entity, this);
 236
 0237            EnablePasssport();
 238
 0239            onPointerDown.SetColliderEnabled(isGlobalSceneAvatar);
 0240            onPointerDown.SetOnClickReportEnabled(isGlobalSceneAvatar);
 0241        }
 242
 243        public void SetImpostor(string userId)
 244        {
 0245            currentLazyObserver?.RemoveListener(avatar.SetImpostorTexture);
 0246            if (string.IsNullOrEmpty(userId))
 0247                return;
 248
 0249            UserProfile userProfile = UserProfileController.GetProfileByUserId(userId);
 0250            if (userProfile == null)
 0251                return;
 252
 0253            currentLazyObserver = userProfile.bodySnapshotObserver;
 0254            currentLazyObserver.AddListener(avatar.SetImpostorTexture);
 0255        }
 256
 0257        private void PlayerPointerExit() { playerName?.SetForceShow(false); }
 0258        private void PlayerPointerEnter() { playerName?.SetForceShow(true); }
 259
 260        private void UpdatePlayerStatus(AvatarModel model)
 261        {
 262            // Remove the player status if the userId changes
 0263            if (player != null && (player.id != model.id || player.name != model.name))
 0264                otherPlayers.Remove(player.id);
 265
 0266            if (isGlobalSceneAvatar && string.IsNullOrEmpty(model?.id))
 0267                return;
 268
 0269            bool isNew = player == null;
 0270            if (isNew)
 271            {
 0272                player = new Player();
 273            }
 274
 0275            bool isNameDirty = player.name != model.name;
 276
 0277            player.id = model.id;
 0278            player.name = model.name;
 0279            player.isTalking = model.talking;
 0280            player.worldPosition = entity.gameObject.transform.position;
 0281            player.avatar = avatar;
 0282            player.onPointerDownCollider = onPointerDown;
 0283            player.collider = avatarCollider;
 284
 0285            if (isNew)
 286            {
 0287                player.playerName = playerName;
 0288                player.playerName.Show();
 0289                player.anchorPoints = anchorPoints;
 0290                if (isGlobalSceneAvatar)
 291                {
 292                    // TODO: Note: This is having a problem, sometimes the users has been detected as new 2 times and it
 293                    // we should investigate this
 0294                    if (otherPlayers.ContainsKey(player.id))
 0295                        otherPlayers.Remove(player.id);
 0296                    otherPlayers.Add(player.id, player);
 297                }
 0298                avatarReporterController.ReportAvatarRemoved();
 299            }
 300
 0301            avatarReporterController.SetUp(entity.scene.sceneData.id, player.id);
 302
 0303            float height = AvatarSystemUtils.AVATAR_Y_OFFSET + avatar.extents.y;
 304
 0305            anchorPoints.Prepare(avatarContainer.transform, avatar.GetBones(), height);
 306
 0307            player.playerName.SetIsTalking(model.talking);
 0308            player.playerName.SetYOffset(Mathf.Max(MINIMUM_PLAYERNAME_HEIGHT, height));
 0309            if (isNameDirty)
 0310                player.playerName.SetName(model.name);
 0311        }
 312
 313        private void Update()
 314        {
 1315            if (player != null)
 316            {
 0317                player.worldPosition = entity.gameObject.transform.position;
 0318                player.forwardDirection = entity.gameObject.transform.forward;
 0319                avatarReporterController.ReportAvatarPosition(player.worldPosition);
 320            }
 1321        }
 322
 323        private void OnEntityTransformChanged(object newModel)
 324        {
 0325            DCLTransform.Model newTransformModel = (DCLTransform.Model)newModel;
 0326            OnEntityTransformChanged(newTransformModel.position, newTransformModel.rotation, !initializedPosition);
 0327        }
 328
 329        private void OnEntityTransformChanged(in Vector3 position, in Quaternion rotation, bool inmediate)
 330        {
 0331            if (isGlobalSceneAvatar)
 332            {
 0333                avatarMovementController.OnTransformChanged(position, rotation, inmediate);
 0334            }
 335            else
 336            {
 0337                var scenePosition = Utils.GridToWorldPosition(entity.scene.sceneData.basePosition.x, entity.scene.sceneD
 0338                avatarMovementController.OnTransformChanged(scenePosition + position, rotation, inmediate);
 339            }
 0340            initializedPosition = true;
 0341        }
 342
 343        public override void OnPoolGet()
 344        {
 5345            base.OnPoolGet();
 346
 5347            everythingIsLoaded = false;
 5348            initializedPosition = false;
 5349            model = new AvatarModel();
 5350            player = null;
 5351        }
 352
 353        public void ApplyHideAvatarModifier()
 354        {
 0355            if (!currentActiveModifiers.ContainsKey(AvatarModifierAreaID.HIDE_AVATAR))
 356            {
 0357                avatar.AddVisibilityConstrain(IN_HIDE_AREA);
 0358                onPointerDown.gameObject.SetActive(false);
 0359                playerNameContainer.SetActive(false);
 0360                stickersControllers.ToggleHideArea(true);
 361            }
 0362            currentActiveModifiers.AddRefCount(AvatarModifierAreaID.HIDE_AVATAR);
 0363        }
 364
 365        public void RemoveHideAvatarModifier()
 366        {
 0367            currentActiveModifiers.RemoveRefCount(AvatarModifierAreaID.HIDE_AVATAR);
 0368            if (!currentActiveModifiers.ContainsKey(AvatarModifierAreaID.HIDE_AVATAR))
 369            {
 0370                avatar.RemoveVisibilityConstrain(IN_HIDE_AREA);
 0371                onPointerDown.gameObject.SetActive(true);
 0372                playerNameContainer.SetActive(true);
 0373                stickersControllers.ToggleHideArea(false);
 374            }
 0375        }
 376
 377        public void ApplyHidePassportModifier()
 378        {
 0379            if (!currentActiveModifiers.ContainsKey(AvatarModifierAreaID.DISABLE_PASSPORT))
 380            {
 0381                DisablePassport();
 382            }
 0383            currentActiveModifiers.AddRefCount(AvatarModifierAreaID.DISABLE_PASSPORT);
 0384        }
 385
 386        public void RemoveHidePassportModifier()
 387        {
 0388            currentActiveModifiers.RemoveRefCount(AvatarModifierAreaID.DISABLE_PASSPORT);
 0389            if (!currentActiveModifiers.ContainsKey(AvatarModifierAreaID.DISABLE_PASSPORT))
 390            {
 0391                EnablePasssport();
 392            }
 0393        }
 394
 395        private void EnablePasssport()
 396        {
 0397            if (onPointerDown.collider == null)
 0398                return;
 399
 0400            onPointerDown.SetPassportEnabled(true);
 0401        }
 402
 403        private void DisablePassport()
 404        {
 5405            if (onPointerDown.collider == null)
 0406                return;
 407
 5408            onPointerDown.SetPassportEnabled(false);
 5409        }
 410
 411        public override void Cleanup()
 412        {
 411413            base.Cleanup();
 414
 411415            playerName?.Hide(true);
 411416            if (player != null)
 417            {
 0418                otherPlayers.Remove(player.id);
 0419                player = null;
 420            }
 421
 411422            loadingCts?.Cancel();
 411423            loadingCts?.Dispose();
 411424            loadingCts = null;
 411425            currentLazyObserver?.RemoveListener(avatar.SetImpostorTexture);
 411426            avatar.Dispose();
 427
 411428            if (poolableObject != null)
 429            {
 15430                poolableObject.OnRelease -= Cleanup;
 431            }
 432
 411433            onPointerDown.OnPointerDownReport -= PlayerClicked;
 411434            onPointerDown.OnPointerEnterReport -= PlayerPointerEnter;
 411435            onPointerDown.OnPointerExitReport -= PlayerPointerExit;
 436
 411437            if (entity != null)
 438            {
 5439                entity.OnTransformChange = null;
 5440                entity = null;
 441            }
 442
 411443            avatarReporterController.ReportAvatarRemoved();
 411444        }
 445
 1446        public override int GetClassId() { return (int) CLASS_ID_COMPONENT.AVATAR_SHAPE; }
 447
 448        [ContextMenu("Print current profile")]
 0449        private void PrintCurrentProfile() { Debug.Log(JsonUtility.ToJson(model)); }
 450
 451    }
 452}