Size-based entity whitelist (#26798)

* Size based whitelisting

* namespace and misc refactor

* modern datafields, what will they think of next

Co-authored-by: Nemanja <98561806+EmoGarbage404@users.noreply.github.com>

* the future is now

Co-authored-by: Nemanja <98561806+EmoGarbage404@users.noreply.github.com>

* Update TagSystem to work with ProtoId lists

* I guess someone might run into these too, one day

* copypaste moment

* update to sawmill

* Okay, but what if it just worked

---------

Co-authored-by: Nemanja <98561806+EmoGarbage404@users.noreply.github.com>
This commit is contained in:
Errant
2024-05-07 04:00:09 +02:00
committed by null
co-authored by Nemanja
parent ea9da63550
commit c9966a73fb
2 changed files with 176 additions and 89 deletions
+72 -3
View File
@@ -234,6 +234,21 @@ public sealed class TagSystem : EntitySystem
/// </summary>
/// <param name="entity">The entity to check.</param>
/// <param name="ids">The tags to check for.</param>
/// <returns>true if they all exist, false otherwise.</returns>
/// <exception cref="UnknownPrototypeException">
/// Thrown if one of the ids represents an unregistered <see cref="TagPrototype"/>.
/// </exception>
public bool HasAllTags(EntityUid entity, List<ProtoId<TagPrototype>> ids)
{
return TryComp<TagComponent>(entity, out var component) &&
HasAllTags(component, ids);
}
/// <summary>
/// Checks if any of the given tags have been added to an entity.
/// </summary>
/// <param name="entity">The entity to check.</param>
/// <param name="ids">The tags to check for.</param>
/// <returns>true if any of them exist, false otherwise.</returns>
/// <exception cref="UnknownPrototypeException">
/// Thrown if one of the ids represents an unregistered <see cref="TagPrototype"/>.
@@ -245,7 +260,7 @@ public sealed class TagSystem : EntitySystem
}
/// <summary>
/// Checks if all of the given tags have been added to an entity.
/// Checks if any of the given tags have been added to an entity.
/// </summary>
/// <param name="entity">The entity to check.</param>
/// <param name="id">The tag to check for.</param>
@@ -256,7 +271,7 @@ public sealed class TagSystem : EntitySystem
public bool HasAnyTag(EntityUid entity, string id) => HasTag(entity, id);
/// <summary>
/// Checks if all of the given tags have been added to an entity.
/// Checks if any of the given tags have been added to an entity.
/// </summary>
/// <param name="entity">The entity to check.</param>
/// <param name="ids">The tags to check for.</param>
@@ -271,7 +286,22 @@ public sealed class TagSystem : EntitySystem
}
/// <summary>
/// Checks if all of the given tags have been added to an entity.
/// Checks if any of the given tags have been added to an entity.
/// </summary>
/// <param name="entity">The entity to check.</param>
/// <param name="ids">The tags to check for.</param>
/// <returns>true if any of them exist, false otherwise.</returns>
/// <exception cref="UnknownPrototypeException">
/// Thrown if one of the ids represents an unregistered <see cref="TagPrototype"/>.
/// </exception>
public bool HasAnyTag(EntityUid entity, List<ProtoId<TagPrototype>> ids)
{
return TryComp<TagComponent>(entity, out var component) &&
HasAnyTag(component, ids);
}
/// <summary>
/// Checks if any of the given tags have been added to an entity.
/// </summary>
/// <param name="entity">The entity to check.</param>
/// <param name="ids">The tags to check for.</param>
@@ -478,6 +508,26 @@ public sealed class TagSystem : EntitySystem
return true;
}
/// <summary>
/// Checks if all of the given tags have been added.
/// </summary>
/// <param name="ids">The tags to check for.</param>
/// <returns>true if they all exist, false otherwise.</returns>
/// <exception cref="UnknownPrototypeException">
/// Thrown if one of the ids represents an unregistered <see cref="TagPrototype"/>.
/// </exception>
public bool HasAllTags(TagComponent component, List<ProtoId<TagPrototype>> ids)
{
var stringIds = new List<string>();
foreach (var tag in ids)
{
stringIds.Add(tag.Id);
}
return HasAllTags(component, stringIds);
}
/// <summary>
/// Checks if any of the given tags have been added.
/// </summary>
@@ -548,6 +598,25 @@ public sealed class TagSystem : EntitySystem
return false;
}
/// <summary>
/// Checks if any of the given tags have been added.
/// </summary>
/// <param name="ids">The tags to check for.</param>
/// <returns>true if any of them exist, false otherwise.</returns>
/// <exception cref="UnknownPrototypeException">
/// Thrown if one of the ids represents an unregistered <see cref="TagPrototype"/>.
/// </exception>
public bool HasAnyTag(TagComponent comp, List<ProtoId<TagPrototype>> ids)
{
var stringIds = new List<string>();
foreach (var tag in ids)
{
stringIds.Add(tag.Id);
}
return HasAnyTag(comp, stringIds);
}
/// <summary>
/// Tries to remove a tag if it exists.
/// </summary>
+104 -86
View File
@@ -1,103 +1,121 @@
using Content.Shared.Item;
using Content.Shared.Tag;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List;
namespace Content.Shared.Whitelist
namespace Content.Shared.Whitelist;
/// <summary>
/// Used to determine whether an entity fits a certain whitelist.
/// Does not whitelist by prototypes, since that is undesirable; you're better off just adding a tag to all
/// entity prototypes that need to be whitelisted, and checking for that.
/// </summary>
/// <code>
/// whitelist:
/// tags:
/// - Cigarette
/// - FirelockElectronics
/// components:
/// - Buckle
/// - AsteroidRock
/// sizes:
/// - Tiny
/// - Large
/// </code>
[DataDefinition]
[Serializable, NetSerializable]
public sealed partial class EntityWhitelist
{
/// <summary>
/// Used to determine whether an entity fits a certain whitelist.
/// Does not whitelist by prototypes, since that is undesirable; you're better off just adding a tag to all
/// entity prototypes that need to be whitelisted, and checking for that.
/// Component names that are allowed in the whitelist.
/// </summary>
/// <code>
/// whitelist:
/// tags:
/// - Cigarette
/// - FirelockElectronics
/// components:
/// - Buckle
/// - AsteroidRock
/// </code>
[DataDefinition]
[Serializable, NetSerializable]
public sealed partial class EntityWhitelist
[DataField] public string[]? Components;
// TODO yaml validation
/// <summary>
/// Item sizes that are allowed in the whitelist.
/// </summary>
[DataField]
public List<ProtoId<ItemSizePrototype>>? Sizes;
[NonSerialized]
private List<ComponentRegistration>? _registrations;
/// <summary>
/// Tags that are allowed in the whitelist.
/// </summary>
[DataField]
public List<ProtoId<TagPrototype>>? Tags;
/// <summary>
/// If false, an entity only requires one of these components or tags to pass the whitelist. If true, an
/// entity requires to have ALL of these components and tags to pass.
/// The "Sizes" criteria will ignores this, since an item can only have one size.
/// </summary>
[DataField]
public bool RequireAll;
public void UpdateRegistrations()
{
/// <summary>
/// Component names that are allowed in the whitelist.
/// </summary>
[DataField("components")] public string[]? Components = null;
// TODO yaml validation
[NonSerialized]
private List<ComponentRegistration>? _registrations = null;
if (Components == null)
return;
/// <summary>
/// Tags that are allowed in the whitelist.
/// </summary>
[DataField("tags", customTypeSerializer:typeof(PrototypeIdListSerializer<TagPrototype>))]
public List<string>? Tags = null;
/// <summary>
/// If false, an entity only requires one of these components or tags to pass the whitelist. If true, an
/// entity requires to have ALL of these components and tags to pass.
/// </summary>
[DataField("requireAll")]
public bool RequireAll = false;
public void UpdateRegistrations()
var compFact = IoCManager.Resolve<IComponentFactory>();
_registrations = new List<ComponentRegistration>();
foreach (var name in Components)
{
if (Components == null) return;
var compfact = IoCManager.Resolve<IComponentFactory>();
_registrations = new List<ComponentRegistration>();
foreach (var name in Components)
var availability = compFact.GetComponentAvailability(name);
if (compFact.TryGetRegistration(name, out var registration)
&& availability == ComponentAvailability.Available)
{
var availability = compfact.GetComponentAvailability(name);
if (compfact.TryGetRegistration(name, out var registration)
&& availability == ComponentAvailability.Available)
{
_registrations.Add(registration);
}
else if (availability == ComponentAvailability.Unknown)
{
Logger.Warning($"Unknown component name {name} passed to EntityWhitelist!");
}
_registrations.Add(registration);
}
}
/// <summary>
/// Returns whether a given entity fits the whitelist.
/// </summary>
public bool IsValid(EntityUid uid, IEntityManager? entityManager = null)
{
if (Components != null && _registrations == null)
UpdateRegistrations();
IoCManager.Resolve(ref entityManager);
if (_registrations != null)
else if (availability == ComponentAvailability.Unknown)
{
foreach (var reg in _registrations)
{
if (entityManager.HasComponent(uid, reg.Type))
{
if (!RequireAll)
return true;
}
else if (RequireAll)
return false;
}
Logger.Warning($"Unknown component name {name} passed to EntityWhitelist!");
}
if (Tags != null && entityManager.TryGetComponent(uid, out TagComponent? tags))
{
var tagSystem = entityManager.System<TagSystem>();
return RequireAll ? tagSystem.HasAllTags(tags, Tags) : tagSystem.HasAnyTag(tags, Tags);
}
if (RequireAll)
return true;
return false;
}
}
/// <summary>
/// Returns whether a given entity fits the whitelist.
/// </summary>
public bool IsValid(EntityUid uid, IEntityManager? entityManager = null)
{
if (Components != null && _registrations == null)
UpdateRegistrations();
IoCManager.Resolve(ref entityManager);
if (_registrations != null)
{
foreach (var reg in _registrations)
{
if (entityManager.HasComponent(uid, reg.Type))
{
if (!RequireAll)
return true;
}
else if (RequireAll)
return false;
}
}
if (Sizes != null && entityManager.TryGetComponent(uid, out ItemComponent? itemComp))
{
if (Sizes.Contains(itemComp.Size))
return true;
}
if (Tags != null && entityManager.TryGetComponent(uid, out TagComponent? tags))
{
var tagSystem = entityManager.System<TagSystem>();
return RequireAll ? tagSystem.HasAllTags(tags, Tags) : tagSystem.HasAnyTag(tags, Tags);
}
if (RequireAll)
return true;
return false;
}
}