< Summary

Class:DCL.Components.LoadableShape
Assembly:DCL.Components.LoadableShapes
File(s):/tmp/workspace/unity-renderer/unity-renderer/Assets/Scripts/MainScripts/DCL/Components/LoadableShapes/LoadableShape.cs
Covered lines:8
Uncovered lines:17
Coverable lines:25
Total lines:352
Line coverage:32% (8 of 25)
Covered branches:0
Total branches:0
Covered methods:6
Total methods:10
Method coverage:60% (6 of 10)

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity NPath complexity Sequence coverage
GetDataFromJSON(...)0%110100%
GetDataFromPb(...)0%1321100%
LoadableShape()0%110100%
GetClassId()0%2100%
ApplyChanges(...)0%2100%
IsVisible()0%110100%
HasCollisions()0%110100%
GetAssetId()0%2100%

File(s)

/tmp/workspace/unity-renderer/unity-renderer/Assets/Scripts/MainScripts/DCL/Components/LoadableShapes/LoadableShape.cs

#LineLine coverage
 1using System;
 2using DCL.Helpers;
 3using DCL.Models;
 4using System.Collections;
 5using UnityEngine;
 6using Decentraland.Sdk.Ecs6;
 7
 8namespace DCL.Components
 9{
 10    public class LoadableShape : BaseShape, IAssetCatalogReferenceHolder
 11    {
 12        [Serializable]
 13        public new class Model : BaseShape.Model
 14        {
 15            public string src;
 16            public string assetId;
 17
 18            public override BaseModel GetDataFromJSON(string json) =>
 6719                Utils.SafeFromJson<Model>(json);
 20
 21            public override BaseModel GetDataFromPb(ComponentBodyPayload pbModel)
 22            {
 023                switch (pbModel.PayloadCase)
 24                {
 25                    case ComponentBodyPayload.PayloadOneofCase.GltfShape:
 026                        var gltfModel = new Model();
 027                        if (pbModel.GltfShape.HasSrc) gltfModel.src = pbModel.GltfShape.Src;
 028                        if (pbModel.GltfShape.HasWithCollisions) gltfModel.withCollisions = pbModel.GltfShape.WithCollis
 029                        if (pbModel.GltfShape.HasVisible) gltfModel.visible = pbModel.GltfShape.Visible;
 030                        if (pbModel.GltfShape.HasIsPointerBlocker) gltfModel.isPointerBlocker = pbModel.GltfShape.IsPoin
 31
 032                        return gltfModel;
 33                    case ComponentBodyPayload.PayloadOneofCase.ObjShape:
 034                        var objModel = new Model();
 035                        if (pbModel.ObjShape.HasSrc) objModel.src = pbModel.ObjShape.Src;
 036                        if (pbModel.ObjShape.HasWithCollisions) objModel.withCollisions = pbModel.ObjShape.WithCollision
 037                        if (pbModel.ObjShape.HasVisible) objModel.visible = pbModel.ObjShape.Visible;
 038                        if (pbModel.ObjShape.HasIsPointerBlocker) objModel.isPointerBlocker = pbModel.ObjShape.IsPointer
 39
 040                        return objModel;
 41                    default:
 042                        return Utils.SafeUnimplemented<LoadableShape, Model>(
 43                            expected: ComponentBodyPayload.PayloadOneofCase.GltfShape, actual: pbModel.PayloadCase);
 44                }
 45            }
 46        }
 47
 28348        public bool isLoaded { get; protected set; }
 49
 50        public Action<LoadableShape> OnLoaded;
 51
 6752        protected Model previousModel = new ();
 53
 6754        protected LoadableShape()
 55        {
 6756            model = new Model();
 6757        }
 58
 59        public override int GetClassId() =>
 060            -1;
 61
 62        public override IEnumerator ApplyChanges(BaseModel newModel) =>
 063            null;
 64
 65        public override bool IsVisible() =>
 4366            ((Model)this.model).visible;
 67
 68        public override bool HasCollisions() =>
 6769            ((Model)this.model).withCollisions;
 70
 71        public string GetAssetId() =>
 072            ((Model)this.model).assetId;
 73    }
 74
 75    public class LoadableShape<LoadWrapperType, LoadWrapperModelType> : LoadableShape
 76        where LoadWrapperType: LoadWrapper, new()
 77        where LoadWrapperModelType: LoadableShape.Model, new()
 78    {
 79        private bool failed = false;
 80        private event Action<BaseDisposable> OnFinishCallbacks;
 81        public System.Action<IDCLEntity> OnEntityShapeUpdated;
 82
 83        new public LoadWrapperModelType model
 84        {
 85            get
 86            {
 87                if (base.model == null)
 88                    base.model = new LoadWrapperModelType();
 89
 90                return base.model as LoadWrapperModelType;
 91            }
 92
 93            set
 94            {
 95                base.model = value;
 96            }
 97        }
 98
 99        new protected LoadWrapperModelType previousModel
 100        {
 101            get
 102            {
 103                if (base.previousModel == null)
 104                    base.previousModel = new LoadWrapperModelType();
 105
 106                return base.previousModel as LoadWrapperModelType;
 107            }
 108
 109            set
 110            {
 111                base.previousModel = value;
 112            }
 113        }
 114
 115        public LoadableShape()
 116        {
 117            OnDetach += DetachShape;
 118            OnAttach += AttachShape;
 119        }
 120
 121        public override IEnumerator ApplyChanges(BaseModel newModel)
 122        {
 123            LoadWrapperModelType model = (LoadWrapperModelType)newModel;
 124
 125            bool updateVisibility = true;
 126            bool updateCollisions = true;
 127            bool triggerAttachment = true;
 128
 129            if (previousModel != null)
 130            {
 131                updateVisibility = previousModel.visible != model.visible;
 132                updateCollisions = previousModel.withCollisions != model.withCollisions || previousModel.isPointerBlocke
 133
 134                triggerAttachment = (!string.IsNullOrEmpty(model.src) && previousModel.src != model.src) ||
 135                                    (!string.IsNullOrEmpty(model.assetId) && previousModel.assetId != model.assetId);
 136            }
 137
 138            foreach (var entity in attachedEntities)
 139            {
 140                if (triggerAttachment)
 141                    AttachShape(entity);
 142
 143                if (updateVisibility)
 144                    ConfigureVisibility(entity);
 145
 146                if (updateCollisions)
 147                    ConfigureColliders(entity);
 148
 149                RaiseOnShapeUpdated(entity);
 150            }
 151
 152            previousModel = model;
 153            return null;
 154        }
 155
 156        protected virtual void AttachShape(IDCLEntity entity)
 157        {
 158            ContentProvider provider = null;
 159
 160            if (!string.IsNullOrEmpty(model.assetId))
 161            {
 162                provider = AssetCatalogBridge.i.GetContentProviderForAssetIdInSceneObjectCatalog(model.assetId);
 163
 164                if (string.IsNullOrEmpty(model.src))
 165                {
 166                    SceneObject sceneObject = AssetCatalogBridge.i.GetSceneObjectById(model.assetId);
 167
 168                    if (sceneObject == null)
 169                    {
 170#if UNITY_EDITOR
 171                        Debug.LogWarning($"LoadableShape '{model.assetId}' not found in catalog, probably asset pack del
 172#endif
 173                        failed = true;
 174                        OnLoadFailed(null, new Exception($"LoadableShape '{model.assetId}' not found in catalog, probabl
 175                        return;
 176                    }
 177
 178                    model.src = sceneObject.model;
 179                }
 180            }
 181
 182            if (provider == null)
 183                provider = scene.contentProvider;
 184
 185            if (provider.HasContentsUrl(model.src))
 186            {
 187                isLoaded = false;
 188                entity.EnsureMeshGameObject(componentName + " mesh");
 189
 190                LoadWrapperType loadableShape =
 191                    Environment.i.world.state.GetOrAddLoaderForEntity<LoadWrapperType>(entity);
 192
 193                if (loadableShape is LoadWrapper_GLTF gltfLoadWrapper)
 194                    gltfLoadWrapper.customContentProvider = provider;
 195
 196                entity.meshesInfo.currentShape = this;
 197
 198                loadableShape.entity = entity;
 199
 200                bool initialVisibility = model.visible;
 201
 202                if (!DataStore.i.debugConfig.isDebugMode.Get())
 203                    initialVisibility &= entity.isInsideSceneBoundaries;
 204
 205                loadableShape.initialVisibility = initialVisibility;
 206
 207                loadableShape.Load(model.src, OnLoadCompleted, OnLoadFailed);
 208            }
 209            else
 210            {
 211                var message = $"LoadableShape '{model.src}' not found";
 212
 213                if (DataStore.i.debugConfig.isDebugMode.Get())
 214                {
 215                    Debug.LogError(message);
 216                }
 217                else
 218                {
 219#if UNITY_EDITOR
 220                    Debug.LogWarning(message);
 221#endif
 222                }
 223
 224                failed = true;
 225            }
 226        }
 227
 228        void ConfigureVisibility(IDCLEntity entity)
 229        {
 230            var loadable = Environment.i.world.state.GetLoaderForEntity(entity);
 231
 232            bool initialVisibility = model.visible;
 233
 234            if (!DataStore.i.debugConfig.isDebugMode.Get())
 235                initialVisibility &= entity.isInsideSceneBoundaries;
 236
 237            if (loadable != null)
 238                loadable.initialVisibility = initialVisibility;
 239
 240            ConfigureVisibility(entity.meshRootGameObject, initialVisibility, entity.meshesInfo.renderers);
 241
 242            UpdateAnimationStatus(entity, initialVisibility);
 243        }
 244
 245        private void UpdateAnimationStatus(IDCLEntity entity, bool enabled)
 246        {
 247            if (!scene.componentsManagerLegacy.HasComponent(entity, CLASS_ID_COMPONENT.ANIMATOR) &&
 248                entity.meshesInfo.animation != null)
 249                entity.meshesInfo.animation.enabled = enabled;
 250        }
 251
 252        protected virtual void ConfigureColliders(IDCLEntity entity)
 253        {
 254            CollidersManager.i.ConfigureColliders(entity.meshRootGameObject, model.withCollisions && entity.isInsideScen
 255        }
 256
 257        protected void OnLoadFailed(LoadWrapper loadWrapper, Exception exception)
 258        {
 259            if (loadWrapper != null)
 260                CleanFailedWrapper(loadWrapper);
 261
 262            failed = true;
 263            OnFinishCallbacks?.Invoke(this);
 264            OnFinishCallbacks = null;
 265        }
 266
 267        private void CleanFailedWrapper(LoadWrapper loadWrapper)
 268        {
 269            if (loadWrapper == null)
 270                return;
 271
 272            if (loadWrapper.entity == null)
 273                return;
 274
 275            if (loadWrapper.entity.gameObject == null)
 276                return;
 277
 278            GameObject go = loadWrapper.entity.gameObject;
 279
 280            go.name += " - Failed loading";
 281        }
 282
 283        protected void OnLoadCompleted(LoadWrapper loadWrapper)
 284        {
 285            IDCLEntity entity = loadWrapper.entity;
 286
 287            if (entity.meshesInfo.currentShape == null)
 288            {
 289                OnLoadFailed(loadWrapper, new Exception($"Entity {entity.entityId} current shape of mesh information is 
 290                return;
 291            }
 292
 293            isLoaded = true;
 294            OnLoaded?.Invoke(this);
 295
 296            entity.meshesInfo.meshRootGameObject = entity.meshRootGameObject;
 297            entity.OnInsideBoundariesChanged += OnInsideBoundariesChanged;
 298
 299            ConfigureVisibility(entity);
 300            ConfigureColliders(entity);
 301            RaiseOnShapeUpdated(entity);
 302            RaiseOnShapeLoaded(entity);
 303
 304            OnFinishCallbacks?.Invoke(this);
 305            OnFinishCallbacks = null;
 306        }
 307
 308        private void OnInsideBoundariesChanged(IDCLEntity entity, bool isInsideBoundaries)
 309        {
 310            UpdateAnimationStatus(entity, isInsideBoundaries);
 311        }
 312
 313        protected virtual void DetachShape(IDCLEntity entity)
 314        {
 315            if (entity == null || entity.meshRootGameObject == null)
 316                return;
 317
 318            LoadWrapper loadWrapper = Environment.i.world.state.GetLoaderForEntity(entity);
 319
 320            if (loadWrapper != null)
 321            {
 322                entity.OnInsideBoundariesChanged -= OnInsideBoundariesChanged;
 323                loadWrapper.Unload();
 324            }
 325
 326            Environment.i.world.state.RemoveLoaderForEntity(entity);
 327            entity.meshesInfo.CleanReferences();
 328        }
 329
 330        public override void CallWhenReady(Action<ISharedComponent> callback)
 331        {
 332            if (attachedEntities.Count == 0 || isLoaded || failed) { callback.Invoke(this); }
 333            else { OnFinishCallbacks += callback; }
 334        }
 335
 336        private void RaiseOnShapeUpdated(IDCLEntity entity)
 337        {
 338            if (!isLoaded)
 339                return;
 340
 341            entity.OnShapeUpdated?.Invoke(entity);
 342        }
 343
 344        private void RaiseOnShapeLoaded(IDCLEntity entity)
 345        {
 346            if (!isLoaded)
 347                return;
 348
 349            entity.OnShapeLoaded?.Invoke(entity);
 350        }
 351    }
 352}