Add door electronics access configuration menu (#17778)

* Add door electronics configuration menu

* Use file-scoped namespaces

Signed-off-by: c4llv07e <kseandi@gmail.com>

* Open door electronics configuration menu only with network configurator

Signed-off-by: c4llv07e <kseandi@gmail.com>

* Doors will now try to move their AccessReaderComponent to their door electronics when the map is initialized

Signed-off-by: c4llv07e <kseandi@gmail.com>

* Make the access list in the id card computer a separate control

Signed-off-by: c4llv07e <kseandi@gmail.com>

* Fix merge conflict

Signed-off-by: c4llv07e <kseandi@gmail.com>

* Remove DoorElectronics tag

Signed-off-by: c4llv07e <kseandi@gmail.com>

* Integrate doors with #17927

Signed-off-by: c4llv07e <kseandi@gmail.com>

* Move door electornics ui stuff to the right place

Signed-off-by: c4llv07e <kseandi@gmail.com>

* Some review fixes

Signed-off-by: c4llv07e <kseandi@gmail.com>

* More fixes

Signed-off-by: c4llv07e <kseandi@gmail.com>

* review fix

Signed-off-by: c4llv07e <kseandi@gmail.com>

* move all accesses from airlock prototypes to door electronics

Signed-off-by: c4llv07e <kseandi@gmail.com>

* rework door electronics config access list

Signed-off-by: c4llv07e <kseandi@gmail.com>

* Remove Linq from the door electronics user interface

* [WIP] Add EntityWhitelist to the activatable ui component

Signed-off-by: c4llv07e <kseandi@gmail.com>

* Better interaction system

Signed-off-by: c4llv07e <kseandi@gmail.com>

* Refactor

Signed-off-by: c4llv07e <kseandi@gmail.com>

* Fix some door electronics not working without AccessReaderComponent

Signed-off-by: c4llv07e <kseandi@gmail.com>

* Move AccessReaderComponent update code to the AccessReaderSystem

Signed-off-by: c4llv07e <kseandi@gmail.com>

* Remove unnecesary newlines in the door access prototypes

Signed-off-by: c4llv07e <kseandi@gmail.com>

* Remove unused variables in access level control

Signed-off-by: c4llv07e <kseandi@gmail.com>

* Remove unnecessary method from the door electronics configuration menu

Signed-off-by: c4llv07e <kseandi@gmail.com>

* [WIP] change access type from string to ProtoId<AccessLevelPrototype>

Signed-off-by: c4llv07e <kseandi@gmail.com>

* Remove unused methods

Signed-off-by: c4llv07e <kseandi@gmail.com>

* Newline fix

Signed-off-by: c4llv07e <kseandi@gmail.com>

* Restored to a functional state

Signed-off-by: c4llv07e <kseandi@gmail.com>

* Fix access configurator not working with door electronics AccessReaderComponent

Signed-off-by: c4llv07e <kseandi@gmail.com>

* Replace all string access fields with ProtoId

Signed-off-by: c4llv07e <kseandi@gmail.com>

* move access level control initialization into Populate method

Signed-off-by: c4llv07e <kseandi@gmail.com>

* Review

---------

Signed-off-by: c4llv07e <kseandi@gmail.com>
Co-authored-by: metalgearsloth <comedian_vs_clown@hotmail.com>
This commit is contained in:
c4llv07e
2024-04-01 09:06:13 +03:00
committed by GitHub
parent 72bdcac1e2
commit 64bb8dbdd5
40 changed files with 1079 additions and 532 deletions

View File

@@ -1,5 +1,6 @@
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;
@@ -12,6 +13,7 @@ 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;
@@ -26,6 +28,7 @@ public sealed class AccessOverriderSystem : SharedAccessOverriderSystem
[Dependency] private readonly PopupSystem _popupSystem = default!;
[Dependency] private readonly SharedAudioSystem _audioSystem = default!;
[Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!;
[Dependency] private readonly SharedContainerSystem _containerSystem = default!;
public override void Initialize()
{
@@ -108,17 +111,20 @@ public sealed class AccessOverriderSystem : SharedAccessOverriderSystem
var targetLabel = Loc.GetString("access-overrider-window-no-target");
var targetLabelColor = Color.Red;
string[]? possibleAccess = null;
string[]? currentAccess = null;
string[]? missingAccess = null;
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;
List<HashSet<string>> currentAccessHashsets = EntityManager.GetComponent<AccessReaderComponent>(accessReader).AccessLists;
currentAccess = ConvertAccessHashSetsToList(currentAccessHashsets)?.ToArray();
if (!_accessReader.GetMainAccessReader(accessReader, out var accessReaderComponent))
return;
var currentAccessHashsets = accessReaderComponent.AccessLists;
currentAccess = ConvertAccessHashSetsToList(currentAccessHashsets).ToArray();
}
if (component.PrivilegedIdSlot.Item is { Valid: true } idCard)
@@ -151,15 +157,15 @@ public sealed class AccessOverriderSystem : SharedAccessOverriderSystem
_userInterface.TrySetUiState(uid, AccessOverriderUiKey.Key, newState);
}
private List<string> ConvertAccessHashSetsToList(List<HashSet<string>> accessHashsets)
private List<ProtoId<AccessLevelPrototype>> ConvertAccessHashSetsToList(List<HashSet<ProtoId<AccessLevelPrototype>>> accessHashsets)
{
List<string> accessList = new List<string>();
List<ProtoId<AccessLevelPrototype>> accessList = new List<ProtoId<AccessLevelPrototype>>();
if (accessHashsets != null && accessHashsets.Any())
{
foreach (HashSet<string> hashSet in accessHashsets)
foreach (HashSet<ProtoId<AccessLevelPrototype>> hashSet in accessHashsets)
{
foreach (string hash in hashSet.ToArray())
foreach (ProtoId<AccessLevelPrototype> hash in hashSet.ToArray())
{
accessList.Add(hash);
}
@@ -169,15 +175,15 @@ public sealed class AccessOverriderSystem : SharedAccessOverriderSystem
return accessList;
}
private List<HashSet<string>> ConvertAccessListToHashSet(List<string> accessList)
private List<HashSet<ProtoId<AccessLevelPrototype>>> ConvertAccessListToHashSet(List<ProtoId<AccessLevelPrototype>> accessList)
{
List<HashSet<string>> accessHashsets = new List<HashSet<string>>();
List<HashSet<ProtoId<AccessLevelPrototype>>> accessHashsets = new List<HashSet<ProtoId<AccessLevelPrototype>>>();
if (accessList != null && accessList.Any())
{
foreach (string access in accessList)
foreach (ProtoId<AccessLevelPrototype> access in accessList)
{
accessHashsets.Add(new HashSet<string>() { access });
accessHashsets.Add(new HashSet<ProtoId<AccessLevelPrototype>>() { access });
}
}
@@ -188,7 +194,7 @@ public sealed class AccessOverriderSystem : SharedAccessOverriderSystem
/// 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<string> newAccessList,
List<ProtoId<AccessLevelPrototype>> newAccessList,
EntityUid player,
AccessOverriderComponent? component = null)
{
@@ -211,9 +217,7 @@ public sealed class AccessOverriderSystem : SharedAccessOverriderSystem
return;
}
TryComp(component.TargetAccessReaderId, out AccessReaderComponent? accessReader);
if (accessReader == null)
if (!_accessReader.GetMainAccessReader(component.TargetAccessReaderId, out var accessReader))
return;
var oldTags = ConvertAccessHashSetsToList(accessReader.AccessLists);
@@ -262,10 +266,10 @@ public sealed class AccessOverriderSystem : SharedAccessOverriderSystem
if (!Resolve(uid, ref component))
return true;
if (!EntityManager.TryGetComponent<AccessReaderComponent>(uid, out var reader))
if (_accessReader.GetMainAccessReader(uid, out var accessReader))
return true;
var privilegedId = component.PrivilegedIdSlot.Item;
return privilegedId != null && _accessReader.IsAllowed(privilegedId.Value, uid, reader);
return privilegedId != null && _accessReader.IsAllowed(privilegedId.Value, uid, accessReader);
}
}

View File

@@ -12,6 +12,7 @@ using Robust.Shared.Containers;
using Robust.Shared.Prototypes;
using System.Linq;
using static Content.Shared.Access.Components.IdCardConsoleComponent;
using Content.Shared.Access;
namespace Content.Server.Access.Systems;
@@ -54,11 +55,11 @@ public sealed class IdCardConsoleSystem : SharedIdCardConsoleSystem
return;
var privilegedIdName = string.Empty;
string[]? possibleAccess = null;
List<ProtoId<AccessLevelPrototype>>? possibleAccess = null;
if (component.PrivilegedIdSlot.Item is { Valid: true } item)
{
privilegedIdName = EntityManager.GetComponent<MetaDataComponent>(item).EntityName;
possibleAccess = _accessReader.FindAccessTags(item).ToArray();
possibleAccess = _accessReader.FindAccessTags(item).ToList();
}
IdCardConsoleBoundUserInterfaceState newState;
@@ -82,7 +83,7 @@ public sealed class IdCardConsoleSystem : SharedIdCardConsoleSystem
var targetIdComponent = EntityManager.GetComponent<IdCardComponent>(targetId);
var targetAccessComponent = EntityManager.GetComponent<AccessComponent>(targetId);
var jobProto = string.Empty;
var jobProto = new ProtoId<AccessLevelPrototype>(string.Empty);
if (TryComp<StationRecordKeyStorageComponent>(targetId, out var keyStorage)
&& keyStorage.Key is {} key
&& _record.TryGetRecord<GeneralStationRecord>(key, out var record))
@@ -96,7 +97,7 @@ public sealed class IdCardConsoleSystem : SharedIdCardConsoleSystem
true,
targetIdComponent.FullName,
targetIdComponent.JobTitle,
targetAccessComponent.Tags.ToArray(),
targetAccessComponent.Tags.ToList(),
possibleAccess,
jobProto,
privilegedIdName,
@@ -113,8 +114,8 @@ public sealed class IdCardConsoleSystem : SharedIdCardConsoleSystem
private void TryWriteToTargetId(EntityUid uid,
string newFullName,
string newJobTitle,
List<string> newAccessList,
string newJobProto,
List<ProtoId<AccessLevelPrototype>> newAccessList,
ProtoId<AccessLevelPrototype> newJobProto,
EntityUid player,
IdCardConsoleComponent? component = null)
{
@@ -140,7 +141,7 @@ public sealed class IdCardConsoleSystem : SharedIdCardConsoleSystem
return;
}
var oldTags = _access.TryGetTags(targetId) ?? new List<string>();
var oldTags = _access.TryGetTags(targetId) ?? new List<ProtoId<AccessLevelPrototype>>();
oldTags = oldTags.ToList();
var privilegedId = component.PrivilegedIdSlot.Item;
@@ -189,7 +190,7 @@ public sealed class IdCardConsoleSystem : SharedIdCardConsoleSystem
return privilegedId != null && _accessReader.IsAllowed(privilegedId.Value, uid, reader);
}
private void UpdateStationRecord(EntityUid uid, EntityUid targetId, string newFullName, string newJobTitle, JobPrototype? newJobProto)
private void UpdateStationRecord(EntityUid uid, EntityUid targetId, string newFullName, ProtoId<AccessLevelPrototype> newJobTitle, JobPrototype? newJobProto)
{
if (!TryComp<StationRecordKeyStorageComponent>(targetId, out var keyStorage)
|| keyStorage.Key is not { } key

View File

@@ -35,6 +35,7 @@ using Robust.Shared.Map.Components;
using Robust.Shared.Physics;
using Robust.Shared.Physics.Components;
using Robust.Shared.Player;
using Robust.Shared.Prototypes;
using Robust.Shared.Utility;
namespace Content.Server.Administration.Systems;
@@ -844,14 +845,14 @@ public sealed partial class AdminVerbSystem
{
var allAccess = _prototypeManager
.EnumeratePrototypes<AccessLevelPrototype>()
.Select(p => p.ID).ToArray();
.Select(p => new ProtoId<AccessLevelPrototype>(p.ID)).ToArray();
_accessSystem.TrySetTags(entity, allAccess);
}
private void RevokeAllAccess(EntityUid entity)
{
_accessSystem.TrySetTags(entity, Array.Empty<string>());
_accessSystem.TrySetTags(entity, new List<ProtoId<AccessLevelPrototype>>());
}
public enum TricksVerbPriorities

View File

@@ -0,0 +1,69 @@
using System.Linq;
using Content.Server.Doors.Electronics;
using Content.Shared.Access;
using Content.Shared.Access.Components;
using Content.Shared.Access.Systems;
using Content.Shared.DeviceNetwork.Components;
using Content.Shared.Doors.Electronics;
using Content.Shared.Doors;
using Content.Shared.Interaction;
using Robust.Server.GameObjects;
using Robust.Shared.Prototypes;
namespace Content.Server.Doors.Electronics;
public sealed class DoorElectronicsSystem : EntitySystem
{
[Dependency] private readonly UserInterfaceSystem _uiSystem = default!;
[Dependency] private readonly AccessReaderSystem _accessReader = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<DoorElectronicsComponent, DoorElectronicsUpdateConfigurationMessage>(OnChangeConfiguration);
SubscribeLocalEvent<DoorElectronicsComponent, AccessReaderConfigurationChangedEvent>(OnAccessReaderChanged);
SubscribeLocalEvent<DoorElectronicsComponent, BoundUIOpenedEvent>(OnBoundUIOpened);
}
public void UpdateUserInterface(EntityUid uid, DoorElectronicsComponent component)
{
var accesses = new List<ProtoId<AccessLevelPrototype>>();
if (TryComp<AccessReaderComponent>(uid, out var accessReader))
{
foreach (var accessList in accessReader.AccessLists)
{
var access = accessList.FirstOrDefault();
accesses.Add(access);
}
}
var state = new DoorElectronicsConfigurationState(accesses);
_uiSystem.TrySetUiState(uid, DoorElectronicsConfigurationUiKey.Key, state);
}
private void OnChangeConfiguration(
EntityUid uid,
DoorElectronicsComponent component,
DoorElectronicsUpdateConfigurationMessage args)
{
var accessReader = EnsureComp<AccessReaderComponent>(uid);
_accessReader.SetAccesses(uid, accessReader, args.AccessList);
}
private void OnAccessReaderChanged(
EntityUid uid,
DoorElectronicsComponent component,
AccessReaderConfigurationChangedEvent args)
{
UpdateUserInterface(uid, component);
}
private void OnBoundUIOpened(
EntityUid uid,
DoorElectronicsComponent component,
BoundUIOpenedEvent args)
{
UpdateUserInterface(uid, component);
}
}

View File

@@ -14,6 +14,7 @@ using Robust.Server.Placement;
using Robust.Server.Player;
using Robust.Shared.Enums;
using Robust.Shared.Player;
using Robust.Shared.Prototypes;
namespace Content.Server.Sandbox
{
@@ -121,7 +122,7 @@ namespace Content.Server.Sandbox
var allAccess = PrototypeManager
.EnumeratePrototypes<AccessLevelPrototype>()
.Select(p => p.ID).ToArray();
.Select(p => new ProtoId<AccessLevelPrototype>(p.ID)).ToList();
if (_inventory.TryGetSlotEntity(attached, "id", out var slotEntity))
{

View File

@@ -3,6 +3,7 @@ using Content.Server.Explosion.EntitySystems;
using Content.Server.Resist;
using Content.Server.Station.Components;
using Content.Server.Storage.Components;
using Content.Shared.Access;
using Content.Shared.Access.Components;
using Content.Shared.Coordinates;
using Content.Shared.DoAfter;
@@ -14,6 +15,7 @@ using Content.Shared.Tools.Systems;
using Robust.Shared.Containers;
using Robust.Shared.Random;
using Robust.Shared.Timing;
using Robust.Shared.Prototypes;
namespace Content.Server.Storage.EntitySystems;
@@ -138,7 +140,7 @@ public sealed class BluespaceLockerSystem : EntitySystem
}
/// <returns>True if any HashSet in <paramref name="a"/> would grant access to <paramref name="b"/></returns>
private bool AccessMatch(IReadOnlyCollection<HashSet<string>>? a, IReadOnlyCollection<HashSet<string>>? b)
private bool AccessMatch(IReadOnlyCollection<HashSet<ProtoId<AccessLevelPrototype>>>? a, IReadOnlyCollection<HashSet<ProtoId<AccessLevelPrototype>>>? b)
{
if ((a == null || a.Count == 0) && (b == null || b.Count == 0))
return true;

View File

@@ -1,3 +1,6 @@
using Content.Shared.Whitelist;
using Robust.Server.GameObjects;
using Robust.Server.Player;
using Robust.Shared.Player;
using Robust.Shared.Serialization.TypeSerializers.Implementations;
@@ -34,12 +37,19 @@ namespace Content.Server.UserInterface
[DataField]
public bool RequireHands = true;
/// <summary>
/// Entities that are required to open this UI.
/// </summary>
[DataField("allowedItems")]
[ViewVariables(VVAccess.ReadWrite)]
public EntityWhitelist? AllowedItems = null;
/// <summary>
/// Whether you can activate this ui with activateinhand or not
/// </summary>
[ViewVariables(VVAccess.ReadWrite)]
[DataField]
public bool rightClickOnly = false;
public bool RightClickOnly;
/// <summary>
/// Whether spectators (non-admin ghosts) should be allowed to view this UI.
@@ -63,4 +73,3 @@ namespace Content.Server.UserInterface
public ICommonSession? CurrentSingleUser;
}
}

View File

@@ -26,6 +26,7 @@ public sealed partial class ActivatableUISystem : EntitySystem
SubscribeLocalEvent<ActivatableUIComponent, ActivateInWorldEvent>(OnActivate);
SubscribeLocalEvent<ActivatableUIComponent, UseInHandEvent>(OnUseInHand);
SubscribeLocalEvent<ActivatableUIComponent, InteractUsingEvent>(OnInteractUsing);
SubscribeLocalEvent<ActivatableUIComponent, HandDeselectedEvent>(OnHandDeselected);
SubscribeLocalEvent<ActivatableUIComponent, GotUnequippedHandEvent>((uid, aui, _) => CloseAll(uid, aui));
// *THIS IS A BLATANT WORKAROUND!* RATIONALE: Microwaves need it
@@ -100,12 +101,20 @@ public sealed partial class ActivatableUISystem : EntitySystem
if (args.Handled)
return;
if (component.rightClickOnly)
if (component.RightClickOnly)
return;
args.Handled = InteractUI(args.User, uid, component);
}
private void OnInteractUsing(EntityUid uid, ActivatableUIComponent component, InteractUsingEvent args)
{
if (args.Handled) return;
if (component.AllowedItems == null) return;
if (!component.AllowedItems.IsValid(args.Used, EntityManager)) return;
args.Handled = InteractUI(args.User, uid, component);
}
private void OnParentChanged(EntityUid uid, ActivatableUIComponent aui, ref EntParentChangedMessage args)
{
CloseAll(uid, aui);