#nullable enable using System; using System.Collections.Generic; using System.Linq; using Content.Server.GameObjects.Components.GUI; using Content.Server.GameObjects.Components.Items.Storage; using Content.Server.Interfaces; using Content.Server.Interfaces.GameObjects.Components.Items; using Content.Shared.GameObjects.Components.Inventory; using JetBrains.Annotations; using Robust.Shared.GameObjects; using Robust.Shared.Interfaces.GameObjects; using Robust.Shared.Serialization; using Robust.Shared.ViewVariables; namespace Content.Server.GameObjects.Components.Access { /// /// Stores access levels necessary to "use" an entity /// and allows checking if something or somebody is authorized with these access levels. /// [PublicAPI] [RegisterComponent] public class AccessReader : Component { public override string Name => "AccessReader"; private readonly List> _accessLists = new(); private readonly HashSet _denyTags = new(); /// /// List of access lists to check allowed against. For an access check to pass /// there has to be an access list that is a subset of the access in the checking list. /// [ViewVariables] public IList> AccessLists => _accessLists; /// /// The set of tags that will automatically deny an allowed check, if any of them are present. /// [ViewVariables] public ISet DenyTags => _denyTags; /// /// Searches an in the entity itself, in its active hand or in its ID slot. /// Then compares the found access with the configured access lists to see if it is allowed. /// /// /// If no access is found, an empty set is used instead. /// /// The entity to be searched for access. public bool IsAllowed(IEntity entity) { var tags = FindAccessTags(entity); return IsAllowed(tags); } public bool IsAllowed(IAccess access) { return IsAllowed(access.Tags); } public bool IsAllowed(ICollection accessTags) { if (_denyTags.Overlaps(accessTags)) { // Sec owned by cargo. return false; } return _accessLists.Count == 0 || _accessLists.Any(a => a.IsSubsetOf(accessTags)); } public static ICollection FindAccessTags(IEntity entity) { if (entity.TryGetComponent(out IAccess? accessComponent)) { return accessComponent.Tags; } if (entity.TryGetComponent(out IHandsComponent? handsComponent)) { var activeHandEntity = handsComponent.GetActiveHand?.Owner; if (activeHandEntity != null && activeHandEntity.TryGetComponent(out IAccess? handAccessComponent)) { return handAccessComponent.Tags; } } else { return Array.Empty(); } if (entity.TryGetComponent(out InventoryComponent? inventoryComponent)) { if (inventoryComponent.HasSlot(EquipmentSlotDefines.Slots.IDCARD) && inventoryComponent.TryGetSlotItem(EquipmentSlotDefines.Slots.IDCARD, out ItemComponent item) && item.Owner.TryGetComponent(out IAccess? idAccessComponent) ) { return idAccessComponent.Tags; } } return Array.Empty(); } public override void ExposeData(ObjectSerializer serializer) { base.ExposeData(serializer); serializer.DataReadWriteFunction("access", new List>(), v => { if (v.Count != 0) { _accessLists.Clear(); _accessLists.AddRange(v.Select(a => new HashSet(a))); } }, () => _accessLists.Select(p => new List(p)).ToList()); } } }