diff --git a/Content.Client/Verbs/VerbSystem.cs b/Content.Client/Verbs/VerbSystem.cs index 2e6c5f5809..5f1f49e5fd 100644 --- a/Content.Client/Verbs/VerbSystem.cs +++ b/Content.Client/Verbs/VerbSystem.cs @@ -123,7 +123,6 @@ namespace Content.Client.Verbs if ((visibility & MenuVisibility.Invisible) == 0) { var spriteQuery = GetEntityQuery(); - var tagQuery = GetEntityQuery(); for (var i = entities.Count - 1; i >= 0; i--) { @@ -131,7 +130,7 @@ namespace Content.Client.Verbs if (!spriteQuery.TryGetComponent(entity, out var spriteComponent) || !spriteComponent.Visible || - _tagSystem.HasTag(entity, "HideContextMenu", tagQuery)) + _tagSystem.HasTag(entity, "HideContextMenu")) { entities.RemoveSwap(i); } diff --git a/Content.IntegrationTests/Tests/Tag/TagTest.cs b/Content.IntegrationTests/Tests/Tag/TagTest.cs index cbcdd1c6c6..e6cd2accaf 100644 --- a/Content.IntegrationTests/Tests/Tag/TagTest.cs +++ b/Content.IntegrationTests/Tests/Tag/TagTest.cs @@ -53,11 +53,13 @@ namespace Content.IntegrationTests.Tests.Tag EntityUid sTagDummy = default; TagComponent sTagComponent = null!; + Entity sTagEntity = default; await server.WaitPost(() => { sTagDummy = sEntityManager.SpawnEntity(TagEntityId, MapCoordinates.Nullspace); sTagComponent = sEntityManager.GetComponent(sTagDummy); + sTagEntity = new Entity(sTagDummy, sTagComponent); }); await server.WaitAssertion(() => @@ -130,49 +132,64 @@ namespace Content.IntegrationTests.Tests.Tag Assert.Multiple(() => { // Cannot add the starting tag again - Assert.That(tagSystem.AddTag(sTagDummy, sTagComponent, StartingTag), Is.False); - Assert.That(tagSystem.AddTags(sTagDummy, sTagComponent, StartingTag, StartingTag), Is.False); - Assert.That(tagSystem.AddTags(sTagDummy, sTagComponent, new List { StartingTag, StartingTag }), Is.False); + Assert.That(tagSystem.AddTag(sTagEntity, StartingTag), Is.False); + + Assert.That(tagSystem.AddTags(sTagEntity, StartingTag, StartingTag), Is.False); + Assert.That(tagSystem.AddTags(sTagEntity, new List> { StartingTag, StartingTag }), Is.False); + Assert.That(tagSystem.AddTags(sTagEntity, new HashSet> { StartingTag, StartingTag }), Is.False); // Has the starting tag Assert.That(tagSystem.HasTag(sTagComponent, StartingTag), Is.True); + Assert.That(tagSystem.HasAllTags(sTagComponent, StartingTag, StartingTag), Is.True); - Assert.That(tagSystem.HasAllTags(sTagComponent, new List { StartingTag, StartingTag }), Is.True); + Assert.That(tagSystem.HasAllTags(sTagComponent, new List> { StartingTag, StartingTag }), Is.True); + Assert.That(tagSystem.HasAllTags(sTagComponent, new HashSet> { StartingTag, StartingTag }), Is.True); + Assert.That(tagSystem.HasAnyTag(sTagComponent, StartingTag, StartingTag), Is.True); - Assert.That(tagSystem.HasAnyTag(sTagComponent, new List { StartingTag, StartingTag }), Is.True); + Assert.That(tagSystem.HasAnyTag(sTagComponent, new List> { StartingTag, StartingTag }), Is.True); + Assert.That(tagSystem.HasAnyTag(sTagComponent, new HashSet> { StartingTag, StartingTag }), Is.True); // Does not have the added tag yet Assert.That(tagSystem.HasTag(sTagComponent, AddedTag), Is.False); + Assert.That(tagSystem.HasAllTags(sTagComponent, AddedTag, AddedTag), Is.False); - Assert.That(tagSystem.HasAllTags(sTagComponent, new List { AddedTag, AddedTag }), Is.False); + Assert.That(tagSystem.HasAllTags(sTagComponent, new List> { AddedTag, AddedTag }), Is.False); + Assert.That(tagSystem.HasAllTags(sTagComponent, new HashSet> { AddedTag, AddedTag }), Is.False); + Assert.That(tagSystem.HasAnyTag(sTagComponent, AddedTag, AddedTag), Is.False); - Assert.That(tagSystem.HasAnyTag(sTagComponent, new List { AddedTag, AddedTag }), Is.False); + Assert.That(tagSystem.HasAnyTag(sTagComponent, new List> { AddedTag, AddedTag }), Is.False); + Assert.That(tagSystem.HasAnyTag(sTagComponent, new HashSet> { AddedTag, AddedTag }), Is.False); // Has a combination of the two tags Assert.That(tagSystem.HasAnyTag(sTagComponent, StartingTag, AddedTag), Is.True); - Assert.That(tagSystem.HasAnyTag(sTagComponent, new List { StartingTag, AddedTag }), Is.True); + Assert.That(tagSystem.HasAnyTag(sTagComponent, new List> { StartingTag, AddedTag }), Is.True); + Assert.That(tagSystem.HasAnyTag(sTagComponent, new HashSet> { StartingTag, AddedTag }), Is.True); // Does not have both tags Assert.That(tagSystem.HasAllTags(sTagComponent, StartingTag, AddedTag), Is.False); - Assert.That(tagSystem.HasAllTags(sTagComponent, new List { StartingTag, AddedTag }), Is.False); + Assert.That(tagSystem.HasAllTags(sTagComponent, new List> { StartingTag, AddedTag }), Is.False); + Assert.That(tagSystem.HasAllTags(sTagComponent, new HashSet> { StartingTag, AddedTag }), Is.False); // Cannot remove a tag that does not exist - Assert.That(tagSystem.RemoveTag(sTagDummy, sTagComponent, AddedTag), Is.False); - Assert.That(tagSystem.RemoveTags(sTagDummy, sTagComponent, AddedTag, AddedTag), Is.False); - Assert.That(tagSystem.RemoveTags(sTagDummy, sTagComponent, new List { AddedTag, AddedTag }), Is.False); + Assert.That(tagSystem.RemoveTag(sTagEntity, AddedTag), Is.False); + + Assert.That(tagSystem.RemoveTags(sTagEntity, AddedTag, AddedTag), Is.False); + Assert.That(tagSystem.RemoveTags(sTagEntity, new List> { AddedTag, AddedTag }), Is.False); + Assert.That(tagSystem.RemoveTags(sTagEntity, new HashSet> { AddedTag, AddedTag }), Is.False); }); // Can add the new tag - Assert.That(tagSystem.AddTag(sTagDummy, sTagComponent, AddedTag), Is.True); + Assert.That(tagSystem.AddTag(sTagEntity, AddedTag), Is.True); Assert.Multiple(() => { // Cannot add it twice - Assert.That(tagSystem.AddTag(sTagDummy, sTagComponent, AddedTag), Is.False); + Assert.That(tagSystem.AddTag(sTagEntity, AddedTag), Is.False); // Cannot add existing tags - Assert.That(tagSystem.AddTags(sTagDummy, sTagComponent, StartingTag, AddedTag), Is.False); - Assert.That(tagSystem.AddTags(sTagDummy, sTagComponent, new List { StartingTag, AddedTag }), Is.False); + Assert.That(tagSystem.AddTags(sTagEntity, StartingTag, AddedTag), Is.False); + Assert.That(tagSystem.AddTags(sTagEntity, new List> { StartingTag, AddedTag }), Is.False); + Assert.That(tagSystem.AddTags(sTagEntity, new HashSet> { StartingTag, AddedTag }), Is.False); // Now has two tags Assert.That(sTagComponent.Tags, Has.Count.EqualTo(2)); @@ -180,65 +197,103 @@ namespace Content.IntegrationTests.Tests.Tag // Has both tags Assert.That(tagSystem.HasTag(sTagComponent, StartingTag), Is.True); Assert.That(tagSystem.HasTag(sTagComponent, AddedTag), Is.True); + Assert.That(tagSystem.HasAllTags(sTagComponent, StartingTag, StartingTag), Is.True); Assert.That(tagSystem.HasAllTags(sTagComponent, AddedTag, StartingTag), Is.True); - Assert.That(tagSystem.HasAllTags(sTagComponent, new List { StartingTag, AddedTag }), Is.True); - Assert.That(tagSystem.HasAllTags(sTagComponent, new List { AddedTag, StartingTag }), Is.True); + Assert.That(tagSystem.HasAllTags(sTagComponent, new List> { StartingTag, AddedTag }), Is.True); + Assert.That(tagSystem.HasAllTags(sTagComponent, new List> { AddedTag, StartingTag }), Is.True); + Assert.That(tagSystem.HasAllTags(sTagComponent, new HashSet> { StartingTag, AddedTag }), Is.True); + Assert.That(tagSystem.HasAllTags(sTagComponent, new HashSet> { AddedTag, StartingTag }), Is.True); + Assert.That(tagSystem.HasAnyTag(sTagComponent, StartingTag, AddedTag), Is.True); Assert.That(tagSystem.HasAnyTag(sTagComponent, AddedTag, StartingTag), Is.True); + Assert.That(tagSystem.HasAnyTag(sTagComponent, new List> { StartingTag, AddedTag }), Is.True); + Assert.That(tagSystem.HasAnyTag(sTagComponent, new List> { AddedTag, StartingTag }), Is.True); + Assert.That(tagSystem.HasAnyTag(sTagComponent, new HashSet> { StartingTag, AddedTag }), Is.True); + Assert.That(tagSystem.HasAnyTag(sTagComponent, new HashSet> { AddedTag, StartingTag }), Is.True); }); Assert.Multiple(() => { // Remove the existing starting tag - Assert.That(tagSystem.RemoveTag(sTagDummy, sTagComponent, StartingTag), Is.True); + Assert.That(tagSystem.RemoveTag(sTagEntity, StartingTag), Is.True); // Remove the existing added tag - Assert.That(tagSystem.RemoveTags(sTagDummy, sTagComponent, AddedTag, AddedTag), Is.True); + Assert.That(tagSystem.RemoveTags(sTagEntity, AddedTag, AddedTag), Is.True); }); Assert.Multiple(() => { // No tags left to remove - Assert.That(tagSystem.RemoveTags(sTagDummy, sTagComponent, new List { StartingTag, AddedTag }), Is.False); + Assert.That(tagSystem.RemoveTags(sTagEntity, new List> { StartingTag, AddedTag }), Is.False); // No tags left in the component Assert.That(sTagComponent.Tags, Is.Empty); }); -#if !DEBUG - return; + // It is run only in DEBUG build, + // as the checks are performed only in DEBUG build. +#if DEBUG + // Has single + Assert.Throws(() => { tagSystem.HasTag(sTagDummy, UnregisteredTag); }); + Assert.Throws(() => { tagSystem.HasTag(sTagComponent, UnregisteredTag); }); + + // HasAny entityUid methods + Assert.Throws(() => { tagSystem.HasAnyTag(sTagDummy, UnregisteredTag); }); + Assert.Throws(() => { tagSystem.HasAnyTag(sTagDummy, UnregisteredTag, UnregisteredTag); }); + Assert.Throws(() => { tagSystem.HasAnyTag(sTagDummy, new List> { UnregisteredTag }); }); + Assert.Throws(() => { tagSystem.HasAnyTag(sTagDummy, new HashSet> { UnregisteredTag }); }); + + // HasAny component methods + Assert.Throws(() => { tagSystem.HasAnyTag(sTagComponent, UnregisteredTag); }); + Assert.Throws(() => { tagSystem.HasAnyTag(sTagComponent, UnregisteredTag, UnregisteredTag); }); + Assert.Throws(() => { tagSystem.HasAnyTag(sTagComponent, new List> { UnregisteredTag }); }); + Assert.Throws(() => { tagSystem.HasAnyTag(sTagComponent, new HashSet> { UnregisteredTag }); }); + + // HasAll entityUid methods + Assert.Throws(() => { tagSystem.HasAllTags(sTagDummy, UnregisteredTag); }); + Assert.Throws(() => { tagSystem.HasAllTags(sTagDummy, UnregisteredTag, UnregisteredTag); }); + Assert.Throws(() => { tagSystem.HasAllTags(sTagDummy, new List> { UnregisteredTag }); }); + Assert.Throws(() => { tagSystem.HasAllTags(sTagDummy, new HashSet> { UnregisteredTag }); }); + + // HasAll component methods + Assert.Throws(() => { tagSystem.HasAllTags(sTagComponent, UnregisteredTag); }); + Assert.Throws(() => { tagSystem.HasAllTags(sTagComponent, UnregisteredTag, UnregisteredTag); }); + Assert.Throws(() => { tagSystem.HasAllTags(sTagComponent, new List> { UnregisteredTag }); }); + Assert.Throws(() => { tagSystem.HasAllTags(sTagComponent, new HashSet> { UnregisteredTag }); }); + + // RemoveTag single + Assert.Throws(() => { tagSystem.RemoveTag(sTagDummy, UnregisteredTag); }); + Assert.Throws(() => { tagSystem.RemoveTag(sTagEntity, UnregisteredTag); }); + + // RemoveTags entityUid methods + Assert.Throws(() => { tagSystem.RemoveTags(sTagDummy, UnregisteredTag); }); + Assert.Throws(() => { tagSystem.RemoveTags(sTagDummy, UnregisteredTag, UnregisteredTag); }); + Assert.Throws(() => { tagSystem.RemoveTags(sTagDummy, new List> { UnregisteredTag }); }); + Assert.Throws(() => { tagSystem.RemoveTags(sTagDummy, new HashSet> { UnregisteredTag }); }); + + // RemoveTags entity methods + Assert.Throws(() => { tagSystem.RemoveTags(sTagEntity, UnregisteredTag); }); + Assert.Throws(() => { tagSystem.RemoveTags(sTagEntity, UnregisteredTag, UnregisteredTag); }); + Assert.Throws(() => { tagSystem.RemoveTags(sTagEntity, new List> { UnregisteredTag }); }); + Assert.Throws(() => { tagSystem.RemoveTags(sTagEntity, new HashSet> { UnregisteredTag }); }); + + // AddTag single + Assert.Throws(() => { tagSystem.AddTag(sTagDummy, UnregisteredTag); }); + Assert.Throws(() => { tagSystem.AddTag(sTagEntity, UnregisteredTag); }); + + // AddTags entityUid methods + Assert.Throws(() => { tagSystem.AddTags(sTagDummy, UnregisteredTag); }); + Assert.Throws(() => { tagSystem.AddTags(sTagDummy, UnregisteredTag, UnregisteredTag); }); + Assert.Throws(() => { tagSystem.AddTags(sTagDummy, new List> { UnregisteredTag }); }); + Assert.Throws(() => { tagSystem.AddTags(sTagDummy, new HashSet> { UnregisteredTag }); }); + + // AddTags entity methods + Assert.Throws(() => { tagSystem.AddTags(sTagEntity, UnregisteredTag); }); + Assert.Throws(() => { tagSystem.AddTags(sTagEntity, UnregisteredTag, UnregisteredTag); }); + Assert.Throws(() => { tagSystem.AddTags(sTagEntity, new List> { UnregisteredTag }); }); + Assert.Throws(() => { tagSystem.AddTags(sTagEntity, new HashSet> { UnregisteredTag }); }); #endif - - // Single - Assert.Throws(() => - { - tagSystem.HasTag(sTagDummy, UnregisteredTag); - }); - Assert.Throws(() => - { - tagSystem.HasTag(sTagComponent, UnregisteredTag); - }); - - // Any - Assert.Throws(() => - { - tagSystem.HasAnyTag(sTagDummy, UnregisteredTag); - }); - Assert.Throws(() => - { - tagSystem.HasAnyTag(sTagComponent, UnregisteredTag); - }); - - // All - Assert.Throws(() => - { - tagSystem.HasAllTags(sTagDummy, UnregisteredTag); - }); - Assert.Throws(() => - { - tagSystem.HasAllTags(sTagComponent, UnregisteredTag); - }); }); await pair.CleanReturnAsync(); } diff --git a/Content.Server/Administration/Toolshed/TagCommand.cs b/Content.Server/Administration/Toolshed/TagCommand.cs index 1af2779766..e1cf53e1b1 100644 --- a/Content.Server/Administration/Toolshed/TagCommand.cs +++ b/Content.Server/Administration/Toolshed/TagCommand.cs @@ -1,6 +1,7 @@ using System.Linq; using Content.Shared.Administration; using Content.Shared.Tag; +using Robust.Shared.Prototypes; using Robust.Shared.Toolshed; using Robust.Shared.Toolshed.Syntax; using Robust.Shared.Toolshed.TypeParsers; @@ -13,14 +14,14 @@ public sealed class TagCommand : ToolshedCommand private TagSystem? _tag; [CommandImplementation("list")] - public IEnumerable List([PipedArgument] IEnumerable ent) + public IEnumerable> List([PipedArgument] IEnumerable ent) { return ent.SelectMany(x => { if (TryComp(x, out var tags)) // Note: Cast is required for C# to figure out the type signature. - return (IEnumerable)tags.Tags; - return Array.Empty(); + return (IEnumerable>)tags.Tags; + return Array.Empty>(); }); } @@ -72,7 +73,7 @@ public sealed class TagCommand : ToolshedCommand ) { _tag ??= GetSys(); - _tag.AddTags(input, @ref.Evaluate(ctx)!); + _tag.AddTags(input, (IEnumerable>)@ref.Evaluate(ctx)!); return input; } @@ -92,7 +93,7 @@ public sealed class TagCommand : ToolshedCommand ) { _tag ??= GetSys(); - _tag.RemoveTags(input, @ref.Evaluate(ctx)!); + _tag.RemoveTags(input, (IEnumerable>)@ref.Evaluate(ctx)!); return input; } diff --git a/Content.Server/Mech/Systems/MechAssemblySystem.cs b/Content.Server/Mech/Systems/MechAssemblySystem.cs index e5b7bfaac3..c1fff819b4 100644 --- a/Content.Server/Mech/Systems/MechAssemblySystem.cs +++ b/Content.Server/Mech/Systems/MechAssemblySystem.cs @@ -14,6 +14,7 @@ namespace Content.Server.Mech.Systems; public sealed class MechAssemblySystem : EntitySystem { [Dependency] private readonly ContainerSystem _container = default!; + [Dependency] private readonly TagSystem _tag = default!; /// public override void Initialize() @@ -44,7 +45,7 @@ public sealed class MechAssemblySystem : EntitySystem foreach (var (tag, val) in component.RequiredParts) { - if (!val && tagComp.Tags.Contains(tag)) + if (!val && _tag.HasTag(tagComp, tag)) { component.RequiredParts[tag] = true; _container.Insert(args.Used, component.PartsContainer); diff --git a/Content.Server/Procedural/DungeonJob.PostGen.cs b/Content.Server/Procedural/DungeonJob.PostGen.cs index b326bcc378..cb9e64f04e 100644 --- a/Content.Server/Procedural/DungeonJob.PostGen.cs +++ b/Content.Server/Procedural/DungeonJob.PostGen.cs @@ -13,6 +13,7 @@ using Robust.Shared.Collections; using Robust.Shared.Map; using Robust.Shared.Map.Components; using Robust.Shared.Physics.Components; +using Robust.Shared.Prototypes; using Robust.Shared.Random; using Robust.Shared.Utility; @@ -24,13 +25,15 @@ public sealed partial class DungeonJob * Run after the main dungeon generation */ + private static readonly ProtoId WallTag = "Wall"; + private bool HasWall(MapGridComponent grid, Vector2i tile) { var anchored = _maps.GetAnchoredEntitiesEnumerator(_gridUid, _grid, tile); while (anchored.MoveNext(out var uid)) { - if (_tagQuery.TryGetComponent(uid, out var tagComp) && tagComp.Tags.Contains("Wall")) + if (_tag.HasTag(uid.Value, WallTag)) return true; } diff --git a/Content.Server/Procedural/DungeonJob.cs b/Content.Server/Procedural/DungeonJob.cs index 8fecf1c9e8..bf2822ff42 100644 --- a/Content.Server/Procedural/DungeonJob.cs +++ b/Content.Server/Procedural/DungeonJob.cs @@ -28,10 +28,10 @@ public sealed partial class DungeonJob : Job private readonly DecalSystem _decals; private readonly DungeonSystem _dungeon; private readonly EntityLookupSystem _lookup; + private readonly TagSystem _tag; private readonly TileSystem _tile; private readonly SharedMapSystem _maps; private readonly SharedTransformSystem _transform; - private EntityQuery _tagQuery; private readonly DungeonConfigPrototype _gen; private readonly int _seed; @@ -53,6 +53,7 @@ public sealed partial class DungeonJob : Job DecalSystem decals, DungeonSystem dungeon, EntityLookupSystem lookup, + TagSystem tag, TileSystem tile, SharedTransformSystem transform, DungeonConfigPrototype gen, @@ -72,10 +73,10 @@ public sealed partial class DungeonJob : Job _decals = decals; _dungeon = dungeon; _lookup = lookup; + _tag = tag; _tile = tile; _maps = _entManager.System(); _transform = transform; - _tagQuery = _entManager.GetEntityQuery(); _gen = gen; _grid = grid; diff --git a/Content.Server/Procedural/DungeonSystem.cs b/Content.Server/Procedural/DungeonSystem.cs index 069508bcbb..36009896a2 100644 --- a/Content.Server/Procedural/DungeonSystem.cs +++ b/Content.Server/Procedural/DungeonSystem.cs @@ -10,6 +10,7 @@ using Content.Shared.GameTicking; using Content.Shared.Maps; using Content.Shared.Physics; using Content.Shared.Procedural; +using Content.Shared.Tag; using Robust.Server.GameObjects; using Robust.Shared.Configuration; using Robust.Shared.Console; @@ -31,6 +32,7 @@ public sealed partial class DungeonSystem : SharedDungeonSystem [Dependency] private readonly AnchorableSystem _anchorable = default!; [Dependency] private readonly DecalSystem _decals = default!; [Dependency] private readonly EntityLookupSystem _lookup = default!; + [Dependency] private readonly TagSystem _tag = default!; [Dependency] private readonly TileSystem _tile = default!; [Dependency] private readonly MapLoaderSystem _loader = default!; [Dependency] private readonly SharedMapSystem _maps = default!; @@ -199,6 +201,7 @@ public sealed partial class DungeonSystem : SharedDungeonSystem _decals, this, _lookup, + _tag, _tile, _transform, gen, @@ -231,6 +234,7 @@ public sealed partial class DungeonSystem : SharedDungeonSystem _decals, this, _lookup, + _tag, _tile, _transform, gen, diff --git a/Content.Server/Revenant/EntitySystems/RevenantSystem.Abilities.cs b/Content.Server/Revenant/EntitySystems/RevenantSystem.Abilities.cs index fd7d15ea5d..670c64577a 100644 --- a/Content.Server/Revenant/EntitySystems/RevenantSystem.Abilities.cs +++ b/Content.Server/Revenant/EntitySystems/RevenantSystem.Abilities.cs @@ -246,7 +246,7 @@ public sealed partial class RevenantSystem foreach (var ent in lookup) { //break windows - if (tags.HasComponent(ent) && _tag.HasAnyTag(ent, "Window")) + if (tags.HasComponent(ent) && _tag.HasTag(ent, "Window")) { //hardcoded damage specifiers til i die. var dspec = new DamageSpecifier(); diff --git a/Content.Server/Spreader/SpreaderSystem.cs b/Content.Server/Spreader/SpreaderSystem.cs index fe14d86aa1..7de8a43d35 100644 --- a/Content.Server/Spreader/SpreaderSystem.cs +++ b/Content.Server/Spreader/SpreaderSystem.cs @@ -21,6 +21,7 @@ public sealed class SpreaderSystem : EntitySystem [Dependency] private readonly IPrototypeManager _prototype = default!; [Dependency] private readonly IRobustRandom _robustRandom = default!; [Dependency] private readonly SharedMapSystem _map = default!; + [Dependency] private readonly TagSystem _tag = default!; /// /// Cached maximum number of updates per spreader prototype. This is applied per-grid. @@ -37,8 +38,7 @@ public sealed class SpreaderSystem : EntitySystem public const float SpreadCooldownSeconds = 1; - [ValidatePrototypeId] - private const string IgnoredTag = "SpreaderIgnore"; + private static readonly ProtoId IgnoredTag = "SpreaderIgnore"; /// public override void Initialize() @@ -189,7 +189,6 @@ public sealed class SpreaderSystem : EntitySystem var airtightQuery = GetEntityQuery(); var dockQuery = GetEntityQuery(); var xformQuery = GetEntityQuery(); - var tagQuery = GetEntityQuery(); var blockedAtmosDirs = AtmosDirection.Invalid; // Due to docking ports they may not necessarily be opposite directions. @@ -212,7 +211,7 @@ public sealed class SpreaderSystem : EntitySystem // If we're on a blocked tile work out which directions we can go. if (!airtightQuery.TryGetComponent(ent, out var airtight) || !airtight.AirBlocked || - tagQuery.TryGetComponent(ent, out var tags) && tags.Tags.Contains(IgnoredTag)) + _tag.HasTag(ent.Value, IgnoredTag)) { continue; } @@ -250,8 +249,7 @@ public sealed class SpreaderSystem : EntitySystem while (directionEnumerator.MoveNext(out var ent)) { - if (!airtightQuery.TryGetComponent(ent, out var airtight) || !airtight.AirBlocked || - tagQuery.TryGetComponent(ent, out var tags) && tags.Tags.Contains(IgnoredTag)) + if (!airtightQuery.TryGetComponent(ent, out var airtight) || !airtight.AirBlocked || _tag.HasTag(ent.Value, IgnoredTag)) { continue; } diff --git a/Content.Shared/Clothing/EntitySystems/SharedChameleonClothingSystem.cs b/Content.Shared/Clothing/EntitySystems/SharedChameleonClothingSystem.cs index a447a54df1..fced03bfab 100644 --- a/Content.Shared/Clothing/EntitySystems/SharedChameleonClothingSystem.cs +++ b/Content.Shared/Clothing/EntitySystems/SharedChameleonClothingSystem.cs @@ -12,9 +12,10 @@ public abstract class SharedChameleonClothingSystem : EntitySystem { [Dependency] private readonly IComponentFactory _factory = default!; [Dependency] private readonly IPrototypeManager _proto = default!; - [Dependency] private readonly SharedItemSystem _itemSystem = default!; [Dependency] private readonly ClothingSystem _clothingSystem = default!; [Dependency] private readonly MetaDataSystem _metaData = default!; + [Dependency] private readonly SharedItemSystem _itemSystem = default!; + [Dependency] private readonly TagSystem _tag = default!; public override void Initialize() { @@ -81,7 +82,7 @@ public abstract class SharedChameleonClothingSystem : EntitySystem return false; // check if it is marked as valid chameleon target - if (!proto.TryGetComponent(out TagComponent? tags, _factory) || !tags.Tags.Contains("WhitelistChameleon")) + if (!proto.TryGetComponent(out TagComponent? tag, _factory) || !_tag.HasTag(tag, "WhitelistChameleon")) return false; // check if it's valid clothing diff --git a/Content.Shared/Construction/Conditions/NoWindowsInTile.cs b/Content.Shared/Construction/Conditions/NoWindowsInTile.cs index be6bc2cfac..3ae3b59362 100644 --- a/Content.Shared/Construction/Conditions/NoWindowsInTile.cs +++ b/Content.Shared/Construction/Conditions/NoWindowsInTile.cs @@ -12,13 +12,12 @@ namespace Content.Shared.Construction.Conditions public bool Condition(EntityUid user, EntityCoordinates location, Direction direction) { var entManager = IoCManager.Resolve(); - var tagQuery = entManager.GetEntityQuery(); var sysMan = entManager.EntitySysManager; var tagSystem = sysMan.GetEntitySystem(); foreach (var entity in location.GetEntitiesInTile(LookupFlags.Static)) { - if (tagSystem.HasTag(entity, "Window", tagQuery)) + if (tagSystem.HasTag(entity, "Window")) return false; } diff --git a/Content.Shared/Construction/EntitySystems/AnchorableSystem.cs b/Content.Shared/Construction/EntitySystems/AnchorableSystem.cs index c041cf1ba0..e9ef053f62 100644 --- a/Content.Shared/Construction/EntitySystems/AnchorableSystem.cs +++ b/Content.Shared/Construction/EntitySystems/AnchorableSystem.cs @@ -15,6 +15,7 @@ using Robust.Shared.Map; using Robust.Shared.Map.Components; using Robust.Shared.Physics.Components; using Content.Shared.Tag; +using Robust.Shared.Prototypes; using Robust.Shared.Serialization; using Robust.Shared.Utility; using SharedToolSystem = Content.Shared.Tools.Systems.SharedToolSystem; @@ -32,16 +33,14 @@ public sealed partial class AnchorableSystem : EntitySystem [Dependency] private readonly TagSystem _tagSystem = default!; private EntityQuery _physicsQuery; - private EntityQuery _tagQuery; - public const string Unstackable = "Unstackable"; + public readonly ProtoId Unstackable = "Unstackable"; public override void Initialize() { base.Initialize(); _physicsQuery = GetEntityQuery(); - _tagQuery = GetEntityQuery(); SubscribeLocalEvent(OnInteractUsing, before: new[] { typeof(ItemSlotsSystem) }, after: new[] { typeof(SharedConstructionSystem) }); @@ -312,7 +311,7 @@ public sealed partial class AnchorableSystem : EntitySystem DebugTools.Assert(!Transform(uid).Anchored); // If we are unstackable, iterate through any other entities anchored on the current square - return _tagSystem.HasTag(uid, Unstackable, _tagQuery) && AnyUnstackablesAnchoredAt(location); + return _tagSystem.HasTag(uid, Unstackable) && AnyUnstackablesAnchoredAt(location); } public bool AnyUnstackablesAnchoredAt(EntityCoordinates location) @@ -327,10 +326,8 @@ public sealed partial class AnchorableSystem : EntitySystem while (enumerator.MoveNext(out var entity)) { // If we find another unstackable here, return true. - if (_tagSystem.HasTag(entity.Value, Unstackable, _tagQuery)) - { + if (_tagSystem.HasTag(entity.Value, Unstackable)) return true; - } } return false; diff --git a/Content.Shared/Construction/Steps/MultipleTagsConstructionGraphStep.cs b/Content.Shared/Construction/Steps/MultipleTagsConstructionGraphStep.cs index 668952dac2..07ba46946a 100644 --- a/Content.Shared/Construction/Steps/MultipleTagsConstructionGraphStep.cs +++ b/Content.Shared/Construction/Steps/MultipleTagsConstructionGraphStep.cs @@ -1,14 +1,15 @@ using Content.Shared.Tag; +using Robust.Shared.Prototypes; namespace Content.Shared.Construction.Steps { public sealed partial class MultipleTagsConstructionGraphStep : ArbitraryInsertConstructionGraphStep { [DataField("allTags")] - private List? _allTags; + private List>? _allTags; [DataField("anyTags")] - private List? _anyTags; + private List>? _anyTags; private static bool IsNullOrEmpty(ICollection? list) { @@ -21,16 +22,12 @@ namespace Content.Shared.Construction.Steps if (IsNullOrEmpty(_allTags) && IsNullOrEmpty(_anyTags)) return false; // Step is somehow invalid, we return. - // No tags at all. - if (!entityManager.TryGetComponent(uid, out TagComponent? tags)) - return false; - var tagSystem = entityManager.EntitySysManager.GetEntitySystem(); - if (_allTags != null && !tagSystem.HasAllTags(tags, _allTags)) + if (_allTags != null && !tagSystem.HasAllTags(uid, _allTags)) return false; // We don't have all the tags needed. - if (_anyTags != null && !tagSystem.HasAnyTag(tags, _anyTags)) + if (_anyTags != null && !tagSystem.HasAnyTag(uid, _anyTags)) return false; // We don't have any of the tags needed. // This entity is valid! diff --git a/Content.Shared/Pinpointer/SharedNavMapSystem.cs b/Content.Shared/Pinpointer/SharedNavMapSystem.cs index 7c12321b5d..3ced5f3c9e 100644 --- a/Content.Shared/Pinpointer/SharedNavMapSystem.cs +++ b/Content.Shared/Pinpointer/SharedNavMapSystem.cs @@ -3,6 +3,7 @@ using System.Numerics; using System.Runtime.CompilerServices; using Content.Shared.Tag; using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; using Robust.Shared.Serialization; using Robust.Shared.Timing; using Robust.Shared.Utility; @@ -24,7 +25,7 @@ public abstract class SharedNavMapSystem : EntitySystem [Robust.Shared.IoC.Dependency] private readonly TagSystem _tagSystem = default!; - private readonly string[] _wallTags = ["Wall", "Window"]; + private static readonly ProtoId[] WallTags = {"Wall", "Window"}; private EntityQuery _doorQuery; public override void Initialize() @@ -58,7 +59,7 @@ public abstract class SharedNavMapSystem : EntitySystem if (_doorQuery.HasComp(uid)) return NavMapChunkType.Airlock; - if (_tagSystem.HasAnyTag(uid, _wallTags)) + if (_tagSystem.HasAnyTag(uid, WallTags)) return NavMapChunkType.Wall; return NavMapChunkType.Invalid; diff --git a/Content.Shared/Tag/TagComponent.cs b/Content.Shared/Tag/TagComponent.cs index b5b8a48a44..ad4240ba06 100644 --- a/Content.Shared/Tag/TagComponent.cs +++ b/Content.Shared/Tag/TagComponent.cs @@ -1,13 +1,11 @@ using Robust.Shared.GameStates; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Set; +using Robust.Shared.Prototypes; -namespace Content.Shared.Tag +namespace Content.Shared.Tag; + +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState, Access(typeof(TagSystem))] +public sealed partial class TagComponent : Component { - [RegisterComponent, NetworkedComponent, Access(typeof(TagSystem))] - public sealed partial class TagComponent : Component - { - [DataField("tags", customTypeSerializer: typeof(PrototypeIdHashSetSerializer))] - [Access(typeof(TagSystem), Other = AccessPermissions.ReadExecute)] // FIXME Friends - public HashSet Tags = new(); - } + [DataField, ViewVariables, AutoNetworkedField] + public HashSet> Tags = new(); } diff --git a/Content.Shared/Tag/TagComponentState.cs b/Content.Shared/Tag/TagComponentState.cs deleted file mode 100644 index 9919aba108..0000000000 --- a/Content.Shared/Tag/TagComponentState.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Robust.Shared.Serialization; - -namespace Content.Shared.Tag -{ - [Serializable, NetSerializable] - public sealed class TagComponentState : ComponentState - { - public TagComponentState(string[] tags) - { - Tags = tags; - } - - public string[] Tags { get; } - } -} diff --git a/Content.Shared/Tag/TagPrototype.cs b/Content.Shared/Tag/TagPrototype.cs index 2a06e22cf9..97f8c1af7d 100644 --- a/Content.Shared/Tag/TagPrototype.cs +++ b/Content.Shared/Tag/TagPrototype.cs @@ -1,17 +1,15 @@ using Robust.Shared.Prototypes; -namespace Content.Shared.Tag +namespace Content.Shared.Tag; + +/// +/// Prototype representing a tag in YAML. +/// Meant to only have an ID property, as that is the only thing that +/// gets saved in TagComponent. +/// +[Prototype("Tag")] +public sealed partial class TagPrototype : IPrototype { - /// - /// Prototype representing a tag in YAML. - /// Meant to only have an ID property, as that is the only thing that - /// gets saved in TagComponent. - /// - [Prototype("Tag")] - public sealed partial class TagPrototype : IPrototype - { - [ViewVariables] - [IdDataField] - public string ID { get; private set; } = default!; - } + [IdDataField, ViewVariables] + public string ID { get; } = string.Empty; } diff --git a/Content.Shared/Tag/TagSystem.cs b/Content.Shared/Tag/TagSystem.cs index 7bcb887a41..f1f620a694 100644 --- a/Content.Shared/Tag/TagSystem.cs +++ b/Content.Shared/Tag/TagSystem.cs @@ -1,11 +1,19 @@ -using System.Diagnostics; -using System.Linq; -using Robust.Shared.GameStates; using Robust.Shared.Prototypes; using Robust.Shared.Utility; namespace Content.Shared.Tag; +/// +/// The system that is responsible for working with tags. +/// Checking the existence of the only happens in DEBUG builds, +/// to improve performance, so don't forget to check it. +/// +/// +/// The methods to add or remove a list of tags have only an implementation with the type, +/// it's not much, but it takes away performance, +/// if you need to use them often, it's better to make a proper implementation, +/// you can read more HERE. +/// public sealed class TagSystem : EntitySystem { [Dependency] private readonly IPrototypeManager _proto = default!; @@ -15,685 +23,693 @@ public sealed class TagSystem : EntitySystem public override void Initialize() { base.Initialize(); + _tagQuery = GetEntityQuery(); - SubscribeLocalEvent(OnTagGetState); - SubscribeLocalEvent(OnTagHandleState); #if DEBUG SubscribeLocalEvent(OnTagInit); +#endif } +#if DEBUG private void OnTagInit(EntityUid uid, TagComponent component, ComponentInit args) { foreach (var tag in component.Tags) { AssertValidTag(tag); } + } #endif + + /// + /// Tries to add a tag to an entity if the tag doesn't already exist. + /// + /// + /// true if it was added, false otherwise even if it already existed. + /// + /// + /// Thrown if no exists with the given id. + /// + public bool AddTag(EntityUid entityUid, ProtoId tag) + { + return AddTag((entityUid, EnsureComp(entityUid)), tag); } - - private void OnTagHandleState(EntityUid uid, TagComponent component, ref ComponentHandleState args) + /// + /// Tries to add the given tags to an entity if the tags don't already exist. + /// + /// + /// true if any tags were added, false otherwise even if they all already existed. + /// + /// + /// Thrown if one of the ids represents an unregistered . + /// + public bool AddTags(EntityUid entityUid, params ProtoId[] tags) { - if (args.Current is not TagComponentState state) - return; + return AddTags(entityUid, (IEnumerable>)tags); + } - component.Tags.Clear(); + /// + /// Tries to add the given tags to an entity if the tags don't already exist. + /// + /// + /// true if any tags were added, false otherwise even if they all already existed. + /// + /// + /// Thrown if one of the ids represents an unregistered . + /// + public bool AddTags(EntityUid entityUid, IEnumerable> tags) + { + return AddTags((entityUid, EnsureComp(entityUid)), tags); + } - foreach (var tag in state.Tags) + /// + /// Tries to add a tag to an entity if it has a + /// and the tag doesn't already exist. + /// + /// + /// true if it was added, false otherwise even if it already existed. + /// + /// + /// Thrown if no exists with the given id. + /// + public bool TryAddTag(EntityUid entityUid, ProtoId tag) + { + return _tagQuery.TryComp(entityUid, out var component) && + AddTag((entityUid, component), tag); + } + + /// + /// Tries to add the given tags to an entity if it has a + /// and the tags don't already exist. + /// + /// + /// true if any tags were added, false otherwise even if they all already existed. + /// + /// + /// Thrown if one of the ids represents an unregistered . + /// + public bool TryAddTags(EntityUid entityUid, params ProtoId[] tags) + { + return TryAddTags(entityUid, (IEnumerable>)tags); + } + + /// + /// Tries to add the given tags to an entity if it has a + /// and the tags don't already exist. + /// + /// + /// true if any tags were added, false otherwise even if they all already existed. + /// + /// + /// Thrown if one of the ids represents an unregistered . + /// + public bool TryAddTags(EntityUid entityUid, IEnumerable> tags) + { + return _tagQuery.TryComp(entityUid, out var component) && + AddTags((entityUid, component), tags); + } + + /// + /// Checks if a tag has been added to an entity. + /// + /// + /// true if it exists, false otherwise. + /// + /// + /// Thrown if no exists with the given id. + /// + public bool HasTag(EntityUid entityUid, ProtoId tag) + { + return _tagQuery.TryComp(entityUid, out var component) && + HasTag(component, tag); + } + + /// + /// Checks if a tag has been added to an entity. + /// + /// + /// true if it exists, false otherwise. + /// + /// + /// Thrown if no exists with the given id. + /// + public bool HasAllTags(EntityUid entityUid, ProtoId tag) => + HasTag(entityUid, tag); + + /// + /// Checks if all of the given tags have been added to an entity. + /// + /// + /// true if they all exist, false otherwise. + /// + /// + /// Thrown if one of the ids represents an unregistered . + /// + public bool HasAllTags(EntityUid entityUid, params ProtoId[] tags) + { + return _tagQuery.TryComp(entityUid, out var component) && + HasAllTags(component, tags); + } + + /// + /// Checks if all of the given tags have been added to an entity. + /// + /// + /// true if they all exist, false otherwise. + /// + /// + /// Thrown if one of the ids represents an unregistered . + /// + public bool HasAllTags(EntityUid entityUid, HashSet> tags) + { + return _tagQuery.TryComp(entityUid, out var component) && + HasAllTags(component, tags); + } + + /// + /// Checks if all of the given tags have been added to an entity. + /// + /// + /// true if they all exist, false otherwise. + /// + /// + /// Thrown if one of the ids represents an unregistered . + /// + public bool HasAllTags(EntityUid entityUid, List> tags) + { + return _tagQuery.TryComp(entityUid, out var component) && + HasAllTags(component, tags); + } + + /// + /// Checks if all of the given tags have been added to an entity. + /// + /// + /// true if they all exist, false otherwise. + /// + /// + /// Thrown if one of the ids represents an unregistered . + /// + public bool HasAllTags(EntityUid entityUid, IEnumerable> tags) + { + return _tagQuery.TryComp(entityUid, out var component) && + HasAllTags(component, tags); + } + + /// + /// Checks if a tag has been added to an entity. + /// + /// + /// true if it exists, false otherwise. + /// + /// + /// Thrown if no exists with the given id. + /// + public bool HasAnyTag(EntityUid entityUid, ProtoId tag) => + HasTag(entityUid, tag); + + /// + /// Checks if any of the given tags have been added to an entity. + /// + /// + /// true if any of them exist, false otherwise. + /// + /// + /// Thrown if one of the ids represents an unregistered . + /// + public bool HasAnyTag(EntityUid entityUid, params ProtoId[] tags) + { + return _tagQuery.TryComp(entityUid, out var component) && + HasAnyTag(component, tags); + } + + /// + /// Checks if any of the given tags have been added to an entity. + /// + /// + /// true if any of them exist, false otherwise. + /// + /// + /// Thrown if one of the ids represents an unregistered . + /// + public bool HasAnyTag(EntityUid entityUid, HashSet> tags) + { + return _tagQuery.TryComp(entityUid, out var component) && + HasAnyTag(component, tags); + } + + /// + /// Checks if any of the given tags have been added to an entity. + /// + /// + /// true if any of them exist, false otherwise. + /// + /// + /// Thrown if one of the ids represents an unregistered . + /// + public bool HasAnyTag(EntityUid entityUid, List> tags) + { + return _tagQuery.TryComp(entityUid, out var component) && + HasAnyTag(component, tags); + } + + /// + /// Checks if any of the given tags have been added to an entity. + /// + /// + /// true if any of them exist, false otherwise. + /// + /// + /// Thrown if one of the ids represents an unregistered . + /// + public bool HasAnyTag(EntityUid entityUid, IEnumerable> tags) + { + return _tagQuery.TryComp(entityUid, out var component) && + HasAnyTag(component, tags); + } + + /// + /// Checks if a tag has been added to an component. + /// + /// + /// true if it exists, false otherwise. + /// + /// + /// Thrown if no exists with the given id. + /// + public bool HasTag(TagComponent component, ProtoId tag) + { +#if DEBUG + AssertValidTag(tag); +#endif + return component.Tags.Contains(tag); + } + + /// + /// Checks if a tag has been added to an component. + /// + /// + /// true if it exists, false otherwise. + /// + /// + /// Thrown if no exists with the given id. + /// + public bool HasAllTags(TagComponent component, ProtoId tag) => + HasTag(component, tag); + + /// + /// Checks if all of the given tags have been added to an component. + /// + /// + /// true if they all exist, false otherwise. + /// + /// + /// Thrown if one of the ids represents an unregistered . + /// + public bool HasAllTags(TagComponent component, params ProtoId[] tags) + { + foreach (var tag in tags) { +#if DEBUG AssertValidTag(tag); - component.Tags.Add(tag); +#endif + if (!component.Tags.Contains(tag)) + return false; } + + return true; } - private static void OnTagGetState(EntityUid uid, TagComponent component, ref ComponentGetState args) + /// + /// Checks if all of the given tags have been added to an component. + /// + /// + /// true if they all exist, false otherwise. + /// + /// + /// Thrown if one of the ids represents an unregistered . + /// + public bool HasAllTagsArray(TagComponent component, ProtoId[] tags) { - var tags = new string[component.Tags.Count]; - var i = 0; - - foreach (var tag in component.Tags) + foreach (var tag in tags) { - tags[i] = tag; - i++; +#if DEBUG + AssertValidTag(tag); +#endif + if (!component.Tags.Contains(tag)) + return false; } - args.State = new TagComponentState(tags); + return true; + } + + /// + /// Checks if all of the given tags have been added to an component. + /// + /// + /// true if they all exist, false otherwise. + /// + /// + /// Thrown if one of the ids represents an unregistered . + /// + public bool HasAllTags(TagComponent component, List> tags) + { + foreach (var tag in tags) + { +#if DEBUG + AssertValidTag(tag); +#endif + if (!component.Tags.Contains(tag)) + return false; + } + + return true; + } + + /// + /// Checks if all of the given tags have been added to an component. + /// + /// + /// true if they all exist, false otherwise. + /// + /// + /// Thrown if one of the ids represents an unregistered . + /// + public bool HasAllTags(TagComponent component, HashSet> tags) + { + foreach (var tag in tags) + { +#if DEBUG + AssertValidTag(tag); +#endif + if (!component.Tags.Contains(tag)) + return false; + } + + return true; + } + + /// + /// Checks if all of the given tags have been added to an component. + /// + /// + /// true if they all exist, false otherwise. + /// + /// + /// Thrown if one of the ids represents an unregistered . + /// + public bool HasAllTags(TagComponent component, IEnumerable> tags) + { + foreach (var tag in tags) + { +#if DEBUG + AssertValidTag(tag); +#endif + if (!component.Tags.Contains(tag)) + return false; + } + + return true; + } + + /// + /// Checks if a tag has been added to an component. + /// + /// + /// true if it exists, false otherwise. + /// + /// + /// Thrown if no exists with the given id. + /// + public bool HasAnyTag(TagComponent component, ProtoId tag) => + HasTag(component, tag); + + /// + /// Checks if any of the given tags have been added to an component. + /// + /// + /// true if any of them exist, false otherwise. + /// + /// + /// Thrown if one of the ids represents an unregistered . + /// + public bool HasAnyTag(TagComponent component, params ProtoId[] tags) + { + foreach (var tag in tags) + { +#if DEBUG + AssertValidTag(tag); +#endif + if (component.Tags.Contains(tag)) + return true; + } + + return false; + } + + /// + /// Checks if any of the given tags have been added to an component. + /// + /// + /// true if any of them exist, false otherwise. + /// + /// + /// Thrown if one of the ids represents an unregistered . + /// + public bool HasAnyTag(TagComponent component, HashSet> tags) + { + foreach (var tag in tags) + { +#if DEBUG + AssertValidTag(tag); +#endif + if (component.Tags.Contains(tag)) + return true; + } + + return false; + } + + /// + /// Checks if any of the given tags have been added to an component. + /// + /// + /// true if any of them exist, false otherwise. + /// + /// + /// Thrown if one of the ids represents an unregistered . + /// + public bool HasAnyTag(TagComponent component, List> tags) + { + foreach (var tag in tags) + { +#if DEBUG + AssertValidTag(tag); +#endif + if (component.Tags.Contains(tag)) + return true; + } + + return false; + } + + /// + /// Checks if any of the given tags have been added to an component. + /// + /// + /// true if any of them exist, false otherwise. + /// + /// + /// Thrown if one of the ids represents an unregistered . + /// + public bool HasAnyTag(TagComponent component, IEnumerable> tags) + { + foreach (var tag in tags) + { +#if DEBUG + AssertValidTag(tag); +#endif + if (component.Tags.Contains(tag)) + return true; + } + + return false; + } + + /// + /// Tries to remove a tag from an entity if it exists. + /// + /// + /// true if it was removed, false otherwise even if it didn't exist. + /// + /// + /// Thrown if no exists with the given id. + /// + public bool RemoveTag(EntityUid entityUid, ProtoId tag) + { + return _tagQuery.TryComp(entityUid, out var component) && + RemoveTag((entityUid, component), tag); + } + + /// + /// Tries to remove a tag from an entity if it exists. + /// + /// + /// true if it was removed, false otherwise even if it didn't exist. + /// + /// + /// Thrown if one of the ids represents an unregistered . + /// + public bool RemoveTags(EntityUid entityUid, params ProtoId[] tags) + { + return RemoveTags(entityUid, (IEnumerable>)tags); + } + + /// + /// Tries to remove a tag from an entity if it exists. + /// + /// + /// true if it was removed, false otherwise even if it didn't exist. + /// + /// + /// Thrown if one of the ids represents an unregistered . + /// + public bool RemoveTags(EntityUid entityUid, IEnumerable> tags) + { + return _tagQuery.TryComp(entityUid, out var component) && + RemoveTags((entityUid, component), tags); + } + + /// + /// Tries to add a tag if it doesn't already exist. + /// + /// + /// true if it was added, false if it already existed. + /// + /// + /// Thrown if no exists with the given id. + /// + public bool AddTag(Entity entity, ProtoId tag) + { +#if DEBUG + AssertValidTag(tag); +#endif + if (!entity.Comp.Tags.Add(tag)) + return false; + + Dirty(entity); + return true; + } + + /// + /// Tries to add the given tags if they don't already exist. + /// + /// + /// true if any tags were added, false if they all already existed. + /// + /// + /// Thrown if one of the ids represents an unregistered . + /// + public bool AddTags(Entity entity, params ProtoId[] tags) + { + return AddTags(entity, (IEnumerable>)tags); + } + + /// + /// Tries to add the given tags if they don't already exist. + /// + /// + /// true if any tags were added, false if they all already existed. + /// + /// + /// Thrown if one of the ids represents an unregistered . + /// + public bool AddTags(Entity entity, IEnumerable> tags) + { + var update = false; + foreach (var tag in tags) + { +#if DEBUG + AssertValidTag(tag); +#endif + if (entity.Comp.Tags.Add(tag) && !update) + update = true; + } + + if (!update) + return false; + + Dirty(entity); + return true; + } + + /// + /// Tries to remove a tag if it exists. + /// + /// + /// true if it was removed, false otherwise even if it didn't exist. + /// + /// + /// Thrown if no exists with the given id. + /// + public bool RemoveTag(Entity entity, ProtoId tag) + { +#if DEBUG + AssertValidTag(tag); +#endif + + if (!entity.Comp.Tags.Remove(tag)) + return false; + + Dirty(entity); + return true; + } + + /// + /// Tries to remove all of the given tags if they exist. + /// + /// + /// true if any tag was removed, false otherwise. + /// + /// + /// Thrown if one of the ids represents an unregistered . + /// + public bool RemoveTags(Entity entity, params ProtoId[] tags) + { + return RemoveTags(entity, (IEnumerable>)tags); + } + + /// + /// Tries to remove all of the given tags if they exist. + /// + /// + /// true if any tag was removed, false otherwise. + /// + /// + /// Thrown if one of the ids represents an unregistered . + /// + public bool RemoveTags(Entity entity, IEnumerable> tags) + { + var update = false; + foreach (var tag in tags) + { +#if DEBUG + AssertValidTag(tag); +#endif + if (entity.Comp.Tags.Remove(tag) && !update) + update = true; + } + + if (!update) + return false; + + Dirty(entity); + return true; } private void AssertValidTag(string id) { DebugTools.Assert(_proto.HasIndex(id), $"Unknown tag: {id}"); } - - /// - /// Tries to add a tag to an entity if the tag doesn't already exist. - /// - /// The entity to add the tag to. - /// The tag to add. - /// - /// true if it was added, false otherwise even if it already existed. - /// - /// - /// Thrown if no exists with the given id. - /// - public bool AddTag(EntityUid entity, string id) - { - return AddTag(entity, EnsureComp(entity), id); - } - - /// - /// Tries to add the given tags to an entity if the tags don't already exist. - /// - /// The entity to add the tag to. - /// The tags to add. - /// - /// true if any tags were added, false otherwise even if they all already existed. - /// - /// - /// Thrown if one of the ids represents an unregistered . - /// - public bool AddTags(EntityUid entity, params string[] ids) - { - return AddTags(entity, EnsureComp(entity), ids); - } - - /// - /// Tries to add the given tags to an entity if the tags don't already exist. - /// - /// The entity to add the tag to. - /// The tags to add. - /// - /// true if any tags were added, false otherwise even if they all already existed. - /// - /// - /// Thrown if one of the ids represents an unregistered . - /// - public bool AddTags(EntityUid entity, IEnumerable ids) - { - return AddTags(entity, EnsureComp(entity), ids); - } - - /// - /// Tries to add a tag to an entity if it has a - /// and the tag doesn't already exist. - /// - /// The entity to add the tag to. - /// The tag to add. - /// - /// true if it was added, false otherwise even if it already existed. - /// - /// - /// Thrown if no exists with the given id. - /// - public bool TryAddTag(EntityUid entity, string id) - { - return _tagQuery.TryComp(entity, out var component) && - AddTag(entity, component, id); - } - - /// - /// Tries to add the given tags to an entity if it has a - /// and the tags don't already exist. - /// - /// The entity to add the tag to. - /// The tags to add. - /// - /// true if any tags were added, false otherwise even if they all already existed. - /// - /// - /// Thrown if one of the ids represents an unregistered . - /// - public bool TryAddTags(EntityUid entity, params string[] ids) - { - return _tagQuery.TryComp(entity, out var component) && - AddTags(entity, component, ids); - } - - /// - /// Tries to add the given tags to an entity if it has a - /// and the tags don't already exist. - /// - /// The entity to add the tag to. - /// The tags to add. - /// - /// true if any tags were added, false otherwise even if they all already existed. - /// - /// - /// Thrown if one of the ids represents an unregistered . - /// - public bool TryAddTags(EntityUid entity, IEnumerable ids) - { - return _tagQuery.TryComp(entity, out var component) && - AddTags(entity, component, ids); - } - - /// - /// Checks if a tag has been added to an entity. - /// - /// The entity to check. - /// The tag to check for. - /// true if it exists, false otherwise. - /// - /// Thrown if no exists with the given id. - /// - public bool HasTag(EntityUid entity, string id) - { - return _tagQuery.TryComp(entity, out var component) && - HasTag(component, id); - } - - /// - /// Checks if a tag has been added to an entity. - /// - [Obsolete] - public bool HasTag(EntityUid entity, string id, EntityQuery tagQuery) - { - return tagQuery.TryGetComponent(entity, out var component) && - HasTag(component, id); - } - - /// - /// Checks if all of the given tags have been added to an entity. - /// - /// The entity to check. - /// The tags to check for. - /// true if they all exist, false otherwise. - /// - /// Thrown if one of the ids represents an unregistered . - /// - public bool HasAllTags(EntityUid entity, string id) => HasTag(entity, id); - - /// - /// Checks if all of the given tags have been added to an entity. - /// - /// The entity to check. - /// The tags to check for. - /// true if they all exist, false otherwise. - /// - /// Thrown if one of the ids represents an unregistered . - /// - public bool HasAllTags(EntityUid entity, List ids) - { - return _tagQuery.TryComp(entity, out var component) && - HasAllTags(component, ids); - } - - /// - /// Checks if all of the given tags have been added to an entity. - /// - /// The entity to check. - /// The tags to check for. - /// true if they all exist, false otherwise. - /// - /// Thrown if one of the ids represents an unregistered . - /// - public bool HasAllTags(EntityUid entity, IEnumerable ids) - { - return _tagQuery.TryComp(entity, out var component) && - HasAllTags(component, ids); - } - - /// - /// Checks if all of the given tags have been added to an entity. - /// - /// The entity to check. - /// The tags to check for. - /// true if they all exist, false otherwise. - /// - /// Thrown if one of the ids represents an unregistered . - /// - public bool HasAllTags(EntityUid entity, List> ids) - { - return _tagQuery.TryComp(entity, out var component) && - HasAllTags(component, ids); - } - - /// - /// Checks if any of the given tags have been added to an entity. - /// - /// The entity to check. - /// The tags to check for. - /// true if any of them exist, false otherwise. - /// - /// Thrown if one of the ids represents an unregistered . - /// - public bool HasAnyTag(EntityUid entity, params string[] ids) - { - return _tagQuery.TryComp(entity, out var component) && - HasAnyTag(component, ids); - } - - /// - /// Checks if any of the given tags have been added to an entity. - /// - /// The entity to check. - /// The tag to check for. - /// true if any of them exist, false otherwise. - /// - /// Thrown if one of the ids represents an unregistered . - /// - public bool HasAnyTag(EntityUid entity, string id) => HasTag(entity, id); - - /// - /// Checks if any of the given tags have been added to an entity. - /// - /// The entity to check. - /// The tags to check for. - /// true if any of them exist, false otherwise. - /// - /// Thrown if one of the ids represents an unregistered . - /// - public bool HasAnyTag(EntityUid entity, List ids) - { - return _tagQuery.TryComp(entity, out var component) && - HasAnyTag(component, ids); - } - - /// - /// Checks if any of the given tags have been added to an entity. - /// - /// The entity to check. - /// The tags to check for. - /// true if any of them exist, false otherwise. - /// - /// Thrown if one of the ids represents an unregistered . - /// - public bool HasAnyTag(EntityUid entity, List> ids) - { - return TryComp(entity, out var component) && - HasAnyTag(component, ids); - } - - /// - /// Checks if any of the given tags have been added to an entity. - /// - /// The entity to check. - /// The tags to check for. - /// true if any of them exist, false otherwise. - /// - /// Thrown if one of the ids represents an unregistered . - /// - public bool HasAnyTag(EntityUid entity, IEnumerable ids) - { - return _tagQuery.TryComp(entity, out var component) && - HasAnyTag(component, ids); - } - - /// - /// Tries to remove a tag from an entity if it exists. - /// - /// The entity to remove the tag from. - /// The tag to remove. - /// - /// true if it was removed, false otherwise even if it didn't exist. - /// - /// - /// Thrown if no exists with the given id. - /// - public bool RemoveTag(EntityUid entity, string id) - { - return _tagQuery.TryComp(entity, out var component) && - RemoveTag(entity, component, id); - } - - /// - /// Tries to remove a tag from an entity if it exists. - /// - /// The entity to remove the tag from. - /// The tag to remove. - /// - /// true if it was removed, false otherwise even if it didn't exist. - /// - /// Thrown if one of the ids represents an unregistered . - /// - /// - public bool RemoveTags(EntityUid entity, params string[] ids) - { - return _tagQuery.TryComp(entity, out var component) && - RemoveTags(entity, component, ids); - } - - /// - /// Tries to remove a tag from an entity if it exists. - /// - /// The entity to remove the tag from. - /// The tag to remove. - /// - /// true if it was removed, false otherwise even if it didn't exist. - /// - /// - /// Thrown if one of the ids represents an unregistered . - /// - public bool RemoveTags(EntityUid entity, IEnumerable ids) - { - return _tagQuery.TryComp(entity, out var component) && - RemoveTags(entity, component, ids); - } - - /// - /// Tries to add a tag if it doesn't already exist. - /// - /// The tag to add. - /// true if it was added, false if it already existed. - /// - /// Thrown if no exists with the given id. - /// - public bool AddTag(EntityUid uid, TagComponent component, string id) - { - AssertValidTag(id); - var added = component.Tags.Add(id); - - if (added) - { - Dirty(uid, component); - return true; - } - - return false; - } - - /// - /// Tries to add the given tags if they don't already exist. - /// - /// The tags to add. - /// true if any tags were added, false if they all already existed. - /// - /// Thrown if one of the ids represents an unregistered . - /// - public bool AddTags(EntityUid uid, TagComponent component, params string[] ids) - { - return AddTags(uid, component, ids.AsEnumerable()); - } - - /// - /// Tries to add the given tags if they don't already exist. - /// - /// The tags to add. - /// true if any tags were added, false if they all already existed. - /// - /// Thrown if one of the ids represents an unregistered . - /// - public bool AddTags(EntityUid uid, TagComponent component, IEnumerable ids) - { - var count = component.Tags.Count; - - foreach (var id in ids) - { - AssertValidTag(id); - component.Tags.Add(id); - } - - if (component.Tags.Count > count) - { - Dirty(uid, component); - return true; - } - - return false; - } - - /// - /// Checks if a tag has been added. - /// - /// The tag to check for. - /// true if it exists, false otherwise. - /// - /// Thrown if no exists with the given id. - /// - public bool HasTag(TagComponent component, string id) - { - AssertValidTag(id); - return component.Tags.Contains(id); - } - - /// - /// Checks if all of the given tags have been added. - /// - /// The tags to check for. - /// true if they all exist, false otherwise. - /// - /// Thrown if one of the ids represents an unregistered . - /// - public bool HasAllTags(TagComponent component, params string[] ids) - { - return HasAllTags(component, ids.AsEnumerable()); - } - - /// - /// Checks if all of the given tags have been added. - /// - /// The tag to check for. - /// true if they all exist, false otherwise. - /// - /// Thrown if one of the ids represents an unregistered . - /// - public bool HasAllTags(TagComponent component, string id) => HasTag(component, id); - - /// - /// Checks if all of the given tags have been added. - /// - /// The tags to check for. - /// true if they all exist, false otherwise. - /// - /// Thrown if one of the ids represents an unregistered . - /// - public bool HasAllTags(TagComponent component, List ids) - { - foreach (var id in ids) - { - AssertValidTag(id); - - if (!component.Tags.Contains(id)) - return false; - } - - return true; - } - - /// - /// Checks if all of the given tags have been added. - /// - /// The tags to check for. - /// true if they all exist, false otherwise. - /// - /// Thrown if one of the ids represents an unregistered . - /// - public bool HasAllTags(TagComponent component, IEnumerable ids) - { - foreach (var id in ids) - { - AssertValidTag(id); - - if (!component.Tags.Contains(id)) - return false; - - } - - return true; - } - - /// - /// Checks if all of the given tags have been added. - /// - /// The tags to check for. - /// true if they all exist, false otherwise. - /// - /// Thrown if one of the ids represents an unregistered . - /// - public bool HasAllTags(TagComponent component, List> ids) - { - foreach (var id in ids) - { - AssertValidTag(id); - - if (!component.Tags.Contains(id)) - return false; - - } - - return true; - } - - /// - /// Checks if any of the given tags have been added. - /// - /// The tags to check for. - /// true if any of them exist, false otherwise. - /// - /// Thrown if one of the ids represents an unregistered . - /// - public bool HasAnyTag(TagComponent component, params string[] ids) - { - foreach (var id in ids) - { - AssertValidTag(id); - - if (component.Tags.Contains(id)) - return true; - } - - return false; - } - - /// - /// Checks if any of the given tags have been added. - /// - /// The tag to check for. - /// true if any of them exist, false otherwise. - /// - /// Thrown if one of the ids represents an unregistered . - /// - public bool HasAnyTag(TagComponent component, string id) => HasTag(component, id); - - /// - /// Checks if any of the given tags have been added. - /// - /// The tags to check for. - /// true if any of them exist, false otherwise. - /// - /// Thrown if one of the ids represents an unregistered . - /// - public bool HasAnyTag(TagComponent component, List ids) - { - foreach (var id in ids) - { - AssertValidTag(id); - - if (component.Tags.Contains(id)) - { - return true; - } - } - - return false; - } - - /// - /// Checks if any of the given tags have been added. - /// - /// The tags to check for. - /// true if any of them exist, false otherwise. - /// - /// Thrown if one of the ids represents an unregistered . - /// - public bool HasAnyTag(TagComponent component, IEnumerable ids) - { - foreach (var id in ids) - { - AssertValidTag(id); - - if (component.Tags.Contains(id)) - { - return true; - } - } - - return false; - } - - /// - /// Checks if any of the given tags have been added. - /// - /// The tags to check for. - /// true if any of them exist, false otherwise. - /// - /// Thrown if one of the ids represents an unregistered . - /// - public bool HasAnyTag(TagComponent comp, List> ids) - { - foreach (var id in ids) - { - AssertValidTag(id); - - if (comp.Tags.Contains(id)) - return true; - } - - return false; - } - - /// - /// Tries to remove a tag if it exists. - /// - /// - /// true if it was removed, false otherwise even if it didn't exist. - /// - /// - /// Thrown if no exists with the given id. - /// - public bool RemoveTag(EntityUid uid, TagComponent component, string id) - { - AssertValidTag(id); - - if (component.Tags.Remove(id)) - { - Dirty(uid, component); - return true; - } - - return false; - } - - /// - /// Tries to remove all of the given tags if they exist. - /// - /// The tags to remove. - /// - /// true if it was removed, false otherwise even if they didn't exist. - /// - /// - /// Thrown if one of the ids represents an unregistered . - /// - public bool RemoveTags(EntityUid uid, TagComponent component, params string[] ids) - { - return RemoveTags(uid, component, ids.AsEnumerable()); - } - - /// - /// Tries to remove all of the given tags if they exist. - /// - /// The tags to remove. - /// true if any tag was removed, false otherwise. - /// - /// Thrown if one of the ids represents an unregistered . - /// - public bool RemoveTags(EntityUid uid, TagComponent component, IEnumerable ids) - { - var count = component.Tags.Count; - - foreach (var id in ids) - { - AssertValidTag(id); - component.Tags.Remove(id); - } - - if (component.Tags.Count < count) - { - Dirty(uid, component); - return true; - } - - return false; - } }