diff --git a/Content.Server/GameObjects/Components/Access/AccessComponent.cs b/Content.Server/GameObjects/Components/Access/AccessComponent.cs
index 829f47ed0e..060f418634 100644
--- a/Content.Server/GameObjects/Components/Access/AccessComponent.cs
+++ b/Content.Server/GameObjects/Components/Access/AccessComponent.cs
@@ -4,29 +4,42 @@ using Robust.Shared.GameObjects;
using Robust.Shared.Serialization;
using Robust.Shared.ViewVariables;
+#nullable enable
+
namespace Content.Server.GameObjects.Components.Access
{
+ ///
+ /// Simple mutable access provider found on ID cards and such.
+ ///
[RegisterComponent]
[ComponentReference(typeof(IAccess))]
public class AccessComponent : Component, IAccess
{
public override string Name => "Access";
+
[ViewVariables]
- private List _tags;
+ private readonly HashSet _tags = new HashSet();
+
+ public ISet Tags => _tags;
+ public bool IsReadOnly => false;
+
public override void ExposeData(ObjectSerializer serializer)
{
base.ExposeData(serializer);
- serializer.DataField(ref _tags, "tags", new List());
+
+ serializer.DataReadWriteFunction("tags", new List(),
+ value =>
+ {
+ _tags.Clear();
+ _tags.UnionWith(value);
+ },
+ () => new List(_tags));
}
- public List GetTags()
+ public void SetTags(IEnumerable newTags)
{
- return _tags;
- }
-
- public void SetTags(List newTags)
- {
- _tags = newTags;
+ _tags.Clear();
+ _tags.UnionWith(newTags);
}
}
}
diff --git a/Content.Server/GameObjects/Components/Access/AccessReaderComponent.cs b/Content.Server/GameObjects/Components/Access/AccessReaderComponent.cs
index 8f1e89da30..f9e6c07a9d 100644
--- a/Content.Server/GameObjects/Components/Access/AccessReaderComponent.cs
+++ b/Content.Server/GameObjects/Components/Access/AccessReaderComponent.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using System.Linq;
using Content.Server.Interfaces;
using Content.Server.Interfaces.GameObjects;
using Content.Shared.GameObjects.Components.Inventory;
@@ -9,54 +10,70 @@ using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Serialization;
using Robust.Shared.ViewVariables;
+#nullable enable
+
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";
- [ViewVariables]
- private List _necessaryTags;
- [ViewVariables]
- private List _sufficientTags;
+
+ private readonly List> _accessLists = new List>();
+ private readonly HashSet _denyTags = new HashSet();
///
- /// Searches an in the entity itself, in its active hand or in its ID slot.
- /// Returns true if an is found and its tags list contains
- /// at least one of or all of .
+ /// 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 tags != null && IsAllowed(tags);
+ return IsAllowed(tags);
}
- private bool IsAllowed(List accessTags)
+ public bool IsAllowed(IAccess access)
{
- foreach (var sufficient in _sufficientTags)
+ return IsAllowed(access.Tags);
+ }
+
+ public bool IsAllowed(ICollection accessTags)
+ {
+ if (_denyTags.Overlaps(accessTags))
{
- if (accessTags.Contains(sufficient))
- {
- return true;
- }
+ // Sec owned by cargo.
+ return false;
}
- foreach (var necessary in _necessaryTags)
- {
- if (!accessTags.Contains(necessary))
- {
- return false;
- }
- }
- return true;
+
+ return _accessLists.Count == 0 || _accessLists.Any(a => a.IsSubsetOf(accessTags));
}
[CanBeNull]
- private static List FindAccessTags(IEntity entity)
+ private static ICollection FindAccessTags(IEntity entity)
{
if (entity.TryGetComponent(out IAccess accessComponent))
{
- return accessComponent.GetTags();
+ return accessComponent.Tags;
}
if (entity.TryGetComponent(out IHandsComponent handsComponent))
@@ -65,32 +82,42 @@ namespace Content.Server.GameObjects.Components.Access
if (activeHandEntity != null &&
activeHandEntity.TryGetComponent(out IAccess handAccessComponent))
{
- return handAccessComponent.GetTags();
+ return handAccessComponent.Tags;
}
}
else
{
- return null;
+ 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.GetTags();
+ return idAccessComponent.Tags;
}
}
- return null;
+
+ return Array.Empty();
}
public override void ExposeData(ObjectSerializer serializer)
{
base.ExposeData(serializer);
- serializer.DataField(ref _necessaryTags, "necessary" ,new List());
- serializer.DataField(ref _sufficientTags, "sufficient", new List());
+ 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());
}
}
}
diff --git a/Content.Server/GameObjects/Components/Access/IdCardConsoleComponent.cs b/Content.Server/GameObjects/Components/Access/IdCardConsoleComponent.cs
index 868c46a0db..cad078d673 100644
--- a/Content.Server/GameObjects/Components/Access/IdCardConsoleComponent.cs
+++ b/Content.Server/GameObjects/Components/Access/IdCardConsoleComponent.cs
@@ -1,4 +1,5 @@
using System.Collections.Generic;
+using System.Linq;
using Content.Server.GameObjects.EntitySystems;
using Content.Server.Interfaces;
using Content.Server.Interfaces.GameObjects;
@@ -180,7 +181,7 @@ namespace Content.Server.GameObjects.Components.Access
true,
targetIdComponent.FullName,
targetIdComponent.JobTitle,
- targetAccessComponent.GetTags(),
+ targetAccessComponent.Tags.ToArray(),
_privilegedIdContainer.ContainedEntity?.Name ?? "",
_targetIdContainer.ContainedEntity?.Name ?? "");
}
diff --git a/Content.Server/GameObjects/Components/PDA/PDAComponent.cs b/Content.Server/GameObjects/Components/PDA/PDAComponent.cs
index 9fb8ead381..52f9b4f670 100644
--- a/Content.Server/GameObjects/Components/PDA/PDAComponent.cs
+++ b/Content.Server/GameObjects/Components/PDA/PDAComponent.cs
@@ -1,3 +1,5 @@
+using System;
+using System.Collections;
using System.Collections.Generic;
using System.Linq;
using Content.Server.GameObjects.Components.Access;
@@ -6,18 +8,18 @@ using Content.Server.Interfaces;
using Content.Server.Interfaces.PDA;
using Content.Shared.GameObjects;
using Content.Shared.GameObjects.Components.PDA;
-using JetBrains.Annotations;
using Robust.Server.GameObjects;
using Robust.Server.GameObjects.Components.Container;
using Robust.Server.GameObjects.Components.UserInterface;
using Robust.Server.Interfaces.GameObjects;
using Robust.Shared.GameObjects;
using Robust.Shared.Interfaces.GameObjects;
-using Robust.Shared.Interfaces.Network;
using Robust.Shared.IoC;
using Robust.Shared.Localization;
-using Robust.Shared.Players;
using Robust.Shared.Serialization;
+using Robust.Shared.ViewVariables;
+
+#nullable enable
namespace Content.Server.GameObjects.Components.PDA
{
@@ -26,24 +28,29 @@ namespace Content.Server.GameObjects.Components.PDA
[ComponentReference(typeof(IAccess))]
public class PDAComponent : SharedPDAComponent, IInteractUsing, IActivate, IUse, IAccess
{
-#pragma warning disable 649
- [Dependency] protected readonly IPDAUplinkManager _uplinkManager;
- [Dependency] protected readonly IEntityManager _entityManager;
-#pragma warning restore 649
+ [Dependency] private readonly IPDAUplinkManager _uplinkManager = default!;
+ [Dependency] private readonly IEntityManager _entityManager = default!;
- private Container _idSlot;
- private PointLightComponent _pdaLight;
- private bool _lightOn = false;
- private BoundUserInterface _interface;
- private string _startingIdCard;
- public bool IdSlotEmpty => _idSlot.ContainedEntities.Count < 1;
- public IEntity OwnerMob { get; private set; }
+ [ViewVariables] private Container _idSlot = default!;
+ [ViewVariables] private PointLightComponent _pdaLight = default!;
+ [ViewVariables] private bool _lightOn;
+ [ViewVariables] private BoundUserInterface _interface = default!;
+ [ViewVariables] private string _startingIdCard = default!;
+ [ViewVariables] public bool IdSlotEmpty => _idSlot.ContainedEntities.Count < 1;
+ [ViewVariables] public IEntity? OwnerMob { get; private set; }
- public IdCardComponent ContainedID { get; private set; }
+ [ViewVariables] public IdCardComponent? ContainedID { get; private set; }
- private AppearanceComponent _appearance;
+ [ViewVariables] private AppearanceComponent _appearance = default!;
- private UplinkAccount _syndicateUplinkAccount;
+ [ViewVariables] private UplinkAccount? _syndicateUplinkAccount;
+
+ [ViewVariables] private readonly PdaAccessSet _accessSet;
+
+ public PDAComponent()
+ {
+ _accessSet = new PdaAccessSet(this);
+ }
public override void ExposeData(ObjectSerializer serializer)
{
@@ -89,7 +96,6 @@ namespace Content.Server.GameObjects.Components.PDA
case PDAUplinkBuyListingMessage buyMsg:
{
-
if (!_uplinkManager.TryPurchaseItem(_syndicateUplinkAccount, buyMsg.ListingToBuy))
{
//TODO: Send a message that tells the buyer they are too poor or something.
@@ -112,13 +118,14 @@ namespace Content.Server.GameObjects.Components.PDA
//Do we have an account? If so provide the info.
if (_syndicateUplinkAccount != null)
{
- var accData = new UplinkAccountData(_syndicateUplinkAccount.AccountHolder, _syndicateUplinkAccount.Balance);
+ var accData = new UplinkAccountData(_syndicateUplinkAccount.AccountHolder,
+ _syndicateUplinkAccount.Balance);
var listings = _uplinkManager.FetchListings.ToArray();
- _interface.SetState(new PDAUpdateState(_lightOn,ownerInfo,accData,listings));
+ _interface.SetState(new PDAUpdateState(_lightOn, ownerInfo, accData, listings));
}
else
{
- _interface.SetState(new PDAUpdateState(_lightOn,ownerInfo));
+ _interface.SetState(new PDAUpdateState(_lightOn, ownerInfo));
}
UpdatePDAAppearance();
@@ -141,10 +148,10 @@ namespace Content.Server.GameObjects.Components.PDA
{
return false;
}
+
InsertIdCard(idCardComponent);
UpdatePDAUserInterface();
return true;
-
}
void IActivate.Activate(ActivateEventArgs eventArgs)
@@ -153,6 +160,7 @@ namespace Content.Server.GameObjects.Components.PDA
{
return;
}
+
_interface.Open(actor.playerSession);
UpdatePDAAppearance();
}
@@ -163,6 +171,7 @@ namespace Content.Server.GameObjects.Components.PDA
{
return false;
}
+
_interface.Open(actor.playerSession);
UpdatePDAAppearance();
return true;
@@ -211,7 +220,7 @@ namespace Content.Server.GameObjects.Components.PDA
private void HandleIDEjection(IEntity pdaUser)
{
- if (IdSlotEmpty)
+ if (ContainedID == null)
{
return;
}
@@ -241,14 +250,129 @@ namespace Content.Server.GameObjects.Components.PDA
}
}
- List IAccess.GetTags()
+ private ISet? GetContainedAccess()
{
- return ContainedID?.Owner.GetComponent()?.GetTags();
+ return ContainedID?.Owner?.GetComponent()?.Tags;
}
- void IAccess.SetTags(List newTags)
+ ISet IAccess.Tags => _accessSet;
+ bool IAccess.IsReadOnly => true;
+
+ void IAccess.SetTags(IEnumerable newTags)
{
- ContainedID?.Owner.GetComponent().SetTags(newTags);
+ throw new NotSupportedException("PDA access list is read-only.");
+ }
+
+ private sealed class PdaAccessSet : ISet
+ {
+ private readonly PDAComponent _pdaComponent;
+ private static readonly HashSet EmptySet = new HashSet();
+
+ public PdaAccessSet(PDAComponent pdaComponent)
+ {
+ _pdaComponent = pdaComponent;
+ }
+
+ public IEnumerator GetEnumerator()
+ {
+ var contained = _pdaComponent.GetContainedAccess() ?? EmptySet;
+ return contained.GetEnumerator();
+ }
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return GetEnumerator();
+ }
+
+ void ICollection.Add(string item)
+ {
+ throw new NotSupportedException("PDA access list is read-only.");
+ }
+
+ public void ExceptWith(IEnumerable other)
+ {
+ throw new NotSupportedException("PDA access list is read-only.");
+ }
+
+ public void IntersectWith(IEnumerable other)
+ {
+ throw new NotSupportedException("PDA access list is read-only.");
+ }
+
+ public bool IsProperSubsetOf(IEnumerable other)
+ {
+ var set = _pdaComponent.GetContainedAccess() ?? EmptySet;
+ return set.IsProperSubsetOf(other);
+ }
+
+ public bool IsProperSupersetOf(IEnumerable other)
+ {
+ var set = _pdaComponent.GetContainedAccess() ?? EmptySet;
+ return set.IsProperSupersetOf(other);
+ }
+
+ public bool IsSubsetOf(IEnumerable other)
+ {
+ var set = _pdaComponent.GetContainedAccess() ?? EmptySet;
+ return set.IsSubsetOf(other);
+ }
+
+ public bool IsSupersetOf(IEnumerable other)
+ {
+ var set = _pdaComponent.GetContainedAccess() ?? EmptySet;
+ return set.IsSupersetOf(other);
+ }
+
+ public bool Overlaps(IEnumerable other)
+ {
+ var set = _pdaComponent.GetContainedAccess() ?? EmptySet;
+ return set.Overlaps(other);
+ }
+
+ public bool SetEquals(IEnumerable other)
+ {
+ var set = _pdaComponent.GetContainedAccess() ?? EmptySet;
+ return set.SetEquals(other);
+ }
+
+ public void SymmetricExceptWith(IEnumerable other)
+ {
+ throw new NotSupportedException("PDA access list is read-only.");
+ }
+
+ public void UnionWith(IEnumerable other)
+ {
+ throw new NotSupportedException("PDA access list is read-only.");
+ }
+
+ bool ISet.Add(string item)
+ {
+ throw new NotSupportedException("PDA access list is read-only.");
+ }
+
+ public void Clear()
+ {
+ throw new NotSupportedException("PDA access list is read-only.");
+ }
+
+ public bool Contains(string item)
+ {
+ return _pdaComponent.GetContainedAccess()?.Contains(item) ?? false;
+ }
+
+ public void CopyTo(string[] array, int arrayIndex)
+ {
+ var set = _pdaComponent.GetContainedAccess() ?? EmptySet;
+ set.CopyTo(array, arrayIndex);
+ }
+
+ public bool Remove(string item)
+ {
+ throw new NotSupportedException("PDA access list is read-only.");
+ }
+
+ public int Count => _pdaComponent.GetContainedAccess()?.Count ?? 0;
+ public bool IsReadOnly => true;
}
}
}
diff --git a/Content.Server/GameTicking/GameTicker.cs b/Content.Server/GameTicking/GameTicker.cs
index 796e2e5c0a..de97df2a5d 100644
--- a/Content.Server/GameTicking/GameTicker.cs
+++ b/Content.Server/GameTicking/GameTicker.cs
@@ -647,8 +647,8 @@ namespace Content.Server.GameTicking
card.JobTitle = jobPrototype.Name;
var access = card.Owner.GetComponent();
- var accessTags = access.GetTags();
- accessTags.AddRange(jobPrototype.Access);
+ var accessTags = access.Tags;
+ accessTags.UnionWith(jobPrototype.Access);
access.SetTags(accessTags);
pdaComponent.SetPDAOwner(mob);
var mindComponent = mob.GetComponent();
diff --git a/Content.Server/Interfaces/IAccess.cs b/Content.Server/Interfaces/IAccess.cs
index 1729f99d3f..f50cae8214 100644
--- a/Content.Server/Interfaces/IAccess.cs
+++ b/Content.Server/Interfaces/IAccess.cs
@@ -1,11 +1,34 @@
+using System;
+using Content.Server.GameObjects.Components.Access;
using System.Collections.Generic;
+#nullable enable
+
namespace Content.Server.Interfaces
{
+ ///
+ /// Contains access levels that can be checked to see if somebody has access with an .
+ ///
public interface IAccess
{
- public List GetTags();
+ ///
+ /// The set of access tags this thing has.
+ ///
+ ///
+ /// This set may be read-only. Check if you want to mutate it.
+ ///
+ ISet Tags { get; }
- public void SetTags(List newTags);
+ ///
+ /// Whether the list is read-only.
+ ///
+ bool IsReadOnly { get; }
+
+ ///
+ /// Replaces the set of access tags we have with the provided set.
+ ///
+ /// The new access tags
+ /// If this access tag list is read-only.
+ void SetTags(IEnumerable newTags);
}
}
diff --git a/Content.Shared/GameObjects/Components/Access/SharedIdCardConsoleComponent.cs b/Content.Shared/GameObjects/Components/Access/SharedIdCardConsoleComponent.cs
index 49250832ff..fc4eb9c78c 100644
--- a/Content.Shared/GameObjects/Components/Access/SharedIdCardConsoleComponent.cs
+++ b/Content.Shared/GameObjects/Components/Access/SharedIdCardConsoleComponent.cs
@@ -52,14 +52,14 @@ namespace Content.Shared.GameObjects.Components.Access
public readonly string TargetIdName;
public readonly string TargetIdFullName;
public readonly string TargetIdJobTitle;
- public readonly List TargetIdAccessList;
+ public readonly string[] TargetIdAccessList;
public IdCardConsoleBoundUserInterfaceState(bool isPrivilegedIdPresent,
bool isPrivilegedIdAuthorized,
bool isTargetIdPresent,
string targetIdFullName,
string targetIdJobTitle,
- List targetIdAccessList, string privilegedIdName, string targetIdName)
+ string[] targetIdAccessList, string privilegedIdName, string targetIdName)
{
IsPrivilegedIdPresent = isPrivilegedIdPresent;
IsPrivilegedIdAuthorized = isPrivilegedIdAuthorized;
diff --git a/Content.Tests/Server/GameObjects/Components/Access/AccessReaderTest.cs b/Content.Tests/Server/GameObjects/Components/Access/AccessReaderTest.cs
new file mode 100644
index 0000000000..724e1e5c37
--- /dev/null
+++ b/Content.Tests/Server/GameObjects/Components/Access/AccessReaderTest.cs
@@ -0,0 +1,85 @@
+using System.Collections.Generic;
+using Content.Server.GameObjects.Components.Access;
+using NUnit.Framework;
+
+namespace Content.Tests.Server.GameObjects.Components.Access
+{
+ [TestFixture]
+ [TestOf(typeof(AccessReader))]
+ public class AccessReaderTest : ContentUnitTest
+ {
+ [Test]
+ public void TestEmpty()
+ {
+ var reader = new AccessReader();
+
+ Assert.That(reader.IsAllowed(new[] {"Foo"}), Is.True);
+ Assert.That(reader.IsAllowed(new[] {"Bar"}), Is.True);
+ Assert.That(reader.IsAllowed(new string[] {}), Is.True);
+ }
+
+ [Test]
+ public void TestDeny()
+ {
+ var reader = new AccessReader();
+ reader.DenyTags.Add("A");
+
+ Assert.That(reader.IsAllowed(new[] {"Foo"}), Is.True);
+ Assert.That(reader.IsAllowed(new[] {"A"}), Is.False);
+ Assert.That(reader.IsAllowed(new[] {"A", "Foo"}), Is.False);
+ Assert.That(reader.IsAllowed(new string[] {}), Is.True);
+ }
+
+ [Test]
+ public void TestOneList()
+ {
+ var reader = new AccessReader();
+ reader.AccessLists.Add(new HashSet {"A"});
+
+ Assert.That(reader.IsAllowed(new[] {"A"}), Is.True);
+ Assert.That(reader.IsAllowed(new[] {"B"}), Is.False);
+ Assert.That(reader.IsAllowed(new[] {"A", "B"}), Is.True);
+ Assert.That(reader.IsAllowed(new string[] {}), Is.False);
+ }
+
+ [Test]
+ public void TestOneListTwoItems()
+ {
+ var reader = new AccessReader();
+ reader.AccessLists.Add(new HashSet {"A", "B"});
+
+ Assert.That(reader.IsAllowed(new[] {"A"}), Is.False);
+ Assert.That(reader.IsAllowed(new[] {"B"}), Is.False);
+ Assert.That(reader.IsAllowed(new[] {"A", "B"}), Is.True);
+ Assert.That(reader.IsAllowed(new string[] {}), Is.False);
+ }
+
+ [Test]
+ public void TestTwoList()
+ {
+ var reader = new AccessReader();
+ reader.AccessLists.Add(new HashSet {"A"});
+ reader.AccessLists.Add(new HashSet {"B", "C"});
+
+ Assert.That(reader.IsAllowed(new[] {"A"}), Is.True);
+ Assert.That(reader.IsAllowed(new[] {"B"}), Is.False);
+ Assert.That(reader.IsAllowed(new[] {"A", "B"}), Is.True);
+ Assert.That(reader.IsAllowed(new[] {"C", "B"}), Is.True);
+ Assert.That(reader.IsAllowed(new[] {"C", "B", "A"}), Is.True);
+ Assert.That(reader.IsAllowed(new string[] {}), Is.False);
+ }
+
+ [Test]
+ public void TestDenyList()
+ {
+ var reader = new AccessReader();
+ reader.AccessLists.Add(new HashSet {"A"});
+ reader.DenyTags.Add("B");
+
+ Assert.That(reader.IsAllowed(new[] {"A"}), Is.True);
+ Assert.That(reader.IsAllowed(new[] {"B"}), Is.False);
+ Assert.That(reader.IsAllowed(new[] {"A", "B"}), Is.False);
+ Assert.That(reader.IsAllowed(new string[] {}), Is.False);
+ }
+ }
+}
diff --git a/Resources/Maps/saltern.yml b/Resources/Maps/saltern.yml
index 875cb20e69..a25dcdb111 100644
--- a/Resources/Maps/saltern.yml
+++ b/Resources/Maps/saltern.yml
@@ -7484,8 +7484,8 @@ entities:
pos: 6.5,17.5
rot: -1.5707963267948966 rad
type: Transform
- - necessary:
- - HeadOfPersonnel
+ - access:
+ - - HeadOfPersonnel
type: AccessReader
- uid: 1042
type: reinforced_wall
@@ -22369,8 +22369,8 @@ entities:
pos: 5.5,25.5
rot: -1.5707963267948966 rad
type: Transform
- - necessary:
- - Captain
+ - access:
+ - - Captain
type: AccessReader
- uid: 2893
type: AirlockMaintCommonLocked
@@ -22527,9 +22527,9 @@ entities:
pos: 38.5,1.5
rot: -1.5707963267948966 rad
type: Transform
- - necessary:
- - Engineering
- - Command
+ - access:
+ - - Engineering
+ - Command
type: AccessReader
- uid: 2912
type: Catwalk
@@ -22545,8 +22545,8 @@ entities:
pos: 11.5,20.5
rot: -1.5707963267948966 rad
type: Transform
- - necessary:
- - HeadOfPersonnel
+ - access:
+ - - HeadOfPersonnel
type: AccessReader
- uid: 2914
type: AirlockExternalLocked
@@ -22601,8 +22601,8 @@ entities:
pos: -8.5,-7.5
rot: -1.5707963267948966 rad
type: Transform
- - necessary:
- - Service
+ - access:
+ - - Service
type: AccessReader
- uid: 2921
type: AirlockServiceGlassLocked
@@ -22687,8 +22687,8 @@ entities:
pos: -20.5,11.5
rot: -1.5707963267948966 rad
type: Transform
- - necessary:
- - Janitor
+ - access:
+ - - Janitor
type: AccessReader
- uid: 2932
type: AirlockEngineeringGlassLocked
@@ -22735,9 +22735,9 @@ entities:
pos: 20.5,-13.5
rot: -1.5707963267948966 rad
type: Transform
- - necessary:
- - Medical
- - Command
+ - access:
+ - - Medical
+ - Command
type: AccessReader
- uid: 2937
type: AirlockCommandGlassLocked
@@ -22748,9 +22748,9 @@ entities:
pos: -8.5,-21.5
rot: -1.5707963267948966 rad
type: Transform
- - necessary:
- - Research
- - Command
+ - access:
+ - - Research
+ - Command
type: AccessReader
- uid: 2938
type: AirlockCommandGlassLocked
@@ -22761,9 +22761,9 @@ entities:
pos: -3.5,-21.5
rot: -1.5707963267948966 rad
type: Transform
- - necessary:
- - Research
- - Command
+ - access:
+ - - Research
+ - Command
type: AccessReader
- uid: 2939
type: AirlockScienceLocked
@@ -22801,8 +22801,8 @@ entities:
pos: -25.5,10.5
rot: -1.5707963267948966 rad
type: Transform
- - necessary:
- - Maintenance
+ - access:
+ - - Maintenance
type: AccessReader
- uid: 2943
type: AirlockServiceGlassLocked
@@ -22813,8 +22813,8 @@ entities:
pos: -25.5,8.5
rot: -1.5707963267948966 rad
type: Transform
- - necessary:
- - Maintenance
+ - access:
+ - - Maintenance
type: AccessReader
- uid: 2944
type: AirlockGlass
@@ -22834,8 +22834,8 @@ entities:
pos: -20.5,-7.5
rot: -1.5707963267948966 rad
type: Transform
- - necessary:
- - Theatre
+ - access:
+ - - Theatre
type: AccessReader
- uid: 2946
type: Airlock
@@ -22889,8 +22889,8 @@ entities:
pos: -14.5,-4.5
rot: -1.5707963267948966 rad
type: Transform
- - necessary:
- - Service
+ - access:
+ - - Service
type: AccessReader
- uid: 2952
type: AirlockMaintCommonLocked
@@ -22899,8 +22899,8 @@ entities:
pos: -16.5,-7.5
rot: -1.5707963267948966 rad
type: Transform
- - necessary:
- - Service
+ - access:
+ - - Service
type: AccessReader
- uid: 2953
type: AirlockMaintCommonLocked
@@ -23002,8 +23002,8 @@ entities:
pos: 7.5,6.5
rot: -1.5707963267948966 rad
type: Transform
- - necessary:
- - External
+ - access:
+ - - External
type: AccessReader
- uid: 2966
type: AirlockCommandGlassLocked
@@ -23014,8 +23014,8 @@ entities:
pos: 9.5,6.5
rot: -1.5707963267948966 rad
type: Transform
- - necessary:
- - External
+ - access:
+ - - External
type: AccessReader
- uid: 2967
type: AirlockMaintCommandLocked
@@ -23024,8 +23024,8 @@ entities:
pos: 8.5,12.5
rot: -1.5707963267948966 rad
type: Transform
- - necessary:
- - External
+ - access:
+ - - External
type: AccessReader
- uid: 2968
type: AirlockCargoGlassLocked
@@ -23063,8 +23063,8 @@ entities:
pos: 20.5,14.5
rot: -1.5707963267948966 rad
type: Transform
- - necessary:
- - Cargo
+ - access:
+ - - Cargo
type: AccessReader
- uid: 2972
type: AirlockExternalLocked
@@ -23075,8 +23075,8 @@ entities:
pos: 20.5,16.5
rot: -1.5707963267948966 rad
type: Transform
- - necessary:
- - Cargo
+ - access:
+ - - Cargo
type: AccessReader
- uid: 2973
type: AirlockExternalLocked
@@ -23087,8 +23087,8 @@ entities:
pos: 22.5,16.5
rot: -1.5707963267948966 rad
type: Transform
- - necessary:
- - Cargo
+ - access:
+ - - Cargo
type: AccessReader
- uid: 2974
type: AirlockExternalLocked
@@ -23099,8 +23099,8 @@ entities:
pos: 22.5,14.5
rot: -1.5707963267948966 rad
type: Transform
- - necessary:
- - Cargo
+ - access:
+ - - Cargo
type: AccessReader
- uid: 2975
type: AirlockExternalLocked
@@ -23118,9 +23118,9 @@ entities:
pos: -8.5,18.5
rot: -1.5707963267948966 rad
type: Transform
- - necessary:
- - Security
- - Command
+ - access:
+ - - Security
+ - Command
type: AccessReader
- uid: 2977
type: Poweredlight
diff --git a/Resources/Prototypes/Entities/Buildings/Doors/airlock_access.yml b/Resources/Prototypes/Entities/Buildings/Doors/airlock_access.yml
index 31df9ef456..eef2f65d98 100644
--- a/Resources/Prototypes/Entities/Buildings/Doors/airlock_access.yml
+++ b/Resources/Prototypes/Entities/Buildings/Doors/airlock_access.yml
@@ -5,7 +5,7 @@
suffix: Service, Locked
components:
- type: AccessReader
- necessary: ["Service"]
+ access: [["Service"]]
- type: entity
parent: AirlockExternal
@@ -13,7 +13,7 @@
suffix: External, Locked
components:
- type: AccessReader
- necessary: ["External"]
+ access: [["External"]]
- type: entity
parent: AirlockEngineering
@@ -21,7 +21,7 @@
suffix: Engineering, Locked
components:
- type: AccessReader
- necessary: ["Engineering"]
+ access: [["Engineering"]]
- type: entity
parent: AirlockCargo
@@ -29,7 +29,7 @@
suffix: Cargo, Locked
components:
- type: AccessReader
- necessary: ["Cargo"]
+ access: [["Cargo"]]
- type: entity
parent: AirlockMedical
@@ -37,7 +37,7 @@
suffix: Medical, Locked
components:
- type: AccessReader
- necessary: ["Medical"]
+ access: [["Medical"]]
- type: entity
parent: AirlockScience
@@ -45,7 +45,7 @@
suffix: Science, Locked
components:
- type: AccessReader
- necessary: ["Research"]
+ access: [["Research"]]
- type: entity
parent: AirlockCommand
@@ -53,7 +53,7 @@
suffix: Command, Locked
components:
- type: AccessReader
- necessary: ["Command"]
+ access: [["Command"]]
- type: entity
parent: AirlockSecurity
@@ -61,7 +61,7 @@
suffix: Security, Locked
components:
- type: AccessReader
- necessary: ["Security"]
+ access: [["Security"]]
- type: entity
parent: AirlockSecurity
@@ -69,7 +69,7 @@
suffix: Vault, Locked
components:
- type: AccessReader
- necessary: ["Security", "Command"]
+ access: [["Security", "Command"]]
- type: entity
parent: AirlockCommand
@@ -77,7 +77,7 @@
suffix: EVA, Locked
components:
- type: AccessReader
- necessary: ["External"]
+ access: [["External"]]
# Glass Airlocks
- type: entity
@@ -86,7 +86,7 @@
suffix: Service, Locked
components:
- type: AccessReader
- necessary: ["Service"]
+ access: [["Service"]]
- type: entity
parent: AirlockEngineeringGlass
@@ -94,7 +94,7 @@
suffix: Glass, Locked
components:
- type: AccessReader
- necessary: ["Engineering"]
+ access: [["Engineering"]]
- type: entity
parent: AirlockCargoGlass
@@ -102,7 +102,7 @@
suffix: Cargo, Locked
components:
- type: AccessReader
- necessary: ["Cargo"]
+ access: [["Cargo"]]
- type: entity
parent: AirlockMedicalGlass
@@ -110,7 +110,7 @@
suffix: Medical, Locked
components:
- type: AccessReader
- necessary: ["Medical"]
+ access: [["Medical"]]
- type: entity
parent: AirlockScienceGlass
@@ -118,7 +118,7 @@
suffix: Science, Locked
components:
- type: AccessReader
- necessary: ["Research"]
+ access: [["Research"]]
- type: entity
parent: AirlockCommandGlass
@@ -126,7 +126,7 @@
suffix: Command, Locked
components:
- type: AccessReader
- necessary: ["Command"]
+ access: [["Command"]]
- type: entity
parent: AirlockSecurityGlass
@@ -134,7 +134,7 @@
suffix: Security, Locked
components:
- type: AccessReader
- necessary: ["Security"]
+ access: [["Security"]]
# Maintenance Hatchs
- type: entity
@@ -143,7 +143,7 @@
suffix: Locked
components:
- type: AccessReader
- necessary: ["Maintenance"]
+ access: [["Maintenance"]]
- type: entity
parent: AirlockMaintCargo
@@ -151,7 +151,7 @@
suffix: Cargo, Locked
components:
- type: AccessReader
- necessary: ["Cargo", "Maintenance"]
+ access: [["Cargo"]]
- type: entity
parent: AirlockMaintCommand
@@ -159,7 +159,7 @@
suffix: Command, Locked
components:
- type: AccessReader
- necessary: ["Command", "Maintenance"]
+ access: [["Command"]]
- type: entity
parent: AirlockMaintCommon
@@ -167,7 +167,7 @@
suffix: Common, Locked
components:
- type: AccessReader
- necessary: ["Maintenance"]
+ access: [["Maintenance"]]
- type: entity
parent: AirlockMaintEngi
@@ -175,15 +175,15 @@
suffix: Engineering, Locked
components:
- type: AccessReader
- necessary: ["Engineering", "Maintenance"]
-
+ access: [["Engineering"]]
+
- type: entity
parent: AirlockMaintInt
id: AirlockMaintIntLocked
suffix: Interior, Locked
components:
- type: AccessReader
- necessary: ["Maintenance"]
+ access: [["Maintenance"]]
- type: entity
parent: AirlockMaintMed
@@ -191,7 +191,7 @@
suffix: Medical, Locked
components:
- type: AccessReader
- necessary: ["Medical", "Maintenance"]
+ access: [["Medical"]]
- type: entity
parent: AirlockMaintRnD
@@ -199,7 +199,7 @@
suffix: RnD, Locked
components:
- type: AccessReader
- necessary: ["Research", "Maintenance"]
+ access: [["Research"]]
- type: entity
parent: AirlockMaintSec
@@ -207,4 +207,4 @@
suffix: Security, Locked
components:
- type: AccessReader
- necessary: ["Security", "Maintenance"]
+ access: [["Security"]]
diff --git a/Resources/Prototypes/Entities/Buildings/computers.yml b/Resources/Prototypes/Entities/Buildings/computers.yml
index 31b58a932b..611bd5c2cb 100644
--- a/Resources/Prototypes/Entities/Buildings/computers.yml
+++ b/Resources/Prototypes/Entities/Buildings/computers.yml
@@ -158,7 +158,7 @@
name: ID Card Computer
components:
- type: AccessReader
- necessary: ["HeadOfPersonnel"]
+ access: [["HeadOfPersonnel"]]
- type: IdCardConsole
- type: UserInterface
interfaces: