< Summary

Class:DCL.AvatarShape
Assembly:AvatarShape
File(s):/tmp/workspace/unity-renderer/unity-renderer/Assets/Scripts/MainScripts/DCL/Components/Avatar/AvatarShape.cs
Covered lines:68
Uncovered lines:113
Coverable lines:181
Total lines:414
Line coverage:37.5% (68 of 181)
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%220100%
PlayerClicked()0%6200%
OnDestroy()0%330100%
ApplyChanges()0%107.9514021.74%
SetImpostor(...)0%20400%
PlayerPointerExit()0%6200%
PlayerPointerEnter()0%6200%
UpdatePlayerStatus(...)0%1821300%
Update()0%2.862040%
DisablePassport()0%2.062075%
EnablePassport()0%6200%
OnEntityTransformChanged(...)0%2100%
OnEntityTransformChanged(...)0%6200%
OnPoolGet()0%110100%
ApplyHideModifier()0%2100%
RemoveHideModifier()0%2100%
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
 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        [SerializeField] private Transform avatarRevealContainer;
 33        [SerializeField] private GameObject armatureContainer;
 34
 35        [SerializeField] internal AvatarOnPointerDown onPointerDown;
 36        [SerializeField] internal GameObject playerNameContainer;
 37        internal IPlayerName playerName;
 38        internal IAvatarReporterController avatarReporterController;
 39
 40        private StringVariable currentPlayerInfoCardId;
 41
 42        public bool everythingIsLoaded;
 43
 44        bool initializedPosition = false;
 45
 46        private Player player = null;
 047        private BaseDictionary<string, Player> otherPlayers => DataStore.i.player.otherPlayers;
 48
 39549        private IAvatarAnchorPoints anchorPoints = new AvatarAnchorPoints();
 50        internal IAvatar avatar;
 39551        private readonly AvatarModel currentAvatar = new AvatarModel { wearables = new List<string>() };
 52        private CancellationTokenSource loadingCts;
 53        private ILazyTextureObserver currentLazyObserver;
 39554        private bool isGlobalSceneAvatar = true;
 55
 456        public override string componentName => "avatarShape";
 57
 58        private void Awake()
 59        {
 39460            model = new AvatarModel();
 39461            currentPlayerInfoCardId = Resources.Load<StringVariable>(CURRENT_PLAYER_ID);
 62
 39463            if (DataStore.i.avatarConfig.useHologramAvatar.Get())
 264                avatar = GetAvatarWithHologram();
 65            else
 39266                avatar = GetStandardAvatar();
 67
 39468            if (avatarReporterController == null)
 69            {
 39470                avatarReporterController = new AvatarReporterController(Environment.i.world.state);
 71            }
 39472        }
 73
 74        private Avatar GetStandardAvatar()
 75        {
 39276            Visibility visibility = new Visibility();
 39277            LOD avatarLOD = new LOD(avatarContainer, visibility, avatarMovementController);
 39278            AvatarAnimatorLegacy animator = GetComponentInChildren<AvatarAnimatorLegacy>();
 39279            return new Avatar(
 80                new AvatarCurator(new WearableItemResolver()),
 81                new Loader(new WearableLoaderFactory(), avatarContainer, new AvatarMeshCombinerHelper()),
 82                animator,
 83                visibility,
 84                avatarLOD,
 85                new SimpleGPUSkinning(),
 86                new GPUSkinningThrottler(),
 87                new EmoteAnimationEquipper(animator, DataStore.i.emotes));
 88        }
 89
 90        private AvatarWithHologram GetAvatarWithHologram()
 91        {
 292            Visibility visibility = new Visibility();
 293            LOD avatarLOD = new LOD(avatarContainer, visibility, avatarMovementController);
 294            AvatarAnimatorLegacy animator = GetComponentInChildren<AvatarAnimatorLegacy>();
 295            BaseAvatar baseAvatar = new BaseAvatar(avatarRevealContainer, armatureContainer, avatarLOD);
 296            return new AvatarWithHologram(
 97                    baseAvatar,
 98                    new AvatarCurator(new WearableItemResolver()),
 99                    new Loader(new WearableLoaderFactory(), avatarContainer, new AvatarMeshCombinerHelper()),
 100                    animator,
 101                    visibility,
 102                    avatarLOD,
 103                    new SimpleGPUSkinning(),
 104                    new GPUSkinningThrottler(),
 105                    new EmoteAnimationEquipper(animator, DataStore.i.emotes));
 106        }
 107
 108        private void Start()
 109        {
 1110            playerName = GetComponentInChildren<IPlayerName>();
 1111            playerName?.Hide(true);
 1112        }
 113
 114        private void PlayerClicked()
 115        {
 0116            if (model == null)
 0117                return;
 0118            currentPlayerInfoCardId.Set(((AvatarModel) model).id);
 0119        }
 120
 121        public void OnDestroy()
 122        {
 394123            Cleanup();
 124
 394125            if (poolableObject != null && poolableObject.isInsidePool)
 5126                poolableObject.RemoveFromPool();
 394127        }
 128
 129        public override IEnumerator ApplyChanges(BaseModel newModel)
 130        {
 5131            isGlobalSceneAvatar = scene.sceneData.id == EnvironmentSettings.AVATAR_GLOBAL_SCENE_ID;
 132
 5133            DisablePassport();
 134
 5135            var model = (AvatarModel) newModel;
 136
 5137            bool needsLoading = !model.HaveSameWearablesAndColors(currentAvatar);
 5138            currentAvatar.CopyFrom(model);
 139
 5140            if (string.IsNullOrEmpty(model.bodyShape) || model.wearables.Count == 0)
 1141                yield break;
 142#if UNITY_EDITOR
 4143            gameObject.name = $"Avatar Shape {model.name}";
 144#endif
 4145            everythingIsLoaded = false;
 146
 147            bool avatarDone = false;
 148            bool avatarFailed = false;
 149
 4150            yield return null; //NOTE(Brian): just in case we have a Object.Destroy waiting to be resolved.
 151
 152            // To deal with the cases in which the entity transform was configured before the AvatarShape
 0153            if (!initializedPosition && scene.componentsManagerLegacy.HasComponent(entity, CLASS_ID_COMPONENT.TRANSFORM)
 154            {
 0155                initializedPosition = true;
 0156                OnEntityTransformChanged(entity.gameObject.transform.localPosition,
 157                    entity.gameObject.transform.localRotation, true);
 158            }
 159
 160            // NOTE: we subscribe here to transform changes since we might "lose" the message
 161            // if we subscribe after a any yield
 0162            entity.OnTransformChange -= OnEntityTransformChanged;
 0163            entity.OnTransformChange += OnEntityTransformChanged;
 164
 0165            var wearableItems = model.wearables.ToList();
 0166            wearableItems.Add(model.bodyShape);
 167
 168            //temporarily hardcoding the embedded emotes until the user profile provides the equipped ones
 0169            var embeddedEmotesSo = Resources.Load<EmbeddedEmotesSO>("EmbeddedEmotes");
 0170            wearableItems.AddRange(embeddedEmotesSo.emotes.Select(x => x.id));
 171
 0172            if (avatar.status != IAvatar.Status.Loaded || needsLoading)
 173            {
 174                //TODO Add Collider to the AvatarSystem
 175                //TODO Without this the collider could get triggered disabling the avatar container,
 176                // this would stop the loading process due to the underlying coroutines of the AssetLoader not starting
 0177                avatarCollider.gameObject.SetActive(false);
 178
 0179                SetImpostor(model.id);
 0180                loadingCts?.Cancel();
 0181                loadingCts?.Dispose();
 0182                loadingCts = new CancellationTokenSource();
 0183                if (DataStore.i.avatarConfig.useHologramAvatar.Get())
 184                {
 0185                    playerName.SetName(model.name);
 0186                    playerName.Show(true);
 187                }
 0188                avatar.Load(wearableItems, new AvatarSettings
 189                {
 190                    playerName = model.name,
 191                    bodyshapeId = model.bodyShape,
 192                    eyesColor = model.eyeColor,
 193                    skinColor = model.skinColor,
 194                    hairColor = model.hairColor,
 195                }, loadingCts.Token);
 196
 197                // Yielding a UniTask doesn't do anything, we manually wait until the avatar is ready
 0198                yield return new WaitUntil(() => avatar.status == IAvatar.Status.Loaded);
 199            }
 200
 0201            avatar.PlayEmote(model.expressionTriggerId, model.expressionTriggerTimestamp);
 202
 0203            onPointerDown.OnPointerDownReport -= PlayerClicked;
 0204            onPointerDown.OnPointerDownReport += PlayerClicked;
 0205            onPointerDown.OnPointerEnterReport -= PlayerPointerEnter;
 0206            onPointerDown.OnPointerEnterReport += PlayerPointerEnter;
 0207            onPointerDown.OnPointerExitReport -= PlayerPointerExit;
 0208            onPointerDown.OnPointerExitReport += PlayerPointerExit;
 209
 0210            UpdatePlayerStatus(model);
 211
 0212            onPointerDown.Initialize(
 213                new OnPointerDown.Model()
 214                {
 215                    type = OnPointerDown.NAME,
 216                    button = WebInterface.ACTION_BUTTON.POINTER.ToString(),
 217                    hoverText = "view profile"
 218                },
 219                entity, player
 220            );
 221
 0222            avatarCollider.gameObject.SetActive(true);
 223
 0224            everythingIsLoaded = true;
 0225            OnAvatarShapeUpdated?.Invoke(entity, this);
 226
 0227            EnablePassport();
 228
 0229            onPointerDown.SetColliderEnabled(isGlobalSceneAvatar);
 0230            onPointerDown.SetOnClickReportEnabled(isGlobalSceneAvatar);
 0231        }
 232
 233        public void SetImpostor(string userId)
 234        {
 0235            currentLazyObserver?.RemoveListener(avatar.SetImpostorTexture);
 0236            if (string.IsNullOrEmpty(userId))
 0237                return;
 238
 0239            UserProfile userProfile = UserProfileController.GetProfileByUserId(userId);
 0240            if (userProfile == null)
 0241                return;
 242
 0243            currentLazyObserver = userProfile.bodySnapshotObserver;
 0244            currentLazyObserver.AddListener(avatar.SetImpostorTexture);
 0245        }
 246
 0247        private void PlayerPointerExit() { playerName?.SetForceShow(false); }
 0248        private void PlayerPointerEnter() { playerName?.SetForceShow(true); }
 249
 250        private void UpdatePlayerStatus(AvatarModel model)
 251        {
 252            // Remove the player status if the userId changes
 0253            if (player != null && (player.id != model.id || player.name != model.name))
 0254                otherPlayers.Remove(player.id);
 255
 0256            if (isGlobalSceneAvatar && string.IsNullOrEmpty(model?.id))
 0257                return;
 258
 0259            bool isNew = player == null;
 0260            if (isNew)
 261            {
 0262                player = new Player();
 263            }
 264
 0265            bool isNameDirty = player.name != model.name;
 266
 0267            player.id = model.id;
 0268            player.name = model.name;
 0269            player.isTalking = model.talking;
 0270            player.worldPosition = entity.gameObject.transform.position;
 0271            player.avatar = avatar;
 0272            player.onPointerDownCollider = onPointerDown;
 0273            player.collider = avatarCollider;
 274
 0275            if (isNew)
 276            {
 0277                player.playerName = playerName;
 0278                player.playerName.Show();
 0279                player.anchorPoints = anchorPoints;
 0280                if (isGlobalSceneAvatar)
 281                {
 282                    // TODO: Note: This is having a problem, sometimes the users has been detected as new 2 times and it
 283                    // we should investigate this
 0284                    if (otherPlayers.ContainsKey(player.id))
 0285                        otherPlayers.Remove(player.id);
 0286                    otherPlayers.Add(player.id, player);
 287                }
 0288                avatarReporterController.ReportAvatarRemoved();
 289            }
 290
 0291            avatarReporterController.SetUp(entity.scene.sceneData.id, player.id);
 292
 0293            float height = AvatarSystemUtils.AVATAR_Y_OFFSET + avatar.extents.y;
 294
 0295            anchorPoints.Prepare(avatarContainer.transform, avatar.GetBones(), height);
 296
 0297            player.playerName.SetIsTalking(model.talking);
 0298            player.playerName.SetYOffset(Mathf.Max(MINIMUM_PLAYERNAME_HEIGHT, height));
 0299            if (isNameDirty)
 0300                player.playerName.SetName(model.name);
 0301        }
 302
 303        private void Update()
 304        {
 1305            if (player != null)
 306            {
 0307                player.worldPosition = entity.gameObject.transform.position;
 0308                player.forwardDirection = entity.gameObject.transform.forward;
 0309                avatarReporterController.ReportAvatarPosition(player.worldPosition);
 310            }
 1311        }
 312
 313        public void DisablePassport()
 314        {
 5315            if (onPointerDown.collider == null)
 0316                return;
 317
 5318            onPointerDown.SetPassportEnabled(false);
 5319        }
 320
 321        public void EnablePassport()
 322        {
 0323            if (onPointerDown.collider == null)
 0324                return;
 325
 0326            onPointerDown.SetPassportEnabled(true);
 0327        }
 328
 329        private void OnEntityTransformChanged(object newModel)
 330        {
 0331            DCLTransform.Model newTransformModel = (DCLTransform.Model)newModel;
 0332            OnEntityTransformChanged(newTransformModel.position, newTransformModel.rotation, !initializedPosition);
 0333        }
 334
 335        private void OnEntityTransformChanged(in Vector3 position, in Quaternion rotation, bool inmediate)
 336        {
 0337            if (isGlobalSceneAvatar)
 338            {
 0339                avatarMovementController.OnTransformChanged(position, rotation, inmediate);
 0340            }
 341            else
 342            {
 0343                var scenePosition = Utils.GridToWorldPosition(entity.scene.sceneData.basePosition.x, entity.scene.sceneD
 0344                avatarMovementController.OnTransformChanged(scenePosition + position, rotation, inmediate);
 345            }
 0346            initializedPosition = true;
 0347        }
 348
 349        public override void OnPoolGet()
 350        {
 5351            base.OnPoolGet();
 352
 5353            everythingIsLoaded = false;
 5354            initializedPosition = false;
 5355            model = new AvatarModel();
 5356            player = null;
 5357        }
 358
 359        public void ApplyHideModifier()
 360        {
 0361            avatar.AddVisibilityConstrain(IN_HIDE_AREA);
 0362            onPointerDown.gameObject.SetActive(false);
 0363            playerNameContainer.SetActive(false);
 364
 0365        }
 366
 367        public void RemoveHideModifier()
 368        {
 0369            avatar.RemoveVisibilityConstrain(IN_HIDE_AREA);
 0370            onPointerDown.gameObject.SetActive(true);
 0371            playerNameContainer.SetActive(true);
 0372        }
 373
 374        public override void Cleanup()
 375        {
 404376            base.Cleanup();
 377
 404378            playerName?.Hide(true);
 404379            if (player != null)
 380            {
 0381                otherPlayers.Remove(player.id);
 0382                player = null;
 383            }
 384
 404385            loadingCts?.Cancel();
 404386            loadingCts?.Dispose();
 404387            loadingCts = null;
 404388            currentLazyObserver?.RemoveListener(avatar.SetImpostorTexture);
 404389            avatar.Dispose();
 390
 404391            if (poolableObject != null)
 392            {
 15393                poolableObject.OnRelease -= Cleanup;
 394            }
 395
 404396            onPointerDown.OnPointerDownReport -= PlayerClicked;
 404397            onPointerDown.OnPointerEnterReport -= PlayerPointerEnter;
 404398            onPointerDown.OnPointerExitReport -= PlayerPointerExit;
 399
 404400            if (entity != null)
 401            {
 5402                entity.OnTransformChange = null;
 5403                entity = null;
 404            }
 405
 404406            avatarReporterController.ReportAvatarRemoved();
 404407        }
 408
 1409        public override int GetClassId() { return (int) CLASS_ID_COMPONENT.AVATAR_SHAPE; }
 410
 411        [ContextMenu("Print current profile")]
 0412        private void PrintCurrentProfile() { Debug.Log(JsonUtility.ToJson(model)); }
 413    }
 414}