< 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:79
Coverable lines:151
Total lines:339
Line coverage:47.6% (72 of 151)
Covered branches:0
Total branches:0

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity NPath complexity Sequence coverage
AvatarShape()0%110100%
Awake()0%220100%
Start()0%220100%
PlayerClicked()0%6200%
OnDestroy()0%330100%
ApplyChanges()0%46.9515047.83%
SetImpostor(...)0%8.744033.33%
PlayerPointerExit()0%6200%
PlayerPointerEnter()0%6200%
UpdatePlayerStatus(...)0%90900%
Update()0%2.862040%
DisablePassport()0%2.062075%
EnablePassport()0%6200%
OnEntityTransformChanged(...)0%2100%
OnPoolGet()0%110100%
Cleanup()0%8.068090%
GetClassId()0%2100%
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.Helpers;
 11using DCL.Models;
 12using GPUSkinning;
 13using UnityEngine;
 14using Avatar = AvatarSystem.Avatar;
 15using LOD = AvatarSystem.LOD;
 16
 17namespace DCL
 18{
 19    public class AvatarShape : BaseComponent
 20    {
 21        private const string CURRENT_PLAYER_ID = "CurrentPlayerInfoCardId";
 22        private const float MINIMUM_PLAYERNAME_HEIGHT = 2.7f;
 23        private const float AVATAR_PASSPORT_TOGGLE_ALPHA_THRESHOLD = 0.9f;
 24
 25        public static event Action<IDCLEntity, AvatarShape> OnAvatarShapeUpdated;
 26
 27        public GameObject avatarContainer;
 28        public Collider avatarCollider;
 29        public AvatarMovementController avatarMovementController;
 30        [SerializeField] private GameObject onloadParticlePrefab;
 31
 32        [SerializeField] internal AvatarOnPointerDown onPointerDown;
 33        internal IPlayerName playerName;
 34        internal IAvatarReporterController avatarReporterController;
 35
 36        private StringVariable currentPlayerInfoCardId;
 37
 38        public bool everythingIsLoaded;
 39
 40        private Vector3? lastAvatarPosition = null;
 41        bool initializedPosition = false;
 42
 43        private Player player = null;
 044        private BaseDictionary<string, Player> otherPlayers => DataStore.i.player.otherPlayers;
 45
 68846        private IAvatarAnchorPoints anchorPoints = new AvatarAnchorPoints();
 47        private IAvatar avatar;
 68848        private readonly AvatarModel currentAvatar = new AvatarModel { wearables = new List<string>() };
 49        private CancellationTokenSource loadingCts;
 50        private ILazyTextureObserver currentLazyObserver;
 51
 52        private void Awake()
 53        {
 68754            model = new AvatarModel();
 68755            currentPlayerInfoCardId = Resources.Load<StringVariable>(CURRENT_PLAYER_ID);
 68756            Visibility visibility = new Visibility();
 68757            LOD avatarLOD = new LOD(avatarContainer, visibility, avatarMovementController);
 68758            avatar = new Avatar(
 59                new AvatarCurator(new WearableItemResolver()),
 60                new Loader(new WearableLoaderFactory(), avatarContainer, new AvatarMeshCombinerHelper()),
 61                GetComponentInChildren<AvatarAnimatorLegacy>(),
 62                visibility,
 63                avatarLOD,
 64                new SimpleGPUSkinning(),
 65                new GPUSkinningThrottler());
 66
 68767            if (avatarReporterController == null)
 68            {
 68769                avatarReporterController = new AvatarReporterController(Environment.i.world.state);
 70            }
 68771        }
 72
 73        private void Start()
 74        {
 175            playerName = GetComponentInChildren<IPlayerName>();
 176            playerName?.Hide(true);
 177        }
 78
 79        private void PlayerClicked()
 80        {
 081            if (model == null)
 082                return;
 083            currentPlayerInfoCardId.Set(((AvatarModel) model).id);
 084        }
 85
 86        public void OnDestroy()
 87        {
 68788            Cleanup();
 89
 68790            if (poolableObject != null && poolableObject.isInsidePool)
 291                poolableObject.RemoveFromPool();
 68792        }
 93
 94        public override IEnumerator ApplyChanges(BaseModel newModel)
 95        {
 296            DisablePassport();
 97
 298            var model = (AvatarModel) newModel;
 99
 2100            bool needsLoading = !model.HaveSameWearablesAndColors(currentAvatar);
 2101            currentAvatar.CopyFrom(model);
 102
 2103            if (string.IsNullOrEmpty(model.bodyShape) || model.wearables.Count == 0)
 0104                yield break;
 105#if UNITY_EDITOR
 2106            gameObject.name = $"Avatar Shape {model.name}";
 107#endif
 2108            everythingIsLoaded = false;
 109
 110            bool avatarDone = false;
 111            bool avatarFailed = false;
 112
 2113            yield return null; //NOTE(Brian): just in case we have a Object.Destroy waiting to be resolved.
 114
 115            // To deal with the cases in which the entity transform was configured before the AvatarShape
 1116            if (!initializedPosition && entity.components.ContainsKey(DCL.Models.CLASS_ID_COMPONENT.TRANSFORM))
 117            {
 1118                initializedPosition = true;
 119
 1120                float characterHeight = DCLCharacterController.i != null ? DCLCharacterController.i.characterController.
 121
 1122                avatarMovementController.MoveTo(
 123                    entity.gameObject.transform.localPosition - Vector3.up * characterHeight / 2,
 124                    entity.gameObject.transform.localRotation, true);
 125            }
 126
 1127            var wearableItems = model.wearables.ToList();
 1128            wearableItems.Add(model.bodyShape);
 1129            if (avatar.status != IAvatar.Status.Loaded || needsLoading)
 130            {
 131                //TODO Add Collider to the AvatarSystem
 132                //TODO Without this the collider could get triggered disabling the avatar container,
 133                // this would stop the loading process due to the underlying coroutines of the AssetLoader not starting
 1134                avatarCollider.gameObject.SetActive(false);
 135
 1136                SetImpostor(model.id);
 1137                loadingCts?.Cancel();
 1138                loadingCts?.Dispose();
 1139                loadingCts = new CancellationTokenSource();
 140
 1141                avatar.Load(wearableItems, new AvatarSettings
 142                {
 143                    playerName = model.name,
 144                    bodyshapeId = model.bodyShape,
 145                    eyesColor = model.eyeColor,
 146                    skinColor = model.skinColor,
 147                    hairColor = model.hairColor,
 148                }, loadingCts.Token);
 149
 150                // Yielding a UniTask doesn't do anything, we manually wait until the avatar is ready
 3151                yield return new WaitUntil(() => avatar.status == IAvatar.Status.Loaded);
 152
 0153                if (avatar.lodLevel <= 1)
 0154                    AvatarSystemUtils.SpawnAvatarLoadedParticles(avatarContainer.transform, onloadParticlePrefab);
 155            }
 156
 0157            avatar.SetExpression(model.expressionTriggerId, model.expressionTriggerTimestamp);
 158
 0159            entity.OnTransformChange -= avatarMovementController.OnTransformChanged;
 0160            entity.OnTransformChange += avatarMovementController.OnTransformChanged;
 161
 0162            entity.OnTransformChange -= OnEntityTransformChanged;
 0163            entity.OnTransformChange += OnEntityTransformChanged;
 164
 0165            onPointerDown.OnPointerDownReport -= PlayerClicked;
 0166            onPointerDown.OnPointerDownReport += PlayerClicked;
 0167            onPointerDown.OnPointerEnterReport -= PlayerPointerEnter;
 0168            onPointerDown.OnPointerEnterReport += PlayerPointerEnter;
 0169            onPointerDown.OnPointerExitReport -= PlayerPointerExit;
 0170            onPointerDown.OnPointerExitReport += PlayerPointerExit;
 171
 0172            UpdatePlayerStatus(model);
 173
 0174            onPointerDown.Initialize(
 175                new OnPointerDown.Model()
 176                {
 177                    type = OnPointerDown.NAME,
 178                    button = WebInterface.ACTION_BUTTON.POINTER.ToString(),
 179                    hoverText = "view profile"
 180                },
 181                entity, player
 182            );
 183
 0184            avatarCollider.gameObject.SetActive(true);
 185
 0186            everythingIsLoaded = true;
 0187            OnAvatarShapeUpdated?.Invoke(entity, this);
 188
 0189            EnablePassport();
 190
 0191            bool isAvatarGlobalScene = scene.sceneData.id == EnvironmentSettings.AVATAR_GLOBAL_SCENE_ID;
 0192            onPointerDown.SetColliderEnabled(isAvatarGlobalScene);
 0193            onPointerDown.SetOnClickReportEnabled(isAvatarGlobalScene);
 0194        }
 195
 196        public void SetImpostor(string userId)
 197        {
 1198            currentLazyObserver?.RemoveListener(avatar.SetImpostorTexture);
 1199            if (string.IsNullOrEmpty(userId))
 1200                return;
 201
 0202            UserProfile userProfile = UserProfileController.GetProfileByUserId(userId);
 0203            if (userProfile == null)
 0204                return;
 205
 0206            currentLazyObserver = userProfile.bodySnapshotObserver;
 0207            currentLazyObserver.AddListener(avatar.SetImpostorTexture);
 0208        }
 209
 0210        private void PlayerPointerExit() { playerName?.SetForceShow(false); }
 0211        private void PlayerPointerEnter() { playerName?.SetForceShow(true); }
 212
 213        private void UpdatePlayerStatus(AvatarModel model)
 214        {
 215            // Remove the player status if the userId changes
 0216            if (player != null && (player.id != model.id || player.name != model.name))
 0217                otherPlayers.Remove(player.id);
 218
 0219            if (string.IsNullOrEmpty(model?.id))
 0220                return;
 221
 0222            bool isNew = false;
 0223            if (player == null)
 224            {
 0225                player = new Player();
 0226                isNew = true;
 227            }
 228
 0229            player.id = model.id;
 0230            player.name = model.name;
 0231            player.isTalking = model.talking;
 0232            player.worldPosition = entity.gameObject.transform.position;
 0233            player.avatar = avatar;
 0234            player.onPointerDownCollider = onPointerDown;
 235
 0236            if (isNew)
 237            {
 0238                player.playerName = playerName;
 0239                player.playerName.SetName(player.name);
 0240                player.playerName.Show();
 0241                player.anchorPoints = anchorPoints;
 0242                otherPlayers.Add(player.id, player);
 0243                avatarReporterController.ReportAvatarRemoved();
 244            }
 245
 0246            avatarReporterController.SetUp(entity.scene.sceneData.id, entity.entityId, player.id);
 247
 0248            float height = AvatarSystemUtils.AVATAR_Y_OFFSET + avatar.extents.y;
 249
 0250            anchorPoints.Prepare(avatarContainer.transform, avatar.GetBones(), height);
 251
 0252            player.playerName.SetIsTalking(model.talking);
 0253            player.playerName.SetYOffset(Mathf.Max(MINIMUM_PLAYERNAME_HEIGHT, height));
 0254        }
 255
 256        private void Update()
 257        {
 2258            if (player != null)
 259            {
 0260                player.worldPosition = entity.gameObject.transform.position;
 0261                player.forwardDirection = entity.gameObject.transform.forward;
 0262                avatarReporterController.ReportAvatarPosition(player.worldPosition);
 263            }
 2264        }
 265
 266        public void DisablePassport()
 267        {
 2268            if (onPointerDown.collider == null)
 0269                return;
 270
 2271            onPointerDown.SetPassportEnabled(false);
 2272        }
 273
 274        public void EnablePassport()
 275        {
 0276            if (onPointerDown.collider == null)
 0277                return;
 278
 0279            onPointerDown.SetPassportEnabled(true);
 0280        }
 281
 282        private void OnEntityTransformChanged(object newModel)
 283        {
 0284            DCLTransform.Model newTransformModel = (DCLTransform.Model)newModel;
 0285            lastAvatarPosition = newTransformModel.position;
 0286        }
 287
 288        public override void OnPoolGet()
 289        {
 2290            base.OnPoolGet();
 291
 2292            everythingIsLoaded = false;
 2293            initializedPosition = false;
 2294            model = new AvatarModel();
 2295            lastAvatarPosition = null;
 2296            player = null;
 2297        }
 298
 299        public override void Cleanup()
 300        {
 690301            base.Cleanup();
 302
 690303            playerName?.Hide(true);
 690304            if (player != null)
 305            {
 0306                otherPlayers.Remove(player.id);
 0307                player = null;
 308            }
 309
 690310            loadingCts?.Cancel();
 690311            loadingCts?.Dispose();
 690312            loadingCts = null;
 690313            currentLazyObserver?.RemoveListener(avatar.SetImpostorTexture);
 690314            avatar.Dispose();
 315
 690316            if (poolableObject != null)
 317            {
 5318                poolableObject.OnRelease -= Cleanup;
 319            }
 320
 690321            onPointerDown.OnPointerDownReport -= PlayerClicked;
 690322            onPointerDown.OnPointerEnterReport -= PlayerPointerEnter;
 690323            onPointerDown.OnPointerExitReport -= PlayerPointerExit;
 324
 690325            if (entity != null)
 326            {
 2327                entity.OnTransformChange = null;
 2328                entity = null;
 329            }
 330
 690331            avatarReporterController.ReportAvatarRemoved();
 690332        }
 333
 0334        public override int GetClassId() { return (int) CLASS_ID_COMPONENT.AVATAR_SHAPE; }
 335
 336        [ContextMenu("Print current profile")]
 0337        private void PrintCurrentProfile() { Debug.Log(JsonUtility.ToJson(model)); }
 338    }
 339}