< Summary

Class:AvatarModifierArea
Assembly:AvatarShape
File(s):/tmp/workspace/unity-renderer/unity-renderer/Assets/Scripts/MainScripts/DCL/Components/Avatar/AvatarModifiers/AvatarModifierArea.cs
Covered lines:94
Uncovered lines:16
Coverable lines:110
Total lines:274
Line coverage:85.4% (94 of 110)
Covered branches:0
Total branches:0
Covered methods:18
Total methods:20
Method coverage:90% (18 of 20)

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity NPath complexity Sequence coverage
Model()0%110100%
GetDataFromJSON(...)0%110100%
GetDataFromPb(...)0%30500%
AvatarModifierArea()0%110100%
ApplyChanges(...)0%110100%
Awake()0%110100%
OnDestroy()0%3.013090.91%
Update()0%13.0213094.74%
AreSetEquals(...)0%550100%
DetectAllAvatarsInArea()0%550100%
RemoveAllModifiers()0%110100%
RemoveAllModifiers(...)0%7.17087.5%
ApplyCurrentModel()0%660100%
GetClassId()0%110100%
GetExcludedColliders(...)0%990100%
OtherPlayersOnOnAdded(...)0%110100%
OwnPlayerOnOnChange(...)0%2100%
OnOtherPlayersRemoved(...)0%2.52050%
RefreshExclusionList(...)0%8.817066.67%

File(s)

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

#LineLine coverage
 1using System;
 2using System.Collections;
 3using System.Collections.Generic;
 4using System.Linq;
 5using DCL;
 6using DCL.Components;
 7using DCL.Helpers;
 8using DCL.Models;
 9using UnityEngine;
 10using Decentraland.Sdk.Ecs6;
 11using MainScripts.DCL.Components;
 12
 13public class AvatarModifierArea : BaseComponent
 14{
 15    [Serializable]
 16    public class Model : BaseModel
 17    {
 18        // TODO: Change to TriggerArea and handle deserialization with subclasses
 4919        public BoxTriggerArea area = new ();
 20        public string[] modifiers;
 21        public string[] excludeIds;
 22
 23        public override BaseModel GetDataFromJSON(string json) =>
 2024            Utils.SafeFromJson<Model>(json);
 25
 26        public override BaseModel GetDataFromPb(ComponentBodyPayload pbModel)
 27        {
 028            if (pbModel.PayloadCase != ComponentBodyPayload.PayloadOneofCase.AvatarModifierArea)
 029                return Utils.SafeUnimplemented<AvatarModifierArea, Model>(expected: ComponentBodyPayload.PayloadOneofCas
 30
 031            var pb = new Model();
 32
 033            if (pbModel.AvatarModifierArea.Modifiers.Count != 0) pb.modifiers = pbModel.AvatarModifierArea.Modifiers.ToA
 034            if (pbModel.AvatarModifierArea.ExcludeIds.Count != 0) pb.excludeIds = pbModel.AvatarModifierArea.ExcludeIds.
 035            if (pbModel.AvatarModifierArea.Area.Box != null) pb.area.box = pbModel.AvatarModifierArea.Area.Box.AsUnityVe
 36
 037            return pb;
 38        }
 39    }
 40
 1141    private Model cachedModel = new ();
 42
 1143    private HashSet<GameObject> avatarsInArea = new ();
 44    private event Action<GameObject> OnAvatarEnter;
 45    private event Action<GameObject> OnAvatarExit;
 46
 47    internal readonly Dictionary<string, IAvatarModifier> modifiers;
 48
 49    private HashSet<Collider> excludedColliders;
 550    public override string componentName => "avatarModifierArea";
 51
 1152    public AvatarModifierArea()
 53    {
 54        // Configure all available modifiers
 1155        this.modifiers = new Dictionary<string, IAvatarModifier>()
 56        {
 57            { "HIDE_AVATARS", new HideAvatarsModifier() },
 58            { "DISABLE_PASSPORTS", new HidePassportModifier() }
 59        };
 1160        model = new Model();
 1161    }
 62
 63    public override IEnumerator ApplyChanges(BaseModel newModel)
 64    {
 65
 66        // Clean up
 2067        RemoveAllModifiers();
 2068        OnAvatarEnter = null;
 2069        OnAvatarExit = null;
 70
 2071        ApplyCurrentModel();
 72
 2073        return null;
 74    }
 75
 76    private void Awake()
 77    {
 878        DataStore.i.player.otherPlayers.OnRemoved += OnOtherPlayersRemoved;
 879    }
 80
 81    private void OnDestroy()
 82    {
 883        var toRemove = new HashSet<GameObject>();
 884        if (avatarsInArea != null)
 685            toRemove.UnionWith(avatarsInArea);
 86
 887        var currentInArea = DetectAllAvatarsInArea();
 888        if (currentInArea != null)
 089            toRemove.UnionWith(currentInArea);
 90
 891        RemoveAllModifiers(toRemove);
 92
 893        DataStore.i.player.ownPlayer.OnChange -= OwnPlayerOnOnChange;
 894        DataStore.i.player.otherPlayers.OnAdded -= OtherPlayersOnOnAdded;
 895        DataStore.i.player.otherPlayers.OnRemoved -= OnOtherPlayersRemoved;
 896    }
 97
 98    private void Update()
 99    {
 78100        if (cachedModel?.area == null)
 101        {
 0102            return;
 103        }
 104
 105        // Find avatars currently on the area
 78106        HashSet<GameObject> newAvatarsInArea = DetectAllAvatarsInArea();
 78107        if (AreSetEquals(avatarsInArea, newAvatarsInArea))
 60108            return;
 109
 18110        if (avatarsInArea == null)
 10111            avatarsInArea = new HashSet<GameObject>();
 112
 18113        if (newAvatarsInArea == null)
 8114            newAvatarsInArea = new HashSet<GameObject>();
 115
 116        // Call event for avatars that just entered the area
 68117        foreach (GameObject avatarThatEntered in newAvatarsInArea.Except(avatarsInArea))
 118        {
 16119            OnAvatarEnter?.Invoke(avatarThatEntered);
 120        }
 121
 122        // Call events for avatars that just exited the area
 44123        foreach (GameObject avatarThatExited in avatarsInArea.Except(newAvatarsInArea))
 124        {
 4125            OnAvatarExit?.Invoke(avatarThatExited);
 126        }
 127
 18128        avatarsInArea = newAvatarsInArea;
 18129    }
 130
 131    private bool AreSetEquals(HashSet<GameObject> set1, HashSet<GameObject> set2)
 132    {
 78133        if (set1 == null && set2 == null)
 46134            return true;
 135
 32136        if (set1 == null || set2 == null)
 18137            return false;
 138
 14139        return set1.SetEquals(set2);
 140    }
 141
 142    private HashSet<GameObject> DetectAllAvatarsInArea()
 143    {
 106144        if (entity?.gameObject == null)
 145        {
 5146            return null;
 147        }
 148
 101149        Vector3 center = entity.gameObject.transform.position;
 101150        Quaternion rotation = entity.gameObject.transform.rotation;
 101151        return cachedModel.area?.DetectAvatars(center, rotation, excludedColliders);
 152    }
 153
 154    private void RemoveAllModifiers()
 155    {
 20156        RemoveAllModifiers(DetectAllAvatarsInArea());
 20157        avatarsInArea = null;
 20158    }
 159
 160    private void RemoveAllModifiers(HashSet<GameObject> avatars)
 161    {
 28162        if (cachedModel?.area == null)
 163        {
 0164            return;
 165        }
 166
 28167        if (avatars != null)
 168        {
 56169            foreach (GameObject avatar in avatars)
 170            {
 15171                OnAvatarExit?.Invoke(avatar);
 172            }
 173        }
 28174    }
 175
 176    private void ApplyCurrentModel()
 177    {
 20178        DataStore.i.player.ownPlayer.OnChange -= OwnPlayerOnOnChange;
 20179        DataStore.i.player.otherPlayers.OnAdded -= OtherPlayersOnOnAdded;
 180
 20181        cachedModel = (Model)this.model;
 20182        if (cachedModel.modifiers != null)
 183        {
 184            // Add all listeners
 62185            foreach (string modifierKey in cachedModel.modifiers)
 186            {
 12187                if (!modifiers.TryGetValue(modifierKey, out IAvatarModifier modifier))
 188                    continue;
 189
 12190                OnAvatarEnter += modifier.ApplyModifier;
 12191                OnAvatarExit += modifier.RemoveModifier;
 192            }
 193
 194            // Set excluded colliders
 19195            excludedColliders = GetExcludedColliders(cachedModel);
 196
 19197            if (cachedModel.excludeIds != null && cachedModel.excludeIds.Length > 0)
 198            {
 3199                DataStore.i.player.ownPlayer.OnChange += OwnPlayerOnOnChange;
 3200                DataStore.i.player.otherPlayers.OnAdded += OtherPlayersOnOnAdded;
 201            }
 202
 203            // Force update due to after model update modifiers are removed and re-added
 204            // leaving a frame with the avatar without the proper modifications
 19205            Update();
 206        }
 20207    }
 208
 209    public override int GetClassId()
 210    {
 1211        return (int)CLASS_ID_COMPONENT.AVATAR_MODIFIER_AREA;
 212    }
 213
 214    private static HashSet<Collider> GetExcludedColliders(in Model componentModel)
 215    {
 19216        string[] excludeIds = componentModel?.excludeIds;
 19217        if (excludeIds == null || excludeIds.Length == 0)
 218        {
 16219            return null;
 220        }
 221
 3222        var ownPlayer = DataStore.i.player.ownPlayer.Get();
 3223        var otherPlayers = DataStore.i.player.otherPlayers;
 224
 3225        HashSet<Collider> result = new HashSet<Collider>();
 14226        for (int i = 0; i < excludeIds.Length; i++)
 227        {
 4228            if (ownPlayer != null && excludeIds[i] == ownPlayer.id)
 229            {
 1230                result.Add(ownPlayer.collider);
 231            }
 3232            else if (otherPlayers.TryGetValue(excludeIds[i], out Player player))
 233            {
 2234                result.Add(player.collider);
 235            }
 236        }
 3237        return result;
 238    }
 239
 240    private void OtherPlayersOnOnAdded(string id, Player player)
 241    {
 1242        RefreshExclusionList(player);
 1243    }
 244
 245    private void OwnPlayerOnOnChange(Player current, Player previous)
 246    {
 0247        RefreshExclusionList(current);
 0248    }
 249
 250    private void OnOtherPlayersRemoved(string id, Player player)
 251    {
 3252        excludedColliders?.Remove(player.collider);
 0253    }
 254
 255    private void RefreshExclusionList(Player player)
 256    {
 1257        string[] excludeIds = cachedModel?.excludeIds;
 1258        if (excludeIds == null || excludeIds.Length == 0)
 259        {
 0260            return;
 261        }
 262
 1263        if (!excludeIds.Contains(player.id))
 264        {
 0265            return;
 266        }
 267
 1268        if (excludedColliders == null)
 269        {
 0270            excludedColliders = new HashSet<Collider>();
 271        }
 1272        excludedColliders.Add(player.collider);
 1273    }
 274}