< 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:8
Coverable lines:102
Total lines:260
Line coverage:92.1% (94 of 102)
Covered branches:0
Total branches:0

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity NPath complexity Sequence coverage
GetDataFromJSON(...)0%110100%
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%770100%
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;
 10
 11public class AvatarModifierArea : BaseComponent
 12{
 13    [Serializable]
 14    public class Model : BaseModel
 15    {
 16        // TODO: Change to TriggerArea and handle deserialization with subclasses
 17        public BoxTriggerArea area;
 18        public string[] modifiers;
 19        public string[] excludeIds;
 20
 21        public override BaseModel GetDataFromJSON(string json)
 22        {
 2023            return Utils.SafeFromJson<Model>(json);
 24        }
 25    }
 26
 1127    private Model cachedModel = new Model();
 28
 1129    private HashSet<GameObject> avatarsInArea = new HashSet<GameObject>();
 30    private event Action<GameObject> OnAvatarEnter;
 31    private event Action<GameObject> OnAvatarExit;
 32
 33    internal readonly Dictionary<string, IAvatarModifier> modifiers;
 34
 35    private HashSet<Collider> excludedColliders;
 536    public override string componentName => "avatarModifierArea";
 37
 1138    public AvatarModifierArea()
 39    {
 40        // Configure all available modifiers
 1141        this.modifiers = new Dictionary<string, IAvatarModifier>()
 42        {
 43            { "HIDE_AVATARS", new HideAvatarsModifier() },
 44            { "DISABLE_PASSPORTS", new HidePassportModifier() }
 45        };
 1146        model = new Model();
 1147    }
 48
 49    public override IEnumerator ApplyChanges(BaseModel newModel)
 50    {
 51
 52        // Clean up
 2053        RemoveAllModifiers();
 2054        OnAvatarEnter = null;
 2055        OnAvatarExit = null;
 56
 2057        ApplyCurrentModel();
 58
 2059        return null;
 60    }
 61
 62    private void Awake()
 63    {
 864        DataStore.i.player.otherPlayers.OnRemoved += OnOtherPlayersRemoved;
 865    }
 66
 67    private void OnDestroy()
 68    {
 869        var toRemove = new HashSet<GameObject>();
 870        if (avatarsInArea != null)
 671            toRemove.UnionWith(avatarsInArea);
 72
 873        var currentInArea = DetectAllAvatarsInArea();
 874        if (currentInArea != null)
 075            toRemove.UnionWith(currentInArea);
 76
 877        RemoveAllModifiers(toRemove);
 78
 879        DataStore.i.player.ownPlayer.OnChange -= OwnPlayerOnOnChange;
 880        DataStore.i.player.otherPlayers.OnAdded -= OtherPlayersOnOnAdded;
 881        DataStore.i.player.otherPlayers.OnRemoved -= OnOtherPlayersRemoved;
 882    }
 83
 84    private void Update()
 85    {
 7886        if (cachedModel?.area == null)
 87        {
 088            return;
 89        }
 90
 91        // Find avatars currently on the area
 7892        HashSet<GameObject> newAvatarsInArea = DetectAllAvatarsInArea();
 7893        if (AreSetEquals(avatarsInArea, newAvatarsInArea))
 6094            return;
 95
 1896        if (avatarsInArea == null)
 1097            avatarsInArea = new HashSet<GameObject>();
 98
 1899        if (newAvatarsInArea == null)
 8100            newAvatarsInArea = new HashSet<GameObject>();
 101
 102        // Call event for avatars that just entered the area
 68103        foreach (GameObject avatarThatEntered in newAvatarsInArea.Except(avatarsInArea))
 104        {
 16105            OnAvatarEnter?.Invoke(avatarThatEntered);
 106        }
 107
 108        // Call events for avatars that just exited the area
 44109        foreach (GameObject avatarThatExited in avatarsInArea.Except(newAvatarsInArea))
 110        {
 4111            OnAvatarExit?.Invoke(avatarThatExited);
 112        }
 113
 18114        avatarsInArea = newAvatarsInArea;
 18115    }
 116
 117    private bool AreSetEquals(HashSet<GameObject> set1, HashSet<GameObject> set2)
 118    {
 78119        if (set1 == null && set2 == null)
 46120            return true;
 121
 32122        if (set1 == null || set2 == null)
 18123            return false;
 124
 14125        return set1.SetEquals(set2);
 126    }
 127
 128    private HashSet<GameObject> DetectAllAvatarsInArea()
 129    {
 106130        if (entity?.gameObject == null)
 131        {
 5132            return null;
 133        }
 134
 101135        Vector3 center = entity.gameObject.transform.position;
 101136        Quaternion rotation = entity.gameObject.transform.rotation;
 101137        return cachedModel.area?.DetectAvatars(center, rotation, excludedColliders);
 138    }
 139
 140    private void RemoveAllModifiers()
 141    {
 20142        RemoveAllModifiers(DetectAllAvatarsInArea());
 20143        avatarsInArea = null;
 20144    }
 145
 146    private void RemoveAllModifiers(HashSet<GameObject> avatars)
 147    {
 28148        if (cachedModel?.area == null)
 149        {
 8150            return;
 151        }
 152
 20153        if (avatars != null)
 154        {
 56155            foreach (GameObject avatar in avatars)
 156            {
 15157                OnAvatarExit?.Invoke(avatar);
 158            }
 159        }
 20160    }
 161
 162    private void ApplyCurrentModel()
 163    {
 20164        DataStore.i.player.ownPlayer.OnChange -= OwnPlayerOnOnChange;
 20165        DataStore.i.player.otherPlayers.OnAdded -= OtherPlayersOnOnAdded;
 166
 20167        cachedModel = (Model)this.model;
 20168        if (cachedModel.modifiers != null)
 169        {
 170            // Add all listeners
 62171            foreach (string modifierKey in cachedModel.modifiers)
 172            {
 12173                if (!modifiers.TryGetValue(modifierKey, out IAvatarModifier modifier))
 174                    continue;
 175
 12176                OnAvatarEnter += modifier.ApplyModifier;
 12177                OnAvatarExit += modifier.RemoveModifier;
 178            }
 179
 180            // Set excluded colliders
 19181            excludedColliders = GetExcludedColliders(cachedModel);
 182
 19183            if (cachedModel.excludeIds != null && cachedModel.excludeIds.Length > 0)
 184            {
 3185                DataStore.i.player.ownPlayer.OnChange += OwnPlayerOnOnChange;
 3186                DataStore.i.player.otherPlayers.OnAdded += OtherPlayersOnOnAdded;
 187            }
 188
 189            // Force update due to after model update modifiers are removed and re-added
 190            // leaving a frame with the avatar without the proper modifications
 19191            Update();
 192        }
 20193    }
 194
 195    public override int GetClassId()
 196    {
 1197        return (int)CLASS_ID_COMPONENT.AVATAR_MODIFIER_AREA;
 198    }
 199
 200    private static HashSet<Collider> GetExcludedColliders(in Model componentModel)
 201    {
 19202        string[] excludeIds = componentModel?.excludeIds;
 19203        if (excludeIds == null || excludeIds.Length == 0)
 204        {
 16205            return null;
 206        }
 207
 3208        var ownPlayer = DataStore.i.player.ownPlayer.Get();
 3209        var otherPlayers = DataStore.i.player.otherPlayers;
 210
 3211        HashSet<Collider> result = new HashSet<Collider>();
 14212        for (int i = 0; i < excludeIds.Length; i++)
 213        {
 4214            if (ownPlayer != null && excludeIds[i] == ownPlayer.id)
 215            {
 1216                result.Add(ownPlayer.collider);
 217            }
 3218            else if (otherPlayers.TryGetValue(excludeIds[i], out Player player))
 219            {
 2220                result.Add(player.collider);
 221            }
 222        }
 3223        return result;
 224    }
 225
 226    private void OtherPlayersOnOnAdded(string id, Player player)
 227    {
 1228        RefreshExclusionList(player);
 1229    }
 230
 231    private void OwnPlayerOnOnChange(Player current, Player previous)
 232    {
 0233        RefreshExclusionList(current);
 0234    }
 235
 236    private void OnOtherPlayersRemoved(string id, Player player)
 237    {
 3238        excludedColliders?.Remove(player.collider);
 0239    }
 240
 241    private void RefreshExclusionList(Player player)
 242    {
 1243        string[] excludeIds = cachedModel?.excludeIds;
 1244        if (excludeIds == null || excludeIds.Length == 0)
 245        {
 0246            return;
 247        }
 248
 1249        if (!excludeIds.Contains(player.id))
 250        {
 0251            return;
 252        }
 253
 1254        if (excludedColliders == null)
 255        {
 0256            excludedColliders = new HashSet<Collider>();
 257        }
 1258        excludedColliders.Add(player.collider);
 1259    }
 260}