Skip to content

Commit

Permalink
Size-based entity whitelist (#26798)
Browse files Browse the repository at this point in the history
* 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>
  • Loading branch information
Errant-4 and EmoGarbage404 authored Apr 30, 2024
1 parent 046d062 commit c3a0ba9
Show file tree
Hide file tree
Showing 2 changed files with 167 additions and 80 deletions.
75 changes: 72 additions & 3 deletions Content.Shared/Tag/TagSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,21 @@ public bool HasAllTags(EntityUid entity, IEnumerable<string> ids)
/// </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"/>.
Expand All @@ -245,7 +260,7 @@ public bool HasAnyTag(EntityUid entity, params string[] ids)
}

/// <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>
Expand All @@ -256,7 +271,7 @@ public bool HasAnyTag(EntityUid entity, params string[] ids)
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>
Expand All @@ -271,7 +286,22 @@ public bool HasAnyTag(EntityUid entity, List<string> ids)
}

/// <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>
Expand Down Expand Up @@ -478,6 +508,26 @@ public bool HasAllTags(TagComponent component, IEnumerable<string> ids)
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>
Expand Down Expand Up @@ -548,6 +598,25 @@ public bool HasAnyTag(TagComponent component, IEnumerable<string> ids)
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>
Expand Down
172 changes: 95 additions & 77 deletions Content.Shared/Whitelist/EntityWhitelist.cs
Original file line number Diff line number Diff line change
@@ -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
{
/// <summary>
/// Component names that are allowed in the whitelist.
/// </summary>
[DataField("components")] public string[]? Components = null;
// TODO yaml validation
[DataField] public string[]? Components;
// TODO yaml validation

[NonSerialized]
private List<ComponentRegistration>? _registrations = null;
/// <summary>
/// Item sizes that are allowed in the whitelist.
/// </summary>
[DataField]
public List<ProtoId<ItemSizePrototype>>? Sizes;

/// <summary>
/// Tags that are allowed in the whitelist.
/// </summary>
[DataField("tags", customTypeSerializer:typeof(PrototypeIdListSerializer<TagPrototype>))]
public List<string>? Tags = null;
[NonSerialized]
private List<ComponentRegistration>? _registrations;

/// <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;
/// <summary>
/// Tags that are allowed in the whitelist.
/// </summary>
[DataField]
public List<ProtoId<TagPrototype>>? Tags;

public void UpdateRegistrations()
{
if (Components == null) return;
/// <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;

var compfact = IoCManager.Resolve<IComponentFactory>();
_registrations = new List<ComponentRegistration>();
foreach (var name in Components)
public void UpdateRegistrations()
{

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);
}
else if (availability == ComponentAvailability.Unknown)
{
Logger.Warning($"Unknown component name {name} passed to EntityWhitelist!");
}
}
}

/// <summary>
/// Returns whether a given entity fits the whitelist.
/// </summary>
public bool IsValid(EntityUid uid, IEntityManager? entityManager = null)
{
if (Components != null && _registrations == null)
UpdateRegistrations();
/// <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)
IoCManager.Resolve(ref entityManager);
if (_registrations != null)
{
foreach (var reg in _registrations)
{
foreach (var reg in _registrations)
if (entityManager.HasComponent(uid, reg.Type))
{
if (entityManager.HasComponent(uid, reg.Type))
{
if (!RequireAll)
return true;
}
else if (RequireAll)
return false;
if (!RequireAll)
return true;
}
else if (RequireAll)
return false;
}
}

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)
if (Sizes != null && entityManager.TryGetComponent(uid, out ItemComponent? itemComp))
{
if (Sizes.Contains(itemComp.Size))
return true;
}

return false;
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;
}
}

0 comments on commit c3a0ba9

Please sign in to comment.