From 70c0a502cf96fdd6ec34580c8f1b181a2850b407 Mon Sep 17 00:00:00 2001 From: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> Date: Tue, 8 Feb 2022 14:08:11 +1100 Subject: [PATCH] ECS tags (#6504) --- Content.Client/Verbs/VerbSystem.cs | 3 +- Content.IntegrationTests/Tests/Tag/TagTest.cs | 121 ++--- .../Botany/Components/PlantHolderComponent.cs | 10 +- .../Botany/Systems/BotanySystem.Plant.cs | 2 + .../Botany/Systems/BotanySystem.Seed.cs | 2 +- Content.Server/Botany/Systems/LogSystem.cs | 4 +- .../HasTagCondition.cs | 2 +- .../Commands/FixRotationsCommand.cs | 27 +- .../Components/MachineFrameComponent.cs | 15 +- Content.Server/Doors/Systems/DoorSystem.cs | 9 +- .../EntitySystems/ExplosionSystem.cs | 3 +- Content.Server/Paper/PaperComponent.cs | 2 +- .../Physics/Controllers/MoverController.cs | 3 +- Content.Server/RCD/Systems/RCDSystem.cs | 3 +- .../EntitySystems/ShuttleConsoleSystem.cs | 3 +- .../ContainmentFieldGeneratorSystem.cs | 4 +- Content.Server/Throwing/ThrowHelper.cs | 2 +- .../Conditions/WallmountCondition.cs | 16 +- .../MultipleTagsConstructionGraphStep.cs | 6 +- .../Steps/TagConstructionGraphStep.cs | 3 +- Content.Shared/Tag/TagComponent.cs | 284 +--------- Content.Shared/Tag/TagComponentExtensions.cs | 241 --------- Content.Shared/Tag/TagSystem.cs | 501 ++++++++++++++++++ Content.Shared/Whitelist/EntityWhitelist.cs | 3 +- 24 files changed, 641 insertions(+), 628 deletions(-) delete mode 100644 Content.Shared/Tag/TagComponentExtensions.cs create mode 100644 Content.Shared/Tag/TagSystem.cs diff --git a/Content.Client/Verbs/VerbSystem.cs b/Content.Client/Verbs/VerbSystem.cs index a9c9586e43..8584f8a260 100644 --- a/Content.Client/Verbs/VerbSystem.cs +++ b/Content.Client/Verbs/VerbSystem.cs @@ -28,6 +28,7 @@ namespace Content.Client.Verbs { [Dependency] private readonly PopupSystem _popupSystem = default!; [Dependency] private readonly ExamineSystem _examineSystem = default!; + [Dependency] private readonly TagSystem _tagSystem = default!; [Dependency] private readonly IStateManager _stateManager = default!; [Dependency] private readonly IEntityLookup _entityLookup = default!; [Dependency] private readonly IPlayerManager _playerManager = default!; @@ -147,7 +148,7 @@ namespace Content.Client.Verbs continue; } - if (entity.HasTag("HideContextMenu")) + if (_tagSystem.HasTag(entity, "HideContextMenu")) entities.Remove(entity); } } diff --git a/Content.IntegrationTests/Tests/Tag/TagTest.cs b/Content.IntegrationTests/Tests/Tag/TagTest.cs index db27cad716..78829ef64e 100644 --- a/Content.IntegrationTests/Tests/Tag/TagTest.cs +++ b/Content.IntegrationTests/Tests/Tag/TagTest.cs @@ -53,6 +53,7 @@ namespace Content.IntegrationTests.Tests.Tag var sMapManager = server.ResolveDependency(); var sEntityManager = server.ResolveDependency(); var sPrototypeManager = server.ResolveDependency(); + var tagSystem = server.ResolveDependency().GetEntitySystem(); EntityUid sTagDummy = default; TagComponent sTagComponent = null!; @@ -61,7 +62,7 @@ namespace Content.IntegrationTests.Tests.Tag { sMapManager.CreateNewMapEntity(MapId.Nullspace); sTagDummy = sEntityManager.SpawnEntity(TagEntityId, MapCoordinates.Nullspace); - sTagComponent = IoCManager.Resolve().GetComponent(sTagDummy); + sTagComponent = sEntityManager.GetComponent(sTagDummy); }); await server.WaitAssertion(() => @@ -72,48 +73,48 @@ namespace Content.IntegrationTests.Tests.Tag Assert.That(sTagComponent.Tags, Contains.Item(StartingTag)); // Single - Assert.True(sTagDummy.HasTag(StartingTag)); - Assert.True(sTagComponent.HasTag(StartingTag)); + Assert.True(tagSystem.HasTag(sTagDummy, StartingTag)); + Assert.True(tagSystem.HasTag(sTagComponent, StartingTag)); // Any - Assert.True(sTagDummy.HasAnyTag(StartingTag)); - Assert.True(sTagComponent.HasAnyTag(StartingTag)); + Assert.True(tagSystem.HasAnyTag(sTagDummy, StartingTag)); + Assert.True(tagSystem.HasAnyTag(sTagComponent, StartingTag)); // All - Assert.True(sTagDummy.HasAllTags(StartingTag)); - Assert.True(sTagComponent.HasAllTags(StartingTag)); + Assert.True(tagSystem.HasAllTags(sTagDummy, StartingTag)); + Assert.True(tagSystem.HasAllTags(sTagComponent, StartingTag)); // Does not have the added tag var addedTagPrototype = sPrototypeManager.Index(AddedTag); Assert.That(sTagComponent.Tags, Does.Not.Contains(addedTagPrototype)); // Single - Assert.False(sTagDummy.HasTag(AddedTag)); - Assert.False(sTagComponent.HasTag(AddedTag)); + Assert.False(tagSystem.HasTag(sTagDummy, AddedTag)); + Assert.False(tagSystem.HasTag(sTagComponent, AddedTag)); // Any - Assert.False(sTagDummy.HasAnyTag(AddedTag)); - Assert.False(sTagComponent.HasAnyTag(AddedTag)); + Assert.False(tagSystem.HasAnyTag(sTagDummy, AddedTag)); + Assert.False(tagSystem.HasAnyTag(sTagComponent, AddedTag)); // All - Assert.False(sTagDummy.HasAllTags(AddedTag)); - Assert.False(sTagComponent.HasAllTags(AddedTag)); + Assert.False(tagSystem.HasAllTags(sTagDummy, AddedTag)); + Assert.False(tagSystem.HasAllTags(sTagComponent, AddedTag)); // Does not have the unused tag var unusedTagPrototype = sPrototypeManager.Index(UnusedTag); Assert.That(sTagComponent.Tags, Does.Not.Contains(unusedTagPrototype)); // Single - Assert.False(sTagDummy.HasTag(UnusedTag)); - Assert.False(sTagComponent.HasTag(UnusedTag)); + Assert.False(tagSystem.HasTag(sTagDummy, UnusedTag)); + Assert.False(tagSystem.HasTag(sTagComponent, UnusedTag)); // Any - Assert.False(sTagDummy.HasAnyTag(UnusedTag)); - Assert.False(sTagComponent.HasAnyTag(UnusedTag)); + Assert.False(tagSystem.HasAnyTag(sTagDummy, UnusedTag)); + Assert.False(tagSystem.HasAnyTag(sTagComponent, UnusedTag)); // All - Assert.False(sTagDummy.HasAllTags(UnusedTag)); - Assert.False(sTagComponent.HasAllTags(UnusedTag)); + Assert.False(tagSystem.HasAllTags(sTagDummy, UnusedTag)); + Assert.False(tagSystem.HasAllTags(sTagComponent, UnusedTag)); // Throws when checking for an unregistered tag Assert.Throws(() => @@ -124,96 +125,96 @@ namespace Content.IntegrationTests.Tests.Tag // Single Assert.Throws(() => { - sTagDummy.HasTag(UnregisteredTag); + tagSystem.HasTag(sTagDummy, UnregisteredTag); }); Assert.Throws(() => { - sTagComponent.HasTag(UnregisteredTag); + tagSystem.HasTag(sTagComponent, UnregisteredTag); }); // Any Assert.Throws(() => { - sTagDummy.HasAnyTag(UnregisteredTag); + tagSystem.HasAnyTag(sTagDummy, UnregisteredTag); }); Assert.Throws(() => { - sTagComponent.HasAnyTag(UnregisteredTag); + tagSystem.HasAnyTag(sTagComponent, UnregisteredTag); }); // All Assert.Throws(() => { - sTagDummy.HasAllTags(UnregisteredTag); + tagSystem.HasAllTags(sTagDummy, UnregisteredTag); }); Assert.Throws(() => { - sTagComponent.HasAllTags(UnregisteredTag); + tagSystem.HasAllTags(sTagComponent, UnregisteredTag); }); // Cannot add the starting tag again - Assert.That(sTagComponent.AddTag(StartingTag), Is.False); - Assert.That(sTagComponent.AddTags(StartingTag, StartingTag), Is.False); - Assert.That(sTagComponent.AddTags(new List {StartingTag, StartingTag}), Is.False); + Assert.That(tagSystem.AddTag(sTagComponent, StartingTag), Is.False); + Assert.That(tagSystem.AddTags(sTagComponent, StartingTag, StartingTag), Is.False); + Assert.That(tagSystem.AddTags(sTagComponent, new List {StartingTag, StartingTag}), Is.False); // Has the starting tag - Assert.That(sTagComponent.HasTag(StartingTag), Is.True); - Assert.That(sTagComponent.HasAllTags(StartingTag, StartingTag), Is.True); - Assert.That(sTagComponent.HasAllTags(new List {StartingTag, StartingTag}), Is.True); - Assert.That(sTagComponent.HasAnyTag(StartingTag, StartingTag), Is.True); - Assert.That(sTagComponent.HasAnyTag(new List {StartingTag, StartingTag}), Is.True); + 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.HasAnyTag(sTagComponent, StartingTag, StartingTag), Is.True); + Assert.That(tagSystem.HasAnyTag(sTagComponent, new List {StartingTag, StartingTag}), Is.True); // Does not have the added tag yet - Assert.That(sTagComponent.HasTag(AddedTag), Is.False); - Assert.That(sTagComponent.HasAllTags(AddedTag, AddedTag), Is.False); - Assert.That(sTagComponent.HasAllTags(new List {AddedTag, AddedTag}), Is.False); - Assert.That(sTagComponent.HasAnyTag(AddedTag, AddedTag), Is.False); - Assert.That(sTagComponent.HasAnyTag(new List {AddedTag, AddedTag}), Is.False); + 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.HasAnyTag(sTagComponent, AddedTag, AddedTag), Is.False); + Assert.That(tagSystem.HasAnyTag(sTagComponent, new List {AddedTag, AddedTag}), Is.False); // Has a combination of the two tags - Assert.That(sTagComponent.HasAnyTag(StartingTag, AddedTag), Is.True); - Assert.That(sTagComponent.HasAnyTag(new List {StartingTag, AddedTag}), Is.True); + Assert.That(tagSystem.HasAnyTag(sTagComponent, StartingTag, AddedTag), Is.True); + Assert.That(tagSystem.HasAnyTag(sTagComponent, new List {StartingTag, AddedTag}), Is.True); // Does not have both tags - Assert.That(sTagComponent.HasAllTags(StartingTag, AddedTag), Is.False); - Assert.That(sTagComponent.HasAllTags(new List {StartingTag, AddedTag}), Is.False); + Assert.That(tagSystem.HasAllTags(sTagComponent, StartingTag, AddedTag), Is.False); + Assert.That(tagSystem.HasAllTags(sTagComponent, new List {StartingTag, AddedTag}), Is.False); // Cannot remove a tag that does not exist - Assert.That(sTagComponent.RemoveTag(AddedTag), Is.False); - Assert.That(sTagComponent.RemoveTags(AddedTag, AddedTag), Is.False); - Assert.That(sTagComponent.RemoveTags(new List {AddedTag, AddedTag}), Is.False); + Assert.That(tagSystem.RemoveTag(sTagComponent, AddedTag), Is.False); + Assert.That(tagSystem.RemoveTags(sTagComponent, AddedTag, AddedTag), Is.False); + Assert.That(tagSystem.RemoveTags(sTagComponent, new List {AddedTag, AddedTag}), Is.False); // Can add the new tag - Assert.That(sTagComponent.AddTag(AddedTag), Is.True); + Assert.That(tagSystem.AddTag(sTagComponent, AddedTag), Is.True); // Cannot add it twice - Assert.That(sTagComponent.AddTag(AddedTag), Is.False); + Assert.That(tagSystem.AddTag(sTagComponent, AddedTag), Is.False); // Cannot add existing tags - Assert.That(sTagComponent.AddTags(StartingTag, AddedTag), Is.False); - Assert.That(sTagComponent.AddTags(new List {StartingTag, AddedTag}), Is.False); + Assert.That(tagSystem.AddTags(sTagComponent, StartingTag, AddedTag), Is.False); + Assert.That(tagSystem.AddTags(sTagComponent, new List {StartingTag, AddedTag}), Is.False); // Now has two tags Assert.That(sTagComponent.Tags.Count, Is.EqualTo(2)); // Has both tags - Assert.That(sTagComponent.HasTag(StartingTag), Is.True); - Assert.That(sTagComponent.HasTag(AddedTag), Is.True); - Assert.That(sTagComponent.HasAllTags(StartingTag, StartingTag), Is.True); - Assert.That(sTagComponent.HasAllTags(AddedTag, StartingTag), Is.True); - Assert.That(sTagComponent.HasAllTags(new List {StartingTag, AddedTag}), Is.True); - Assert.That(sTagComponent.HasAllTags(new List {AddedTag, StartingTag}), Is.True); - Assert.That(sTagComponent.HasAnyTag(StartingTag, AddedTag), Is.True); - Assert.That(sTagComponent.HasAnyTag(AddedTag, StartingTag), Is.True); + 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.HasAnyTag(sTagComponent, StartingTag, AddedTag), Is.True); + Assert.That(tagSystem.HasAnyTag(sTagComponent, AddedTag, StartingTag), Is.True); // Remove the existing starting tag - Assert.That(sTagComponent.RemoveTag(StartingTag), Is.True); + Assert.That(tagSystem.RemoveTag(sTagComponent, StartingTag), Is.True); // Remove the existing added tag - Assert.That(sTagComponent.RemoveTags(AddedTag, AddedTag), Is.True); + Assert.That(tagSystem.RemoveTags(sTagComponent, AddedTag, AddedTag), Is.True); // No tags left to remove - Assert.That(sTagComponent.RemoveTags(new List {StartingTag, AddedTag}), Is.False); + Assert.That(tagSystem.RemoveTags(sTagComponent, new List {StartingTag, AddedTag}), Is.False); // No tags left in the component Assert.That(sTagComponent.Tags, Is.Empty); diff --git a/Content.Server/Botany/Components/PlantHolderComponent.cs b/Content.Server/Botany/Components/PlantHolderComponent.cs index 21c8e82bba..720a545577 100644 --- a/Content.Server/Botany/Components/PlantHolderComponent.cs +++ b/Content.Server/Botany/Components/PlantHolderComponent.cs @@ -693,7 +693,9 @@ namespace Content.Server.Botany.Components return false; } - if (usingItem.HasTag("Hoe")) + var tagSystem = EntitySystem.Get(); + + if (tagSystem.HasTag(usingItem, "Hoe")) { if (WeedLevel > 0) { @@ -712,7 +714,7 @@ namespace Content.Server.Botany.Components return true; } - if (usingItem.HasTag("Shovel")) + if (tagSystem.HasTag(usingItem, "Shovel")) { if (Seed != null) { @@ -764,7 +766,7 @@ namespace Content.Server.Botany.Components return true; } - if (usingItem.HasTag("PlantSampleTaker")) + if (tagSystem.HasTag(usingItem, "PlantSampleTaker")) { if (Seed == null) { @@ -800,7 +802,7 @@ namespace Content.Server.Botany.Components return true; } - if (usingItem.HasTag("BotanySharp")) + if (tagSystem.HasTag(usingItem, "BotanySharp")) { return DoHarvest(user); } diff --git a/Content.Server/Botany/Systems/BotanySystem.Plant.cs b/Content.Server/Botany/Systems/BotanySystem.Plant.cs index db7981980e..8513e8ae37 100644 --- a/Content.Server/Botany/Systems/BotanySystem.Plant.cs +++ b/Content.Server/Botany/Systems/BotanySystem.Plant.cs @@ -3,6 +3,7 @@ using Content.Server.Botany.Components; using Content.Server.Chemistry.EntitySystems; using Content.Server.Popups; using Content.Shared.GameTicking; +using Content.Shared.Tag; using JetBrains.Annotations; using Robust.Shared.GameObjects; using Robust.Shared.IoC; @@ -18,6 +19,7 @@ namespace Content.Server.Botany.Systems [Dependency] private readonly PopupSystem _popupSystem = default!; [Dependency] private readonly IRobustRandom _robustRandom = default!; [Dependency] private readonly SolutionContainerSystem _solutionContainerSystem = default!; + [Dependency] private readonly TagSystem _tags = default!; private int _nextUid = 0; private float _timer = 0f; diff --git a/Content.Server/Botany/Systems/BotanySystem.Seed.cs b/Content.Server/Botany/Systems/BotanySystem.Seed.cs index f32a49a45f..97b58be408 100644 --- a/Content.Server/Botany/Systems/BotanySystem.Seed.cs +++ b/Content.Server/Botany/Systems/BotanySystem.Seed.cs @@ -136,7 +136,7 @@ public partial class BotanySystem public bool CanHarvest(SeedPrototype proto, EntityUid? held = null) { - return !proto.Ligneous || proto.Ligneous && held != null && held.Value.HasTag("BotanySharp"); + return !proto.Ligneous || proto.Ligneous && held != null && _tags.HasTag(held.Value, "BotanySharp"); } #endregion diff --git a/Content.Server/Botany/Systems/LogSystem.cs b/Content.Server/Botany/Systems/LogSystem.cs index ba859b4ea7..2b6ab4ad44 100644 --- a/Content.Server/Botany/Systems/LogSystem.cs +++ b/Content.Server/Botany/Systems/LogSystem.cs @@ -9,6 +9,8 @@ namespace Content.Server.Botany.Systems; public sealed class LogSystem : EntitySystem { + [Dependency] private readonly TagSystem _tags = default!; + public override void Initialize() { base.Initialize(); @@ -18,7 +20,7 @@ public sealed class LogSystem : EntitySystem private void OnInteractUsing(EntityUid uid, LogComponent component, InteractUsingEvent args) { - if (args.Used.HasTag("BotanySharp")) + if (_tags.HasTag(args.Used, "BotanySharp")) { for (var i = 0; i < component.SpawnCount; i++) { diff --git a/Content.Server/Chemistry/ReagentEffectConditions/HasTagCondition.cs b/Content.Server/Chemistry/ReagentEffectConditions/HasTagCondition.cs index c87fd6d5ea..fc45b64958 100644 --- a/Content.Server/Chemistry/ReagentEffectConditions/HasTagCondition.cs +++ b/Content.Server/Chemistry/ReagentEffectConditions/HasTagCondition.cs @@ -18,7 +18,7 @@ public class HasTag : ReagentEffectCondition public override bool Condition(ReagentEffectArgs args) { if (args.EntityManager.TryGetComponent(args.SolutionEntity, out var tag)) - return tag.HasTag(Tag) ^ Invert; + return EntitySystem.Get().HasTag(tag, Tag) ^ Invert; return false; } diff --git a/Content.Server/Construction/Commands/FixRotationsCommand.cs b/Content.Server/Construction/Commands/FixRotationsCommand.cs index 34c6cdcd49..84ed05c623 100644 --- a/Content.Server/Construction/Commands/FixRotationsCommand.cs +++ b/Content.Server/Construction/Commands/FixRotationsCommand.cs @@ -25,22 +25,23 @@ namespace Content.Server.Construction.Commands var player = shell.Player as IPlayerSession; var entityManager = IoCManager.Resolve(); GridId gridId; + var xformQuery = entityManager.GetEntityQuery(); switch (args.Length) { case 0: if (player?.AttachedEntity is not {Valid: true} playerEntity) { - shell.WriteLine("Only a player can run this command."); + shell.WriteError("Only a player can run this command."); return; } - gridId = entityManager.GetComponent(playerEntity).GridID; + gridId = xformQuery.GetComponent(playerEntity).GridID; break; case 1: if (!int.TryParse(args[0], out var id)) { - shell.WriteLine($"{args[0]} is not a valid integer."); + shell.WriteError($"{args[0]} is not a valid integer."); return; } @@ -54,18 +55,20 @@ namespace Content.Server.Construction.Commands var mapManager = IoCManager.Resolve(); if (!mapManager.TryGetGrid(gridId, out var grid)) { - shell.WriteLine($"No grid exists with id {gridId}"); + shell.WriteError($"No grid exists with id {gridId}"); return; } if (!entityManager.EntityExists(grid.GridEntityId)) { - shell.WriteLine($"Grid {gridId} doesn't have an associated grid entity."); + shell.WriteError($"Grid {gridId} doesn't have an associated grid entity."); return; } var changed = 0; - foreach (var child in entityManager.GetComponent(grid.GridEntityId).ChildEntities) + var tagSystem = EntitySystem.Get(); + + foreach (var child in xformQuery.GetComponent(grid.GridEntityId).ChildEntities) { if (!entityManager.EntityExists(child)) { @@ -85,18 +88,18 @@ namespace Content.Server.Construction.Commands // cables valid |= entityManager.HasComponent(child); // anything else that might need this forced - valid |= child.HasTag("ForceFixRotations"); + valid |= tagSystem.HasTag(child, "ForceFixRotations"); // override - valid &= !child.HasTag("ForceNoFixRotations"); + valid &= !tagSystem.HasTag(child, "ForceNoFixRotations"); if (!valid) - { continue; - } - if (entityManager.GetComponent(child).LocalRotation != Angle.Zero) + var childXform = xformQuery.GetComponent(child); + + if (childXform.LocalRotation != Angle.Zero) { - entityManager.GetComponent(child).LocalRotation = Angle.Zero; + childXform.LocalRotation = Angle.Zero; changed++; } } diff --git a/Content.Server/Construction/Components/MachineFrameComponent.cs b/Content.Server/Construction/Components/MachineFrameComponent.cs index 7b46ed29f6..2f9d6c806b 100644 --- a/Content.Server/Construction/Components/MachineFrameComponent.cs +++ b/Content.Server/Construction/Components/MachineFrameComponent.cs @@ -1,14 +1,9 @@ -using System.Collections.Generic; -using System.Threading.Tasks; +using System.Threading.Tasks; using Content.Server.Stack; using Content.Shared.Construction; using Content.Shared.Interaction; using Content.Shared.Tag; using Robust.Shared.Containers; -using Robust.Shared.GameObjects; -using Robust.Shared.IoC; -using Robust.Shared.Serialization.Manager.Attributes; -using Robust.Shared.ViewVariables; namespace Content.Server.Construction.Components { @@ -236,10 +231,12 @@ namespace Content.Server.Construction.Components _componentProgress[compName]++; } + var tagSystem = EntitySystem.Get(); + // I have MANY regrets. foreach (var (tagName, _) in TagRequirements) { - if (!part.HasTag(tagName)) + if (!tagSystem.HasTag(part, tagName)) continue; if (!_tagProgress.ContainsKey(tagName)) @@ -339,12 +336,14 @@ namespace Content.Server.Construction.Components return true; } + var tags = EntitySystem.Get(); + foreach (var (tagName, info) in TagRequirements) { if (_tagProgress[tagName] >= info.Amount) continue; - if (!eventArgs.Using.HasTag(tagName)) + if (!tags.HasTag(eventArgs.Using, tagName)) continue; if (!eventArgs.Using.TryRemoveFromContainer() || !_partContainer.Insert(eventArgs.Using)) continue; diff --git a/Content.Server/Doors/Systems/DoorSystem.cs b/Content.Server/Doors/Systems/DoorSystem.cs index 3902866d29..dad43ed0db 100644 --- a/Content.Server/Doors/Systems/DoorSystem.cs +++ b/Content.Server/Doors/Systems/DoorSystem.cs @@ -28,6 +28,7 @@ public sealed class DoorSystem : SharedDoorSystem [Dependency] private readonly ToolSystem _toolSystem = default!; [Dependency] private readonly AirtightSystem _airtightSystem = default!; [Dependency] private readonly AccessReaderSystem _accessReaderSystem = default!; + [Dependency] private readonly TagSystem _tagSystem = default!; public override void Initialize() { @@ -236,8 +237,10 @@ public sealed class DoorSystem : SharedDoorSystem if (door.State != DoorState.Closed) return; - if (TryComp(args.OtherFixture.Body.Owner, out TagComponent? tags) && tags.HasTag("DoorBumpOpener")) - TryOpen(uid, door, args.OtherFixture.Body.Owner); + var otherUid = args.OtherFixture.Body.Owner; + + if (_tagSystem.HasTag(otherUid, "DoorBumpOpener")) + TryOpen(uid, door, otherUid); } public override void OnPartialOpen(EntityUid uid, DoorComponent? door = null, PhysicsComponent? physics = null) @@ -264,7 +267,7 @@ public sealed class DoorSystem : SharedDoorSystem if (!base.OnPartialClose(uid, door, physics)) return false; - // update airtight, if we did not crush something. + // update airtight, if we did not crush something. if (door.ChangeAirtight && door.CurrentlyCrushing.Count == 0 && TryComp(uid, out AirtightComponent? airtight)) _airtightSystem.SetAirblocked(airtight, true); diff --git a/Content.Server/Explosion/EntitySystems/ExplosionSystem.cs b/Content.Server/Explosion/EntitySystems/ExplosionSystem.cs index 5a455c0d4a..096d3a6e02 100644 --- a/Content.Server/Explosion/EntitySystems/ExplosionSystem.cs +++ b/Content.Server/Explosion/EntitySystems/ExplosionSystem.cs @@ -53,10 +53,11 @@ namespace Content.Server.Explosion.EntitySystems [Dependency] private readonly TriggerSystem _triggers = default!; [Dependency] private readonly AdminLogSystem _logSystem = default!; [Dependency] private readonly CameraRecoilSystem _cameraRecoil = default!; + [Dependency] private readonly TagSystem _tags = default!; private bool IgnoreExplosivePassable(EntityUid e) { - return e.HasTag("ExplosivePassable"); + return _tags.HasTag(e, "ExplosivePassable"); } private ExplosionSeverity CalculateSeverity(float distance, float devastationRange, float heavyRange) diff --git a/Content.Server/Paper/PaperComponent.cs b/Content.Server/Paper/PaperComponent.cs index 6d771f4929..ed4f743d83 100644 --- a/Content.Server/Paper/PaperComponent.cs +++ b/Content.Server/Paper/PaperComponent.cs @@ -113,7 +113,7 @@ namespace Content.Server.Paper async Task IInteractUsing.InteractUsing(InteractUsingEventArgs eventArgs) { - if (!eventArgs.Using.HasTag("Write")) + if (!EntitySystem.Get().HasTag(eventArgs.Using, "Write")) return false; if (!_entMan.TryGetComponent(eventArgs.User, out ActorComponent? actor)) return false; diff --git a/Content.Server/Physics/Controllers/MoverController.cs b/Content.Server/Physics/Controllers/MoverController.cs index 2f9ff0f734..6298ebcfc6 100644 --- a/Content.Server/Physics/Controllers/MoverController.cs +++ b/Content.Server/Physics/Controllers/MoverController.cs @@ -27,6 +27,7 @@ namespace Content.Server.Physics.Controllers { [Dependency] private readonly ITileDefinitionManager _tileDefinitionManager = default!; [Dependency] private readonly IMapManager _mapManager = default!; + [Dependency] private readonly TagSystem _tags = default!; private const float StepSoundMoveDistanceRunning = 2; private const float StepSoundMoveDistanceWalking = 1.5f; @@ -272,7 +273,7 @@ namespace Content.Server.Physics.Controllers protected override void HandleFootsteps(IMoverComponent mover, IMobMoverComponent mobMover) { - if (!mover.Owner.HasTag("FootstepSound")) return; + if (!_tags.HasTag(mover.Owner, "FootstepSound")) return; var transform = EntityManager.GetComponent(mover.Owner); var coordinates = transform.Coordinates; diff --git a/Content.Server/RCD/Systems/RCDSystem.cs b/Content.Server/RCD/Systems/RCDSystem.cs index dd416299d8..8c98ff2b39 100644 --- a/Content.Server/RCD/Systems/RCDSystem.cs +++ b/Content.Server/RCD/Systems/RCDSystem.cs @@ -26,6 +26,7 @@ namespace Content.Server.RCD.Systems [Dependency] private readonly DoAfterSystem _doAfterSystem = default!; [Dependency] private readonly SharedInteractionSystem _interactionSystem = default!; + [Dependency] private readonly TagSystem _tagSystem = default!; private readonly RcdMode[] _modes = (RcdMode[]) Enum.GetValues(typeof(RcdMode)); @@ -191,7 +192,7 @@ namespace Content.Server.RCD.Systems return false; } //They tried to decon a non-turf but it's not in the whitelist - if (eventArgs.Target != null && !eventArgs.Target.Value.HasTag("RCDDeconstructWhitelist")) + if (eventArgs.Target != null && !_tagSystem.HasTag(eventArgs.Target.Value, "RCDDeconstructWhitelist")) { rcd.Owner.PopupMessage(eventArgs.User, Loc.GetString("rcd-component-deconstruct-target-not-on-whitelist-message")); return false; diff --git a/Content.Server/Shuttles/EntitySystems/ShuttleConsoleSystem.cs b/Content.Server/Shuttles/EntitySystems/ShuttleConsoleSystem.cs index f884a2c007..577f53677a 100644 --- a/Content.Server/Shuttles/EntitySystems/ShuttleConsoleSystem.cs +++ b/Content.Server/Shuttles/EntitySystems/ShuttleConsoleSystem.cs @@ -25,6 +25,7 @@ namespace Content.Server.Shuttles.EntitySystems [Dependency] private readonly ActionBlockerSystem _blocker = default!; [Dependency] private readonly AlertsSystem _alertsSystem = default!; [Dependency] private readonly PopupSystem _popup = default!; + [Dependency] private readonly TagSystem _tags = default!; public override void Initialize() { @@ -147,7 +148,7 @@ namespace Content.Server.Shuttles.EntitySystems /// private void HandleConsoleInteract(EntityUid uid, ShuttleConsoleComponent component, ActivateInWorldEvent args) { - if (!args.User.HasTag("CanPilot")) + if (!_tags.HasTag(args.User, "CanPilot")) { return; } diff --git a/Content.Server/Singularity/EntitySystems/ContainmentFieldGeneratorSystem.cs b/Content.Server/Singularity/EntitySystems/ContainmentFieldGeneratorSystem.cs index 373d44dfbf..c302ddb5d5 100644 --- a/Content.Server/Singularity/EntitySystems/ContainmentFieldGeneratorSystem.cs +++ b/Content.Server/Singularity/EntitySystems/ContainmentFieldGeneratorSystem.cs @@ -10,6 +10,8 @@ namespace Content.Server.Singularity.EntitySystems { public sealed class ContainmentFieldGeneratorSystem : EntitySystem { + [Dependency] private readonly TagSystem _tags = default!; + public override void Initialize() { base.Initialize(); @@ -40,7 +42,7 @@ namespace Content.Server.Singularity.EntitySystems private void HandleGeneratorCollide(EntityUid uid, ContainmentFieldGeneratorComponent component, StartCollideEvent args) { - if (args.OtherFixture.Body.Owner.HasTag("EmitterBolt")) { + if (_tags.HasTag(args.OtherFixture.Body.Owner, "EmitterBolt")) { component.ReceivePower(6); } } diff --git a/Content.Server/Throwing/ThrowHelper.cs b/Content.Server/Throwing/ThrowHelper.cs index d8d3f8bd5c..e5081d6364 100644 --- a/Content.Server/Throwing/ThrowHelper.cs +++ b/Content.Server/Throwing/ThrowHelper.cs @@ -58,7 +58,7 @@ namespace Content.Server.Throwing { comp.Thrower = user; // Give it a l'il spin. - if (!entity.HasTag("NoSpinOnThrow")) + if (!EntitySystem.Get().HasTag(entity, "NoSpinOnThrow")) { physicsComponent.ApplyAngularImpulse(ThrowAngularImpulse); } diff --git a/Content.Shared/Construction/Conditions/WallmountCondition.cs b/Content.Shared/Construction/Conditions/WallmountCondition.cs index aaec9a2233..b2e3b30a13 100644 --- a/Content.Shared/Construction/Conditions/WallmountCondition.cs +++ b/Content.Shared/Construction/Conditions/WallmountCondition.cs @@ -9,6 +9,7 @@ using Robust.Shared.Maths; using Robust.Shared.Physics; using Robust.Shared.Physics.Broadphase; using Robust.Shared.Serialization.Manager.Attributes; +using Robust.Shared.Utility; namespace Content.Shared.Construction.Conditions { @@ -39,18 +40,23 @@ namespace Content.Shared.Construction.Conditions var physics = EntitySystem.Get(); var rUserToObj = new CollisionRay(userWorldPosition, userToObject.Normalized, (int) CollisionGroup.Impassable); var length = userToObject.Length; + + var tagSystem = EntitySystem.Get(); + var userToObjRaycastResults = physics.IntersectRayWithPredicate(entManager.GetComponent(user).MapID, rUserToObj, maxLength: length, - predicate: (e) => !e.HasTag("Wall")); - if (!userToObjRaycastResults.Any()) + predicate: (e) => !tagSystem.HasTag(e, "Wall")); + + var targetWall = userToObjRaycastResults.FirstOrNull(); + + if (targetWall == null) return false; // get this wall entity - var targetWall = userToObjRaycastResults.First().HitEntity; - // check that we didn't try to build wallmount that facing another adjacent wall var rAdjWall = new CollisionRay(objWorldPosition, directionWithOffset.Normalized, (int) CollisionGroup.Impassable); var adjWallRaycastResults = physics.IntersectRayWithPredicate(entManager.GetComponent(user).MapID, rAdjWall, maxLength: 0.5f, - predicate: (e) => e == targetWall || !e.HasTag("Wall")); + predicate: (e) => e == targetWall.Value.HitEntity || !tagSystem.HasTag(e, "Wall")); + return !adjWallRaycastResults.Any(); } diff --git a/Content.Shared/Construction/Steps/MultipleTagsConstructionGraphStep.cs b/Content.Shared/Construction/Steps/MultipleTagsConstructionGraphStep.cs index d7c27e3d6e..af0a122c90 100644 --- a/Content.Shared/Construction/Steps/MultipleTagsConstructionGraphStep.cs +++ b/Content.Shared/Construction/Steps/MultipleTagsConstructionGraphStep.cs @@ -28,10 +28,12 @@ namespace Content.Shared.Construction.Steps if (!entityManager.TryGetComponent(uid, out TagComponent? tags)) return false; - if (_allTags != null && !tags.HasAllTags(_allTags)) + var tagSystem = EntitySystem.Get(); + + if (_allTags != null && !tagSystem.HasAllTags(tags, _allTags)) return false; // We don't have all the tags needed. - if (_anyTags != null && !tags.HasAnyTag(_anyTags)) + if (_anyTags != null && !tagSystem.HasAnyTag(tags, _anyTags)) return false; // We don't have any of the tags needed. // This entity is valid! diff --git a/Content.Shared/Construction/Steps/TagConstructionGraphStep.cs b/Content.Shared/Construction/Steps/TagConstructionGraphStep.cs index 3a4fb6495f..0917905768 100644 --- a/Content.Shared/Construction/Steps/TagConstructionGraphStep.cs +++ b/Content.Shared/Construction/Steps/TagConstructionGraphStep.cs @@ -12,7 +12,8 @@ namespace Content.Shared.Construction.Steps public override bool EntityValid(EntityUid uid, IEntityManager entityManager) { - return !string.IsNullOrEmpty(_tag) && entityManager.TryGetComponent(uid, out TagComponent? tags) && tags.HasTag(_tag); + var tagSystem = EntitySystem.Get(); + return !string.IsNullOrEmpty(_tag) && tagSystem.HasTag(uid, _tag); } } } diff --git a/Content.Shared/Tag/TagComponent.cs b/Content.Shared/Tag/TagComponent.cs index 5029dd5b2d..4bd10b2a25 100644 --- a/Content.Shared/Tag/TagComponent.cs +++ b/Content.Shared/Tag/TagComponent.cs @@ -1,289 +1,13 @@ -using System.Collections.Generic; -using System.Linq; -using Robust.Shared.GameObjects; -using Robust.Shared.IoC; -using Robust.Shared.Players; -using Robust.Shared.Prototypes; -using Robust.Shared.Serialization; -using Robust.Shared.Serialization.Manager.Attributes; +using Robust.Shared.Serialization; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Set; -using Robust.Shared.ViewVariables; namespace Content.Shared.Tag { - [RegisterComponent] - public class TagComponent : Component, ISerializationHooks + [RegisterComponent, Friend(typeof(TagSystem))] + public sealed class TagComponent : Component, ISerializationHooks { [ViewVariables] [DataField("tags", customTypeSerializer: typeof(PrototypeIdHashSetSerializer))] - private readonly HashSet _tags = new(); - - public IReadOnlySet Tags => _tags; - - protected override void Initialize() - { - base.Initialize(); - - foreach (var tag in _tags) - { - GetTagOrThrow(tag); - } - } - - public override ComponentState GetComponentState() - { - var tags = new string[_tags.Count]; - var i = 0; - - foreach (var tag in _tags) - { - tags[i] = tag; - } - - return new TagComponentState(tags); - } - - public override void HandleComponentState(ComponentState? curState, ComponentState? nextState) - { - if (curState is not TagComponentState state) - { - return; - } - - _tags.Clear(); - - var prototypeManager = IoCManager.Resolve(); - - foreach (var tag in state.Tags) - { - GetTagOrThrow(tag, prototypeManager); - _tags.Add(tag); - } - } - - private TagPrototype GetTagOrThrow(string id, IPrototypeManager? manager = null) - { - manager ??= IoCManager.Resolve(); - return manager.Index(id); - } - - /// - /// 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(string id) - { - GetTagOrThrow(id); - var added = _tags.Add(id); - - if (added) - { - Dirty(); - 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(params string[] ids) - { - return AddTags(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(IEnumerable ids) - { - var count = _tags.Count; - var prototypeManager = IoCManager.Resolve(); - - foreach (var id in ids) - { - GetTagOrThrow(id, prototypeManager); - _tags.Add(id); - } - - if (_tags.Count > count) - { - Dirty(); - 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(string id) - { - GetTagOrThrow(id); - return _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(params string[] ids) - { - return HasAllTags(ids.AsEnumerable()); - } - - /// - /// 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(IEnumerable ids) - { - var prototypeManager = IoCManager.Resolve(); - - foreach (var id in ids) - { - GetTagOrThrow(id, prototypeManager); - - if (!_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(params string[] ids) - { - return HasAnyTag(ids.AsEnumerable()); - } - - /// - /// 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(IEnumerable ids) - { - var prototypeManager = IoCManager.Resolve(); - - foreach (var id in ids) - { - GetTagOrThrow(id, prototypeManager); - - if (_tags.Contains(id)) - { - return true; - } - } - - return false; - } - - /// - /// Tries to remove a tag if it exists. - /// - /// 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(string id) - { - GetTagOrThrow(id); - - if (_tags.Remove(id)) - { - Dirty(); - 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(params string[] ids) - { - return RemoveTags(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(IEnumerable ids) - { - var count = _tags.Count; - var prototypeManager = IoCManager.Resolve(); - - foreach (var id in ids) - { - GetTagOrThrow(id, prototypeManager); - _tags.Remove(id); - } - - if (_tags.Count < count) - { - Dirty(); - return true; - } - - return false; - } + public readonly HashSet Tags = new(); } } diff --git a/Content.Shared/Tag/TagComponentExtensions.cs b/Content.Shared/Tag/TagComponentExtensions.cs deleted file mode 100644 index b722b8699f..0000000000 --- a/Content.Shared/Tag/TagComponentExtensions.cs +++ /dev/null @@ -1,241 +0,0 @@ -using System.Collections.Generic; -using Robust.Shared.GameObjects; -using Robust.Shared.IoC; -using Robust.Shared.Prototypes; - -namespace Content.Shared.Tag -{ - public static class TagComponentExtensions - { - /// - /// 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 static bool AddTag(this EntityUid entity, string id) - { - return entity.EnsureComponent(out TagComponent tagComponent) && - tagComponent.AddTag(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 static bool AddTags(this EntityUid entity, params string[] ids) - { - return entity.EnsureComponent(out TagComponent tagComponent) && - tagComponent.AddTags(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 static bool AddTags(this EntityUid entity, IEnumerable ids) - { - return entity.EnsureComponent(out TagComponent tagComponent) && - tagComponent.AddTags(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 static bool TryAddTag(this EntityUid entity, string id) - { - return IoCManager.Resolve().TryGetComponent(entity, out TagComponent? tagComponent) && - tagComponent.AddTag(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 static bool TryAddTags(this EntityUid entity, params string[] ids) - { - return IoCManager.Resolve().TryGetComponent(entity, out TagComponent? tagComponent) && - tagComponent.AddTags(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 static bool TryAddTags(this EntityUid entity, IEnumerable ids) - { - return IoCManager.Resolve().TryGetComponent(entity, out TagComponent? tagComponent) && - tagComponent.AddTags(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 static bool HasTag(this EntityUid entity, string id) - { - return IoCManager.Resolve().TryGetComponent(entity, out TagComponent? tagComponent) && - tagComponent.HasTag(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 static bool HasAllTags(this EntityUid entity, params string[] ids) - { - return IoCManager.Resolve().TryGetComponent(entity, out TagComponent? tagComponent) && - tagComponent.HasAllTags(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 static bool HasAllTags(this EntityUid entity, IEnumerable ids) - { - return IoCManager.Resolve().TryGetComponent(entity, out TagComponent? tagComponent) && - tagComponent.HasAllTags(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 any of them exist, false otherwise. - /// - /// Thrown if one of the ids represents an unregistered . - /// - public static bool HasAnyTag(this EntityUid entity, params string[] ids) - { - return IoCManager.Resolve().TryGetComponent(entity, out TagComponent? tagComponent) && - tagComponent.HasAnyTag(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 any of them exist, false otherwise. - /// - /// Thrown if one of the ids represents an unregistered . - /// - public static bool HasAnyTag(this EntityUid entity, IEnumerable ids) - { - return IoCManager.Resolve().TryGetComponent(entity, out TagComponent? tagComponent) && - tagComponent.HasAnyTag(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 static bool RemoveTag(this EntityUid entity, string id) - { - return IoCManager.Resolve().TryGetComponent(entity, out TagComponent? tagComponent) && - tagComponent.RemoveTag(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 static bool RemoveTags(this EntityUid entity, params string[] ids) - { - return IoCManager.Resolve().TryGetComponent(entity, out TagComponent? tagComponent) && - tagComponent.RemoveTags(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 static bool RemoveTags(this EntityUid entity, IEnumerable ids) - { - return IoCManager.Resolve().TryGetComponent(entity, out TagComponent? tagComponent) && - tagComponent.RemoveTags(ids); - } - } -} diff --git a/Content.Shared/Tag/TagSystem.cs b/Content.Shared/Tag/TagSystem.cs new file mode 100644 index 0000000000..294bcba302 --- /dev/null +++ b/Content.Shared/Tag/TagSystem.cs @@ -0,0 +1,501 @@ +using System.Linq; +using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; + +namespace Content.Shared.Tag; + +public sealed class TagSystem : EntitySystem +{ + [Dependency] private readonly IPrototypeManager _proto = default!; + + public override void Initialize() + { + base.Initialize(); + SubscribeLocalEvent(OnTagInit); + SubscribeLocalEvent(OnTagGetState); + SubscribeLocalEvent(OnTagHandleState); + } + + private void OnTagHandleState(EntityUid uid, TagComponent component, ref ComponentHandleState args) + { + if (args.Current is not TagComponentState state) + return; + + component.Tags.Clear(); + + foreach (var tag in state.Tags) + { + GetTagOrThrow(tag); + component.Tags.Add(tag); + } + } + + private static void OnTagGetState(EntityUid uid, TagComponent component, ref ComponentGetState args) + { + var tags = new string[component.Tags.Count]; + var i = 0; + + foreach (var tag in component.Tags) + { + tags[i] = tag; + i++; + } + + args.State = new TagComponentState(tags); + } + + private void OnTagInit(EntityUid uid, TagComponent component, ComponentInit args) + { + foreach (var tag in component.Tags) + { + GetTagOrThrow(tag); + } + } + + private TagPrototype GetTagOrThrow(string id) + { + return _proto.Index(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 EntityManager.EnsureComponent(entity, out var component) && + AddTag(component, 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 EntityManager.EnsureComponent(entity, out var component) && + AddTags(component, 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 EntityManager.EnsureComponent(entity, out var component) && + AddTags(component, 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 TryComp(entity, out var component) && + AddTag(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 TryComp(entity, out var component) && + AddTags(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 TryComp(entity, out var component) && + AddTags(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 TryComp(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, params string[] ids) + { + return 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 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 any of them exist, false otherwise. + /// + /// Thrown if one of the ids represents an unregistered . + /// + public bool HasAnyTag(EntityUid entity, params string[] ids) + { + return TryComp(entity, out var component) && + HasAnyTag(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 any of them exist, false otherwise. + /// + /// Thrown if one of the ids represents an unregistered . + /// + public bool HasAnyTag(EntityUid entity, IEnumerable ids) + { + return 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 TryComp(entity, out var component) && + RemoveTag(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 TryComp(entity, out var component) && + RemoveTags(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 TryComp(entity, out var component) && + RemoveTags(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(TagComponent component, string id) + { + GetTagOrThrow(id); + var added = component.Tags.Add(id); + + if (added) + { + Dirty(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(TagComponent component, params string[] ids) + { + return AddTags(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(TagComponent component, IEnumerable ids) + { + var count = component.Tags.Count; + + foreach (var id in ids) + { + GetTagOrThrow(id); + component.Tags.Add(id); + } + + if (component.Tags.Count > count) + { + Dirty(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) + { + GetTagOrThrow(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 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) + { + GetTagOrThrow(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) + { + return HasAnyTag(component, ids.AsEnumerable()); + } + + /// + /// 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) + { + GetTagOrThrow(id); + + if (component.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(TagComponent component, string id) + { + GetTagOrThrow(id); + + if (component.Tags.Remove(id)) + { + Dirty(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(TagComponent component, params string[] ids) + { + return RemoveTags(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(TagComponent component, IEnumerable ids) + { + var count = component.Tags.Count; + + foreach (var id in ids) + { + GetTagOrThrow(id); + component.Tags.Remove(id); + } + + if (component.Tags.Count < count) + { + Dirty(component); + return true; + } + + return false; + } +} diff --git a/Content.Shared/Whitelist/EntityWhitelist.cs b/Content.Shared/Whitelist/EntityWhitelist.cs index ccf3ed53cf..da68546f53 100644 --- a/Content.Shared/Whitelist/EntityWhitelist.cs +++ b/Content.Shared/Whitelist/EntityWhitelist.cs @@ -72,10 +72,11 @@ namespace Content.Shared.Whitelist public bool IsValid(EntityUid uid, IEntityManager? entityManager = null) { entityManager ??= IoCManager.Resolve(); + var tagSystem = EntitySystem.Get(); if (Tags != null && entityManager.TryGetComponent(uid, out TagComponent? tags)) { - if (tags.HasAnyTag(Tags)) + if (tagSystem.HasAnyTag(tags, Tags)) return true; }