| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272 |
- using System.Linq;
- using Content.Server.Popups;
- using Content.Shared.Access;
- using Content.Shared.Access.Components;
- using Content.Shared.Access.Systems;
- using Content.Shared.Administration.Logs;
- using Content.Shared.Database;
- using Content.Shared.DoAfter;
- using Content.Shared.Interaction;
- using JetBrains.Annotations;
- using Robust.Server.GameObjects;
- using Robust.Shared.Audio;
- using Robust.Shared.Audio.Systems;
- using Robust.Shared.Containers;
- using Robust.Shared.Player;
- using Robust.Shared.Prototypes;
- using static Content.Shared.Access.Components.AccessOverriderComponent;
- namespace Content.Server.Access.Systems;
- [UsedImplicitly]
- public sealed class AccessOverriderSystem : SharedAccessOverriderSystem
- {
- [Dependency] private readonly UserInterfaceSystem _userInterface = default!;
- [Dependency] private readonly AccessReaderSystem _accessReader = default!;
- [Dependency] private readonly ISharedAdminLogManager _adminLogger = default!;
- [Dependency] private readonly SharedInteractionSystem _interactionSystem = default!;
- [Dependency] private readonly PopupSystem _popupSystem = default!;
- [Dependency] private readonly SharedAudioSystem _audioSystem = default!;
- [Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!;
- public override void Initialize()
- {
- base.Initialize();
- SubscribeLocalEvent<AccessOverriderComponent, ComponentStartup>(UpdateUserInterface);
- SubscribeLocalEvent<AccessOverriderComponent, EntInsertedIntoContainerMessage>(UpdateUserInterface);
- SubscribeLocalEvent<AccessOverriderComponent, EntRemovedFromContainerMessage>(UpdateUserInterface);
- SubscribeLocalEvent<AccessOverriderComponent, AfterInteractEvent>(AfterInteractOn);
- SubscribeLocalEvent<AccessOverriderComponent, AccessOverriderDoAfterEvent>(OnDoAfter);
- Subs.BuiEvents<AccessOverriderComponent>(AccessOverriderUiKey.Key, subs =>
- {
- subs.Event<BoundUIOpenedEvent>(UpdateUserInterface);
- subs.Event<BoundUIClosedEvent>(OnClose);
- subs.Event<WriteToTargetAccessReaderIdMessage>(OnWriteToTargetAccessReaderIdMessage);
- });
- }
- private void AfterInteractOn(EntityUid uid, AccessOverriderComponent component, AfterInteractEvent args)
- {
- if (args.Target == null || !TryComp(args.Target, out AccessReaderComponent? accessReader))
- return;
- if (!_interactionSystem.InRangeUnobstructed(args.User, (EntityUid) args.Target))
- return;
- var doAfterEventArgs = new DoAfterArgs(EntityManager, args.User, component.DoAfter, new AccessOverriderDoAfterEvent(), uid, target: args.Target, used: uid)
- {
- BreakOnMove = true,
- BreakOnDamage = true,
- NeedHand = true,
- };
- _doAfterSystem.TryStartDoAfter(doAfterEventArgs);
- }
- private void OnDoAfter(EntityUid uid, AccessOverriderComponent component, AccessOverriderDoAfterEvent args)
- {
- if (args.Handled || args.Cancelled)
- return;
- if (args.Args.Target != null)
- {
- component.TargetAccessReaderId = args.Args.Target.Value;
- _userInterface.OpenUi(uid, AccessOverriderUiKey.Key, args.User);
- UpdateUserInterface(uid, component, args);
- }
- args.Handled = true;
- }
- private void OnClose(EntityUid uid, AccessOverriderComponent component, BoundUIClosedEvent args)
- {
- if (args.UiKey.Equals(AccessOverriderUiKey.Key))
- {
- component.TargetAccessReaderId = new();
- }
- }
- private void OnWriteToTargetAccessReaderIdMessage(EntityUid uid, AccessOverriderComponent component, WriteToTargetAccessReaderIdMessage args)
- {
- if (args.Actor is not { Valid: true } player)
- return;
- TryWriteToTargetAccessReaderId(uid, args.AccessList, player, component);
- UpdateUserInterface(uid, component, args);
- }
- private void UpdateUserInterface(EntityUid uid, AccessOverriderComponent component, EntityEventArgs args)
- {
- if (!component.Initialized)
- return;
- var privilegedIdName = string.Empty;
- var targetLabel = Loc.GetString("access-overrider-window-no-target");
- var targetLabelColor = Color.Red;
- ProtoId<AccessLevelPrototype>[]? possibleAccess = null;
- ProtoId<AccessLevelPrototype>[]? currentAccess = null;
- ProtoId<AccessLevelPrototype>[]? missingAccess = null;
- if (component.TargetAccessReaderId is { Valid: true } accessReader)
- {
- targetLabel = Loc.GetString("access-overrider-window-target-label") + " " + EntityManager.GetComponent<MetaDataComponent>(component.TargetAccessReaderId).EntityName;
- targetLabelColor = Color.White;
- if (!_accessReader.GetMainAccessReader(accessReader, out var accessReaderEnt))
- return;
- var currentAccessHashsets = accessReaderEnt.Value.Comp.AccessLists;
- currentAccess = ConvertAccessHashSetsToList(currentAccessHashsets).ToArray();
- }
- if (component.PrivilegedIdSlot.Item is { Valid: true } idCard)
- {
- privilegedIdName = EntityManager.GetComponent<MetaDataComponent>(idCard).EntityName;
- if (component.TargetAccessReaderId is { Valid: true })
- {
- possibleAccess = _accessReader.FindAccessTags(idCard).ToArray();
- }
- if (currentAccess != null && possibleAccess != null)
- {
- missingAccess = currentAccess.Except(possibleAccess).ToArray();
- }
- }
- AccessOverriderBoundUserInterfaceState newState;
- newState = new AccessOverriderBoundUserInterfaceState(
- component.PrivilegedIdSlot.HasItem,
- PrivilegedIdIsAuthorized(uid, component),
- currentAccess,
- possibleAccess,
- missingAccess,
- privilegedIdName,
- targetLabel,
- targetLabelColor);
- _userInterface.SetUiState(uid, AccessOverriderUiKey.Key, newState);
- }
- private List<ProtoId<AccessLevelPrototype>> ConvertAccessHashSetsToList(List<HashSet<ProtoId<AccessLevelPrototype>>> accessHashsets)
- {
- var accessList = new List<ProtoId<AccessLevelPrototype>>();
- if (accessHashsets.Count <= 0)
- return accessList;
- foreach (var hashSet in accessHashsets)
- {
- accessList.AddRange(hashSet);
- }
- return accessList;
- }
- private List<HashSet<ProtoId<AccessLevelPrototype>>> ConvertAccessListToHashSet(List<ProtoId<AccessLevelPrototype>> accessList)
- {
- List<HashSet<ProtoId<AccessLevelPrototype>>> accessHashsets = new List<HashSet<ProtoId<AccessLevelPrototype>>>();
- if (accessList != null && accessList.Any())
- {
- foreach (ProtoId<AccessLevelPrototype> access in accessList)
- {
- accessHashsets.Add(new HashSet<ProtoId<AccessLevelPrototype>>() { access });
- }
- }
- return accessHashsets;
- }
- /// <summary>
- /// Called whenever an access button is pressed, adding or removing that access requirement from the target access reader.
- /// </summary>
- private void TryWriteToTargetAccessReaderId(EntityUid uid,
- List<ProtoId<AccessLevelPrototype>> newAccessList,
- EntityUid player,
- AccessOverriderComponent? component = null)
- {
- if (!Resolve(uid, ref component) || component.TargetAccessReaderId is not { Valid: true })
- return;
- if (!PrivilegedIdIsAuthorized(uid, component))
- return;
- if (!_interactionSystem.InRangeUnobstructed(uid, component.TargetAccessReaderId))
- {
- _popupSystem.PopupEntity(Loc.GetString("access-overrider-out-of-range"), player, player);
- return;
- }
- if (newAccessList.Count > 0 && !newAccessList.TrueForAll(x => component.AccessLevels.Contains(x)))
- {
- _sawmill.Warning($"User {ToPrettyString(uid)} tried to write unknown access tag.");
- return;
- }
- if (!_accessReader.GetMainAccessReader(component.TargetAccessReaderId, out var accessReaderEnt))
- return;
- var oldTags = ConvertAccessHashSetsToList(accessReaderEnt.Value.Comp.AccessLists);
- var privilegedId = component.PrivilegedIdSlot.Item;
- if (oldTags.SequenceEqual(newAccessList))
- return;
- var difference = newAccessList.Union(oldTags).Except(newAccessList.Intersect(oldTags)).ToHashSet();
- var privilegedPerms = _accessReader.FindAccessTags(privilegedId!.Value).ToHashSet();
- if (!difference.IsSubsetOf(privilegedPerms))
- {
- _sawmill.Warning($"User {ToPrettyString(uid)} tried to modify permissions they could not give/take!");
- return;
- }
- if (!oldTags.ToHashSet().IsSubsetOf(privilegedPerms))
- {
- _sawmill.Warning($"User {ToPrettyString(uid)} tried to modify permissions when they do not have sufficient access!");
- _popupSystem.PopupEntity(Loc.GetString("access-overrider-cannot-modify-access"), player, player);
- _audioSystem.PlayPvs(component.DenialSound, uid);
- return;
- }
- var addedTags = newAccessList.Except(oldTags).Select(tag => "+" + tag).ToList();
- var removedTags = oldTags.Except(newAccessList).Select(tag => "-" + tag).ToList();
- _adminLogger.Add(LogType.Action, LogImpact.Medium,
- $"{ToPrettyString(player):player} has modified {ToPrettyString(accessReaderEnt.Value):entity} with the following allowed access level holders: [{string.Join(", ", addedTags.Union(removedTags))}] [{string.Join(", ", newAccessList)}]");
- accessReaderEnt.Value.Comp.AccessLists = ConvertAccessListToHashSet(newAccessList);
- var ev = new OnAccessOverriderAccessUpdatedEvent(player);
- RaiseLocalEvent(component.TargetAccessReaderId, ref ev);
- Dirty(accessReaderEnt.Value);
- }
- /// <summary>
- /// Returns true if there is an ID in <see cref="AccessOverriderComponent.PrivilegedIdSlot"/> and said ID satisfies the requirements of <see cref="AccessReaderComponent"/>.
- /// </summary>
- /// <remarks>
- /// Other code relies on the fact this returns false if privileged Id is null. Don't break that invariant.
- /// </remarks>
- private bool PrivilegedIdIsAuthorized(EntityUid uid, AccessOverriderComponent? component = null)
- {
- if (!Resolve(uid, ref component))
- return true;
- if (_accessReader.GetMainAccessReader(uid, out var accessReader))
- return true;
- var privilegedId = component.PrivilegedIdSlot.Item;
- return privilegedId != null && _accessReader.IsAllowed(privilegedId.Value, uid, accessReader);
- }
- }
|