using System.Diagnostics.CodeAnalysis; using Content.Shared.Item; using Content.Shared.Tag; using Robust.Shared.Utility; namespace Content.Shared.Whitelist; public sealed class EntityWhitelistSystem : EntitySystem { [Dependency] private readonly TagSystem _tag = default!; private EntityQuery _itemQuery; public override void Initialize() { base.Initialize(); _itemQuery = GetEntityQuery(); } /// public bool IsValid(EntityWhitelist list, [NotNullWhen(true)] EntityUid? uid) { return uid != null && IsValid(list, uid.Value); } /// /// Checks whether a given entity is allowed by a whitelist and not blocked by a blacklist. /// If a blacklist is provided and it matches then this returns false. /// If a whitelist is provided and it does not match then this returns false. /// If either list is null it does not get checked. /// public bool CheckBoth([NotNullWhen(true)] EntityUid? uid, EntityWhitelist? blacklist = null, EntityWhitelist? whitelist = null) { if (uid == null) return false; if (blacklist != null && IsValid(blacklist, uid)) return false; return whitelist == null || IsValid(whitelist, uid); } /// /// Checks whether a given entity satisfies a whitelist. /// public bool IsValid(EntityWhitelist list, EntityUid uid) { list.Registrations ??= StringsToRegs(list.Components); if (list.Registrations != null) { foreach (var reg in list.Registrations) { if (EntityManager.HasComponent(uid, reg)) { if (!list.RequireAll) return true; } else if (list.RequireAll) return false; } } if (list.Sizes != null && _itemQuery.TryComp(uid, out var itemComp)) { if (list.Sizes.Contains(itemComp.Size)) return true; } if (list.Tags != null) { return list.RequireAll ? _tag.HasAllTags(uid, list.Tags) : _tag.HasAnyTag(uid, list.Tags); } return list.RequireAll; } /// The following are a list of "helper functions" that are basically the same as each other /// to help make code that uses EntityWhitelist a bit more readable because at the moment /// it is quite clunky having to write out component.Whitelist == null ? true : _whitelist.IsValid(component.Whitelist, uid) /// several times in a row and makes comparisons easier to read /// /// Helper function to determine if Whitelist is not null and entity is on list /// public bool IsWhitelistPass(EntityWhitelist? whitelist, EntityUid uid) { if (whitelist == null) return false; return IsValid(whitelist, uid); } /// /// Helper function to determine if Whitelist is not null and entity is not on the list /// public bool IsWhitelistFail(EntityWhitelist? whitelist, EntityUid uid) { if (whitelist == null) return false; return !IsValid(whitelist, uid); } /// /// Helper function to determine if Whitelist is either null or the entity is on the list /// public bool IsWhitelistPassOrNull(EntityWhitelist? whitelist, EntityUid uid) { if (whitelist == null) return true; return IsValid(whitelist, uid); } /// /// Helper function to determine if Whitelist is either null or the entity is not on the list /// public bool IsWhitelistFailOrNull(EntityWhitelist? whitelist, EntityUid uid) { if (whitelist == null) return true; return !IsValid(whitelist, uid); } /// /// Helper function to determine if Blacklist is not null and entity is on list /// Duplicate of equivalent Whitelist function /// public bool IsBlacklistPass(EntityWhitelist? blacklist, EntityUid uid) { return IsWhitelistPass(blacklist, uid); } /// /// Helper function to determine if Blacklist is not null and entity is not on the list /// Duplicate of equivalent Whitelist function /// public bool IsBlacklistFail(EntityWhitelist? blacklist, EntityUid uid) { return IsWhitelistFail(blacklist, uid); } /// /// Helper function to determine if Blacklist is either null or the entity is on the list /// Duplicate of equivalent Whitelist function /// public bool IsBlacklistPassOrNull(EntityWhitelist? blacklist, EntityUid uid) { return IsWhitelistPassOrNull(blacklist, uid); } /// /// Helper function to determine if Blacklist is either null or the entity is not on the list /// Duplicate of equivalent Whitelist function /// public bool IsBlacklistFailOrNull(EntityWhitelist? blacklist, EntityUid uid) { return IsWhitelistFailOrNull(blacklist, uid); } private List? StringsToRegs(string[]? input) { if (input == null || input.Length == 0) return null; var list = new List(input.Length); foreach (var name in input) { if (Factory.TryGetRegistration(name, out var registration)) list.Add(registration); else if (Factory.GetComponentAvailability(name) != ComponentAvailability.Ignore) Log.Error($"{nameof(StringsToRegs)} failed: Unknown component name {name} passed to EntityWhitelist!"); } return list; } }