system.ClearDenyTags(reader);
// test one list
- system.AddAccess(reader, "A");
+ system.TryAddAccess(reader, "A");
Assert.Multiple(() =>
{
Assert.That(system.AreAccessTagsAllowed(new List<ProtoId<AccessLevelPrototype>> { "A" }, reader), Is.True);
Assert.That(system.AreAccessTagsAllowed(new List<ProtoId<AccessLevelPrototype>> { "A", "B" }, reader), Is.True);
Assert.That(system.AreAccessTagsAllowed(Array.Empty<ProtoId<AccessLevelPrototype>>(), reader), Is.False);
});
- system.ClearAccesses(reader);
+ system.TryClearAccesses(reader);
// test one list - two items
- system.AddAccess(reader, new HashSet<ProtoId<AccessLevelPrototype>> { "A", "B" });
+ system.TryAddAccess(reader, new HashSet<ProtoId<AccessLevelPrototype>> { "A", "B" });
Assert.Multiple(() =>
{
Assert.That(system.AreAccessTagsAllowed(new List<ProtoId<AccessLevelPrototype>> { "A" }, reader), Is.False);
Assert.That(system.AreAccessTagsAllowed(new List<ProtoId<AccessLevelPrototype>> { "A", "B" }, reader), Is.True);
Assert.That(system.AreAccessTagsAllowed(Array.Empty<ProtoId<AccessLevelPrototype>>(), reader), Is.False);
});
- system.ClearAccesses(reader);
+ system.TryClearAccesses(reader);
// test two list
var accesses = new List<HashSet<ProtoId<AccessLevelPrototype>>>() {
new HashSet<ProtoId<AccessLevelPrototype>> () { "A" },
new HashSet<ProtoId<AccessLevelPrototype>> () { "B", "C" }
};
- system.AddAccesses(reader, accesses);
+ system.TryAddAccesses(reader, accesses);
Assert.Multiple(() =>
{
Assert.That(system.AreAccessTagsAllowed(new List<ProtoId<AccessLevelPrototype>> { "A" }, reader), Is.True);
Assert.That(system.AreAccessTagsAllowed(new List<ProtoId<AccessLevelPrototype>> { "C", "B", "A" }, reader), Is.True);
Assert.That(system.AreAccessTagsAllowed(Array.Empty<ProtoId<AccessLevelPrototype>>(), reader), Is.False);
});
- system.ClearAccesses(reader);
+ system.TryClearAccesses(reader);
// test deny list
- system.AddAccess(reader, new HashSet<ProtoId<AccessLevelPrototype>> { "A" });
+ system.TryAddAccess(reader, new HashSet<ProtoId<AccessLevelPrototype>> { "A" });
system.AddDenyTag(reader, "B");
Assert.Multiple(() =>
{
Assert.That(system.AreAccessTagsAllowed(new List<ProtoId<AccessLevelPrototype>> { "A", "B" }, reader), Is.False);
Assert.That(system.AreAccessTagsAllowed(Array.Empty<ProtoId<AccessLevelPrototype>>(), reader), Is.False);
});
- system.ClearAccesses(reader);
+ system.TryClearAccesses(reader);
system.ClearDenyTags(reader);
});
await pair.CleanReturnAsync();
using System.Diagnostics.CodeAnalysis;
using System.Linq;
+using System.Text;
using Content.Shared.Access.Components;
using Content.Shared.DeviceLinking.Events;
using Content.Shared.Emag.Systems;
+using Content.Shared.Examine;
using Content.Shared.GameTicking;
using Content.Shared.Hands.EntitySystems;
using Content.Shared.IdentityManagement;
using Content.Shared.Inventory;
+using Content.Shared.Localizations;
using Content.Shared.NameIdentifier;
using Content.Shared.PDA;
using Content.Shared.StationRecords;
{
base.Initialize();
+ SubscribeLocalEvent<AccessReaderComponent, ExaminedEvent>(OnExamined);
SubscribeLocalEvent<AccessReaderComponent, GotEmaggedEvent>(OnEmagged);
SubscribeLocalEvent<AccessReaderComponent, LinkAttemptEvent>(OnLinkAttempt);
+ SubscribeLocalEvent<AccessReaderComponent, AccessReaderConfigurationAttemptEvent>(OnConfigurationAttempt);
SubscribeLocalEvent<AccessReaderComponent, ComponentGetState>(OnGetState);
SubscribeLocalEvent<AccessReaderComponent, ComponentHandleState>(OnHandleState);
}
+ private void OnExamined(Entity<AccessReaderComponent> ent, ref ExaminedEvent args)
+ {
+ if (!GetMainAccessReader(ent, out var mainAccessReader))
+ return;
+
+ mainAccessReader.Value.Comp.AccessListsOriginal ??= new(mainAccessReader.Value.Comp.AccessLists);
+
+ var accessHasBeenModified = mainAccessReader.Value.Comp.AccessLists.Count != mainAccessReader.Value.Comp.AccessListsOriginal.Count;
+
+ if (!accessHasBeenModified)
+ {
+ foreach (var accessSubgroup in mainAccessReader.Value.Comp.AccessLists)
+ {
+ if (!mainAccessReader.Value.Comp.AccessListsOriginal.Any(y => y.SetEquals(accessSubgroup)))
+ {
+ accessHasBeenModified = true;
+ break;
+ }
+ }
+ }
+
+ var canSeeAccessModification = accessHasBeenModified &&
+ (HasComp<ShowAccessReaderSettingsComponent>(ent) ||
+ _inventorySystem.TryGetInventoryEntity<ShowAccessReaderSettingsComponent>(args.Examiner, out _));
+
+ if (canSeeAccessModification)
+ {
+ var localizedCurrentNames = GetLocalizedAccessNames(mainAccessReader.Value.Comp.AccessLists);
+ var accessesFormatted = ContentLocalizationManager.FormatListToOr(localizedCurrentNames);
+ var currentSettingsMessage = localizedCurrentNames.Count > 0
+ ? Loc.GetString("access-reader-access-settings-modified-message", ("access", accessesFormatted))
+ : Loc.GetString("access-reader-access-settings-removed-message");
+
+ args.PushMarkup(currentSettingsMessage);
+
+ return;
+ }
+
+ var localizedOriginalNames = GetLocalizedAccessNames(mainAccessReader.Value.Comp.AccessListsOriginal);
+
+ // If the string list is empty either there were no access restrictions or the localized names were invalid
+ if (localizedOriginalNames.Count == 0)
+ return;
+
+ var originalAccessesFormatted = ContentLocalizationManager.FormatListToOr(localizedOriginalNames);
+ var originalSettingsMessage = Loc.GetString(mainAccessReader.Value.Comp.ExaminationText, ("access", originalAccessesFormatted));
+ args.PushMarkup(originalSettingsMessage);
+ }
+
private void OnGetState(EntityUid uid, AccessReaderComponent component, ref ComponentGetState args)
{
- args.State = new AccessReaderComponentState(component.Enabled, component.DenyTags, component.AccessLists,
- _recordsSystem.Convert(component.AccessKeys), component.AccessLog, component.AccessLogLimit);
+ args.State = new AccessReaderComponentState(
+ component.Enabled,
+ component.DenyTags,
+ component.AccessLists,
+ component.AccessListsOriginal,
+ _recordsSystem.Convert(component.AccessKeys),
+ component.AccessLog,
+ component.AccessLogLimit);
}
private void OnHandleState(EntityUid uid, AccessReaderComponent component, ref ComponentHandleState args)
}
component.AccessLists = new(state.AccessLists);
+ component.AccessListsOriginal = state.AccessListsOriginal == null ? null : new(state.AccessListsOriginal);
component.DenyTags = new(state.DenyTags);
component.AccessLog = new(state.AccessLog);
component.AccessLogLimit = state.AccessLogLimit;
Dirty(uid, reader);
}
+ private void OnConfigurationAttempt(Entity<AccessReaderComponent> ent, ref AccessReaderConfigurationAttemptEvent args)
+ {
+ // The first time that the access list of the reader is modified,
+ // make a copy of the original settings
+ ent.Comp.AccessListsOriginal ??= new(ent.Comp.AccessLists);
+ }
+
/// <summary>
/// Searches the source for access tags
/// then compares it with the all targets accesses to see if it is allowed.
#region: AccessLists API
+ /// <summary>
+ /// Tries to clear the entity's <see cref="AccessReaderComponent.AccessLists"/>.
+ /// </summary>
+ /// <param name="ent">The access reader entity which is having its access permissions cleared.</param>
+ public void TryClearAccesses(Entity<AccessReaderComponent> ent)
+ {
+ if (CanConfigureAccessReader(ent))
+ {
+ ClearAccesses(ent);
+ }
+ }
+
/// <summary>
/// Clears the entity's <see cref="AccessReaderComponent.AccessLists"/>.
/// </summary>
/// <param name="ent">The access reader entity which is having its access permissions cleared.</param>
- public void ClearAccesses(Entity<AccessReaderComponent> ent)
+ private void ClearAccesses(Entity<AccessReaderComponent> ent)
{
ent.Comp.AccessLists.Clear();
RaiseLocalEvent(ent, new AccessReaderConfigurationChangedEvent());
}
+ /// <summary>
+ /// Tries to replace the access permissions in an entity's <see cref="AccessReaderComponent.AccessLists"/> with a supplied list.
+ /// </summary>
+ /// <param name="ent">The access reader entity which is having its list of access permissions replaced.</param>
+ /// <param name="accesses">The list of access permissions replacing the original one.</param>
+ public void TrySetAccesses(Entity<AccessReaderComponent> ent, List<HashSet<ProtoId<AccessLevelPrototype>>> accesses)
+ {
+ if (CanConfigureAccessReader(ent))
+ {
+ SetAccesses(ent, accesses);
+ }
+ }
+
/// <summary>
/// Replaces the access permissions in an entity's <see cref="AccessReaderComponent.AccessLists"/> with a supplied list.
/// </summary>
/// <param name="ent">The access reader entity which is having its list of access permissions replaced.</param>
/// <param name="accesses">The list of access permissions replacing the original one.</param>
- public void SetAccesses(Entity<AccessReaderComponent> ent, List<HashSet<ProtoId<AccessLevelPrototype>>> accesses)
+ private void SetAccesses(Entity<AccessReaderComponent> ent, List<HashSet<ProtoId<AccessLevelPrototype>>> accesses)
{
ent.Comp.AccessLists.Clear();
-
AddAccesses(ent, accesses);
}
+ /// <inheritdoc cref = "TrySetAccesses"/>
+ public void TrySetAccesses(Entity<AccessReaderComponent> ent, List<ProtoId<AccessLevelPrototype>> accesses)
+ {
+ if (CanConfigureAccessReader(ent))
+ {
+ SetAccesses(ent, accesses);
+ }
+ }
+
/// <inheritdoc cref = "SetAccesses"/>
- public void SetAccesses(Entity<AccessReaderComponent> ent, List<ProtoId<AccessLevelPrototype>> accesses)
+ private void SetAccesses(Entity<AccessReaderComponent> ent, List<ProtoId<AccessLevelPrototype>> accesses)
{
ent.Comp.AccessLists.Clear();
-
AddAccesses(ent, accesses);
}
+ /// <summary>
+ /// Tries to add a collection of access permissions to an access reader entity's <see cref="AccessReaderComponent.AccessLists"/>
+ /// </summary>
+ /// <param name="ent">The access reader entity to which the new access permissions are being added.</param>
+ /// <param name="accesses">The list of access permissions being added.</param>
+ public void TryAddAccesses(Entity<AccessReaderComponent> ent, List<HashSet<ProtoId<AccessLevelPrototype>>> accesses)
+ {
+ if (CanConfigureAccessReader(ent))
+ {
+ AddAccesses(ent, accesses);
+ }
+ }
+
/// <summary>
/// Adds a collection of access permissions to an access reader entity's <see cref="AccessReaderComponent.AccessLists"/>
/// </summary>
/// <param name="ent">The access reader entity to which the new access permissions are being added.</param>
/// <param name="accesses">The list of access permissions being added.</param>
- public void AddAccesses(Entity<AccessReaderComponent> ent, List<HashSet<ProtoId<AccessLevelPrototype>>> accesses)
+ private void AddAccesses(Entity<AccessReaderComponent> ent, List<HashSet<ProtoId<AccessLevelPrototype>>> accesses)
{
foreach (var access in accesses)
{
RaiseLocalEvent(ent, new AccessReaderConfigurationChangedEvent());
}
+ /// <inheritdoc cref = "TryAddAccesses"/>
+ public void TryAddAccesses(Entity<AccessReaderComponent> ent, List<ProtoId<AccessLevelPrototype>> accesses)
+ {
+ if (CanConfigureAccessReader(ent))
+ {
+ AddAccesses(ent, accesses);
+ }
+ }
+
/// <inheritdoc cref = "AddAccesses"/>
- public void AddAccesses(Entity<AccessReaderComponent> ent, List<ProtoId<AccessLevelPrototype>> accesses)
+ private void AddAccesses(Entity<AccessReaderComponent> ent, List<ProtoId<AccessLevelPrototype>> accesses)
{
foreach (var access in accesses)
{
RaiseLocalEvent(ent, new AccessReaderConfigurationChangedEvent());
}
+ /// <summary>
+ /// Tries to add an access permission to an access reader entity's <see cref="AccessReaderComponent.AccessLists"/>
+ /// </summary>
+ /// <param name="ent">The access reader entity to which the access permission is being added.</param>
+ /// <param name="access">The access permission being added.</param>
+ /// <param name="dirty">If true, the component will be marked as changed afterward.</param>
+ public void TryAddAccess(Entity<AccessReaderComponent> ent, HashSet<ProtoId<AccessLevelPrototype>> access)
+ {
+ if (CanConfigureAccessReader(ent))
+ {
+ AddAccess(ent, access);
+ }
+ }
+
/// <summary>
/// Adds an access permission to an access reader entity's <see cref="AccessReaderComponent.AccessLists"/>
/// </summary>
/// <param name="ent">The access reader entity to which the access permission is being added.</param>
/// <param name="access">The access permission being added.</param>
/// <param name="dirty">If true, the component will be marked as changed afterward.</param>
- public void AddAccess(Entity<AccessReaderComponent> ent, HashSet<ProtoId<AccessLevelPrototype>> access, bool dirty = true)
+ private void AddAccess(Entity<AccessReaderComponent> ent, HashSet<ProtoId<AccessLevelPrototype>> access, bool dirty = true)
{
ent.Comp.AccessLists.Add(access);
RaiseLocalEvent(ent, new AccessReaderConfigurationChangedEvent());
}
+ /// <inheritdoc cref = "TryAddAccess"/>
+ public void TryAddAccess(Entity<AccessReaderComponent> ent, ProtoId<AccessLevelPrototype> access)
+ {
+ if (CanConfigureAccessReader(ent))
+ {
+ AddAccess(ent, access);
+ }
+ }
+
/// <inheritdoc cref = "AddAccess"/>
- public void AddAccess(Entity<AccessReaderComponent> ent, ProtoId<AccessLevelPrototype> access, bool dirty = true)
+ private void AddAccess(Entity<AccessReaderComponent> ent, ProtoId<AccessLevelPrototype> access, bool dirty = true)
{
AddAccess(ent, new HashSet<ProtoId<AccessLevelPrototype>>() { access }, dirty);
}
+ /// <summary>
+ /// Tries to remove a collection of access permissions from an access reader entity's <see cref="AccessReaderComponent.AccessLists"/>
+ /// </summary>
+ /// <param name="ent">The access reader entity from which the access permissions are being removed.</param>
+ /// <param name="accesses">The list of access permissions being removed.</param>
+ public void TryRemoveAccesses(Entity<AccessReaderComponent> ent, List<HashSet<ProtoId<AccessLevelPrototype>>> accesses)
+ {
+ if (CanConfigureAccessReader(ent))
+ {
+ RemoveAccesses(ent, accesses);
+ }
+ }
+
/// <summary>
/// Removes a collection of access permissions from an access reader entity's <see cref="AccessReaderComponent.AccessLists"/>
/// </summary>
/// <param name="ent">The access reader entity from which the access permissions are being removed.</param>
/// <param name="accesses">The list of access permissions being removed.</param>
- public void RemoveAccesses(Entity<AccessReaderComponent> ent, List<HashSet<ProtoId<AccessLevelPrototype>>> accesses)
+ private void RemoveAccesses(Entity<AccessReaderComponent> ent, List<HashSet<ProtoId<AccessLevelPrototype>>> accesses)
{
foreach (var access in accesses)
{
RaiseLocalEvent(ent, new AccessReaderConfigurationChangedEvent());
}
+ /// <inheritdoc cref = "TryRemoveAccesses"/>
+ public void TryRemoveAccesses(Entity<AccessReaderComponent> ent, List<ProtoId<AccessLevelPrototype>> accesses)
+ {
+ if (CanConfigureAccessReader(ent))
+ {
+ RemoveAccesses(ent, accesses);
+ }
+ }
+
/// <inheritdoc cref = "RemoveAccesses"/>
- public void RemoveAccesses(Entity<AccessReaderComponent> ent, List<ProtoId<AccessLevelPrototype>> accesses)
+ private void RemoveAccesses(Entity<AccessReaderComponent> ent, List<ProtoId<AccessLevelPrototype>> accesses)
{
foreach (var access in accesses)
{
RaiseLocalEvent(ent, new AccessReaderConfigurationChangedEvent());
}
+ /// <summary>
+ /// Tries to removes an access permission from an access reader entity's <see cref="AccessReaderComponent.AccessLists"/>
+ /// </summary>
+ /// <param name="ent">The access reader entity from which the access permission is being removed.</param>
+ /// <param name="access">The access permission being removed.</param>
+ /// <param name="dirty">If true, the component will be marked as changed afterward.</param>
+ public void TryRemoveAccess(Entity<AccessReaderComponent> ent, HashSet<ProtoId<AccessLevelPrototype>> access)
+ {
+ if (CanConfigureAccessReader(ent))
+ {
+ RemoveAccess(ent, access);
+ }
+ }
+
/// <summary>
/// Removes an access permission from an access reader entity's <see cref="AccessReaderComponent.AccessLists"/>
/// </summary>
/// <param name="ent">The access reader entity from which the access permission is being removed.</param>
/// <param name="access">The access permission being removed.</param>
/// <param name="dirty">If true, the component will be marked as changed afterward.</param>
- public void RemoveAccess(Entity<AccessReaderComponent> ent, HashSet<ProtoId<AccessLevelPrototype>> access, bool dirty = true)
+ private void RemoveAccess(Entity<AccessReaderComponent> ent, HashSet<ProtoId<AccessLevelPrototype>> access, bool dirty = true)
{
for (int i = ent.Comp.AccessLists.Count - 1; i >= 0; i--)
{
RaiseLocalEvent(ent, new AccessReaderConfigurationChangedEvent());
}
+ /// <inheritdoc cref = "TryRemoveAccess"/>
+ public void TryRemoveAccess(Entity<AccessReaderComponent> ent, ProtoId<AccessLevelPrototype> access)
+ {
+ if (CanConfigureAccessReader(ent))
+ {
+ RemoveAccess(ent, new HashSet<ProtoId<AccessLevelPrototype>>() { access });
+ }
+ }
+
/// <inheritdoc cref = "RemoveAccess"/>
- public void RemoveAccess(Entity<AccessReaderComponent> ent, ProtoId<AccessLevelPrototype> access, bool dirty = true)
+ private void RemoveAccess(Entity<AccessReaderComponent> ent, ProtoId<AccessLevelPrototype> access, bool dirty = true)
{
RemoveAccess(ent, new HashSet<ProtoId<AccessLevelPrototype>>() { access }, dirty);
}
+ private bool CanConfigureAccessReader(Entity<AccessReaderComponent> ent)
+ {
+ var ev = new AccessReaderConfigurationAttemptEvent();
+ RaiseLocalEvent(ent, ev);
+
+ return !ev.Cancelled;
+ }
+
#endregion
#region: AccessKeys API
Dirty(ent);
}
+
+ private List<string> GetLocalizedAccessNames(List<HashSet<ProtoId<AccessLevelPrototype>>> accessLists)
+ {
+ var localizedNames = new List<string>();
+ string? andSeparator = null;
+
+ foreach (var accessHashSet in accessLists)
+ {
+ var sb = new StringBuilder();
+ var accessSubset = accessHashSet.ToList();
+
+ // Combine the names of all access levels in the subset into a single string
+ foreach (var access in accessSubset)
+ {
+ var accessName = Loc.GetString("access-reader-unknown-id");
+
+ if (_prototype.Resolve(access, out var accessProto) && !string.IsNullOrWhiteSpace(accessProto.Name))
+ accessName = Loc.GetString(accessProto.Name);
+
+ sb.Append(Loc.GetString("access-reader-access-label", ("access", accessName)));
+
+ if (accessSubset.IndexOf(access) < (accessSubset.Count - 1))
+ {
+ andSeparator ??= " " + Loc.GetString("generic-and") + " ";
+ sb.Append(andSeparator);
+ }
+ }
+
+ // Add this string to the list
+ localizedNames.Add(sb.ToString());
+ }
+
+ return localizedNames;
+ }
}