]> git.smokeofanarchy.ru Git - space-station-14.git/commitdiff
Refactor: ProximityDetectionSystem (#35133)
authorWinkarst <74284083+Winkarst-cpu@users.noreply.github.com>
Sun, 11 May 2025 14:33:35 +0000 (17:33 +0300)
committerGitHub <noreply@github.com>
Sun, 11 May 2025 14:33:35 +0000 (00:33 +1000)
* Refactor: ProximityDetectionSystem

* Update

* Update

* Update

* Yikes

* Update

* Dirty

* Update

* Update

* Lil cleanup

* Update

* Update

Content.Shared/Beeper/Systems/ProximityBeeperSystem.cs
Content.Shared/ProximityDetection/Components/ProximityDetectorComponent.cs
Content.Shared/ProximityDetection/ProximityDetectionEvents.cs
Content.Shared/ProximityDetection/Systems/ProximityDetectionSystem.cs
Resources/Prototypes/Entities/Objects/Fun/spectral_locator.yml
Resources/Prototypes/Entities/Objects/Specific/Research/anomaly.yml

index bee75e948d8811f94271569ecb8ef33d4e60a3d7..546532c3d41313976162d9ae89010cd84ede1162 100644 (file)
@@ -25,14 +25,8 @@ public sealed class ProximityBeeperSystem : EntitySystem
     {
         if (!TryComp<BeeperComponent>(owner, out var beeper))
             return;
-        if (args.Target == null)
-        {
-            _beeper.SetMute(owner, true, beeper);
-            return;
-        }
 
-        _beeper.SetIntervalScaling(owner, args.Distance / args.Detector.Range, beeper);
-        _beeper.SetMute(owner, false, beeper);
+        _beeper.SetIntervalScaling(owner, args.Distance / args.Detector.Comp.Range, beeper);
     }
 
     private void OnNewProximityTarget(EntityUid owner, ProximityBeeperComponent proxBeeper, ref NewProximityTargetEvent args)
index 7e2bb4dfe620f14dca77e892520559e8b86349e8..8f8d37a27d86f3eaa6ef20bbb178a8add0fb92ad 100644 (file)
@@ -1,43 +1,51 @@
-using Content.Shared.FixedPoint;
-using Content.Shared.ProximityDetection.Systems;
-using Content.Shared.Whitelist;
+using Content.Shared.ProximityDetection.Systems;
 using Robust.Shared.GameStates;
+using Robust.Shared.Prototypes;
+using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
 
 namespace Content.Shared.ProximityDetection.Components;
+
 /// <summary>
-/// This is used to search for the closest entity with a range that matches specified requirements (tags and/or components)
+/// Used to search for the closest entity with a range that matches specified requirements (tags and/or components).
 /// </summary>
-[RegisterComponent, NetworkedComponent, AutoGenerateComponentState ,Access(typeof(ProximityDetectionSystem))]
+[RegisterComponent, NetworkedComponent]
+[AutoGenerateComponentState(fieldDeltas: true), AutoGenerateComponentPause]
+[Access(typeof(ProximityDetectionSystem))]
 public sealed partial class ProximityDetectorComponent : Component
 {
     /// <summary>
-    /// The criteria used to filter entities
-    /// Note: RequireAll is only supported for tags, all components are required to count as a match!
+    /// Entities that detector will search for.
     /// </summary>
-    [DataField( required: true), AutoNetworkedField, ViewVariables(VVAccess.ReadWrite)]
-    public EntityWhitelist Criteria = new();
+    [DataField(required: true)]
+    public ComponentRegistry Components;
 
     /// <summary>
-    /// Found Entity
+    /// The entity that was found.
     /// </summary>
-    [ViewVariables(VVAccess.ReadWrite), AutoNetworkedField]
-    public EntityUid? TargetEnt;
+    [ViewVariables, AutoNetworkedField]
+    public EntityUid? Target;
 
     /// <summary>
-    /// Distance to Found Entity
+    /// The distance to <see cref="Target"/>.
     /// </summary>
-    [ViewVariables(VVAccess.ReadWrite), AutoNetworkedField]
-    public FixedPoint2 Distance = -1;
+    [ViewVariables, AutoNetworkedField]
+    public float Distance = float.PositiveInfinity;
 
     /// <summary>
-    /// The farthest distance to search for targets
+    /// The farthest distance to search for targets.
     /// </summary>
-    [DataField, ViewVariables(VVAccess.ReadWrite), AutoNetworkedField]
-    public FixedPoint2 Range = 10f;
+    [DataField, AutoNetworkedField]
+    public float Range = 10f;
 
-    // TODO: use timespans not this
-    public float AccumulatedFrameTime;
+    /// <summary>
+    /// How often detector updates.
+    /// </summary>
+    [DataField, AutoNetworkedField]
+    public TimeSpan UpdateCooldown = TimeSpan.FromSeconds(1);
 
-    [DataField, ViewVariables(VVAccess.ReadWrite), AutoNetworkedField]
-    public float UpdateRate = 0.3f;
+    /// <summary>
+    /// Next time detector updates.
+    /// </summary>
+    [DataField(customTypeSerializer: typeof(TimeOffsetSerializer)), AutoNetworkedField, AutoPausedField]
+    public TimeSpan NextUpdate = TimeSpan.Zero;
 }
index 0a0ac554bd631a63a938cc271ada7f36bbd0a4b8..1c235f6ce4ea94b8ac0b69004328d8c253e2660c 100644 (file)
@@ -1,17 +1,27 @@
-using Content.Shared.FixedPoint;
-using Content.Shared.ProximityDetection.Components;
-using Robust.Shared.Serialization;
+using Content.Shared.ProximityDetection.Components;
 
 namespace Content.Shared.ProximityDetection;
 
+/// <summary>
+/// Raised to determine if proximity sensor can detect an entity.
+/// </summary>
 [ByRefEvent]
-public record struct ProximityDetectionAttemptEvent(bool Cancel, FixedPoint2 Distance, Entity<ProximityDetectorComponent> Detector);
+public struct ProximityDetectionAttemptEvent(float distance, Entity<ProximityDetectorComponent> detector, EntityUid target)
+{
+    public bool Cancelled;
+    public readonly float Distance = distance;
+    public readonly Entity<ProximityDetectorComponent> Detector = detector;
+    public readonly EntityUid Target = target;
+}
 
+/// <summary>
+/// Raised when distance from proximity sensor to the target was updated.
+/// </summary>
 [ByRefEvent]
-public record struct ProximityTargetUpdatedEvent(ProximityDetectorComponent Detector, EntityUid? Target, FixedPoint2 Distance);
+public readonly record struct ProximityTargetUpdatedEvent(float Distance, Entity<ProximityDetectorComponent> Detector, EntityUid? Target = null);
 
+/// <summary>
+/// Raised when proximity sensor got new target.
+/// </summary>
 [ByRefEvent]
-public record struct NewProximityTargetEvent(ProximityDetectorComponent Detector, EntityUid? Target);
-
-
-
+public readonly record struct NewProximityTargetEvent(float Distance, Entity<ProximityDetectorComponent> Detector, EntityUid? Target = null);
index df302f94771e3d75619faa295207d91177e1aa0f..a00ec1d0c6dc4954f0e22b3a158ee162af38b4bf 100644 (file)
 using Content.Shared.Item.ItemToggle;
-using Content.Shared.Item.ItemToggle.Components;
+using Content.Shared.Item.ItemToggle.Components;
 using Content.Shared.ProximityDetection.Components;
-using Content.Shared.Tag;
-using Robust.Shared.Network;
+using Robust.Shared.Timing;
 
 namespace Content.Shared.ProximityDetection.Systems;
 
-
-//This handles generic proximity detector logic
+/// <summary>
+/// Handles generic proximity detector logic.
+/// </summary>
 public sealed class ProximityDetectionSystem : EntitySystem
 {
-    [Dependency] private readonly EntityLookupSystem _entityLookup = default!;
+    [Dependency] private readonly IGameTiming _timing = default!;
     [Dependency] private readonly ItemToggleSystem _toggle = default!;
-    [Dependency] private readonly SharedTransformSystem _transform = default!;
-    [Dependency] private readonly TagSystem _tagSystem = default!;
-    [Dependency] private readonly INetManager _net = default!;
 
-    //update is only run on the server
+    private EntityQuery<TransformComponent> _xformQuery;
 
     public override void Initialize()
     {
         base.Initialize();
 
-        SubscribeLocalEvent<ProximityDetectorComponent, ComponentInit>(OnCompInit);
+        SubscribeLocalEvent<ProximityDetectorComponent, MapInitEvent>(OnMapInit);
         SubscribeLocalEvent<ProximityDetectorComponent, ItemToggledEvent>(OnToggled);
+
+        _xformQuery = GetEntityQuery<TransformComponent>();
     }
 
-    private void OnCompInit(EntityUid uid, ProximityDetectorComponent component, ComponentInit args)
+    private void OnMapInit(Entity<ProximityDetectorComponent> ent, ref MapInitEvent args)
     {
-        if (component.Criteria.RequireAll)
-            return;
-        Log.Debug("DetectorComponent only supports requireAll = false for tags. All components are required for a match!");
+        var component = ent.Comp;
+
+        component.NextUpdate = _timing.CurTime + component.UpdateCooldown;
+        DirtyField(ent, component, nameof(ProximityDetectorComponent.NextUpdate));
     }
 
-    public override void Update(float frameTime)
+    private void OnToggled(Entity<ProximityDetectorComponent> ent, ref ItemToggledEvent args)
     {
-        if (_net.IsClient)
-            return;
+        if (args.Activated)
+            UpdateTarget(ent);
+        else
+            ClearTarget(ent);
+    }
 
+    public override void Update(float frameTime)
+    {
         var query = EntityQueryEnumerator<ProximityDetectorComponent>();
-        while (query.MoveNext(out var owner, out var detector))
+
+        while (query.MoveNext(out var uid, out var component))
         {
-            if (!_toggle.IsActivated(owner))
+            if (component.NextUpdate > _timing.CurTime)
                 continue;
 
-            detector.AccumulatedFrameTime += frameTime;
-            if (detector.AccumulatedFrameTime < detector.UpdateRate)
+            component.NextUpdate += component.UpdateCooldown;
+            DirtyField(uid, component, nameof(ProximityDetectorComponent.NextUpdate));
+
+            if (!_toggle.IsActivated(uid))
                 continue;
 
-            detector.AccumulatedFrameTime -= detector.UpdateRate;
-            RunUpdate_Internal(owner, detector);
+            UpdateTarget((uid, component));
         }
     }
 
-    private void OnToggled(Entity<ProximityDetectorComponent> ent, ref ItemToggledEvent args)
+    private void ClearTarget(Entity<ProximityDetectorComponent> ent)
     {
-        if (args.Activated)
-        {
-            RunUpdate_Internal(ent, ent.Comp);
+        var component = ent.Comp;
+
+        // Don't do anything if we have no target.
+        if (component.Target == null)
             return;
-        }
 
-        var noDetectEvent = new ProximityTargetUpdatedEvent(ent.Comp, Target: null, ent.Comp.Distance);
-        RaiseLocalEvent(ent, ref noDetectEvent);
+        component.Distance = float.PositiveInfinity;
+        DirtyField(ent, component, nameof(ProximityDetectorComponent.Distance));
 
-        ent.Comp.AccumulatedFrameTime = 0;
-        Dirty(ent, ent.Comp);
-    }
+        component.Target = null;
+        DirtyField(ent, component, nameof(ProximityDetectorComponent.Target));
 
-    public void ForceUpdate(EntityUid owner, ProximityDetectorComponent? detector = null)
-    {
-        if (!Resolve(owner, ref detector))
-            return;
-        RunUpdate_Internal(owner, detector);
-    }
+        var updatedEv = new ProximityTargetUpdatedEvent(component.Distance, ent);
+        RaiseLocalEvent(ent, ref updatedEv);
 
-    private void ClearTarget(Entity<ProximityDetectorComponent> ent)
-    {
-        var (uid, comp) = ent;
-        if (comp.TargetEnt == null)
-            return;
-
-        comp.Distance = -1;
-        comp.TargetEnt = null;
-        var noDetectEvent = new ProximityTargetUpdatedEvent(comp, null, -1);
-        RaiseLocalEvent(uid, ref noDetectEvent);
-        var newTargetEvent = new NewProximityTargetEvent(comp, null);
-        RaiseLocalEvent(uid, ref newTargetEvent);
-        Dirty(uid, comp);
+        var newTargetEv = new NewProximityTargetEvent(component.Distance, ent);
+        RaiseLocalEvent(ent, ref newTargetEv);
     }
 
-    private void RunUpdate_Internal(EntityUid owner,ProximityDetectorComponent detector)
+    private void UpdateTarget(Entity<ProximityDetectorComponent> detector)
     {
-        if (!_net.IsServer) //only run detection checks on the server!
+        var component = detector.Comp;
+
+        if (!_xformQuery.TryGetComponent(detector, out var transform))
             return;
 
-        if (Deleted(detector.TargetEnt))
-        {
-            ClearTarget((owner, detector));
-        }
+        if (Deleted(component.Target))
+            ClearTarget(detector);
 
-        var xformQuery = GetEntityQuery<TransformComponent>();
-        var xform = xformQuery.GetComponent(owner);
-        List<(EntityUid TargetEnt, float Distance)> detections = new();
+        var closestDistance = float.PositiveInfinity;
+        EntityUid? closestUid = null;
 
-        if (detector.Criteria.Components == null)
-        {
-            Log.Error($"ProximityDetectorComponent on {ToPrettyString(owner)} must use at least 1 component as a filter in criteria!");
-            throw new ArgumentException($"ProximityDetectorComponent on {ToPrettyString(owner)} must use at least 1 component as a filter in criteria!");
-        }
-        var firstCompType = EntityManager.ComponentFactory.GetRegistration(detector.Criteria.Components[0]).Type;
-        var foundEnts = _entityLookup.GetEntitiesInRange(firstCompType,_transform.GetMapCoordinates(owner, xform), detector.Range.Float());
+        var query = EntityManager.CompRegistryQueryEnumerator(component.Components);
 
-        var tagSearchEnabled = detector.Criteria.Tags is {Count: > 0};
+        while (query.MoveNext(out var uid))
+        {
+            if (!_xformQuery.TryGetComponent(uid, out var xForm))
+                continue;
 
-        CheckForAllComponentsPresent(detector, ref foundEnts, tagSearchEnabled);
+            if (!transform.Coordinates.TryDistance(EntityManager, xForm.Coordinates, out var distance) ||
+                distance > component.Range || distance >= closestDistance)
+                continue;
 
-        if (foundEnts.Count == 0)
-        {
-            UpdateTargetFromClosest(owner, detector, detections);
-            return;
-        }
+            var detectAttempt = new ProximityDetectionAttemptEvent(distance, detector, uid);
+            RaiseLocalEvent(detector, ref detectAttempt);
 
-        foreach (var ent in foundEnts)
-        {
-            if (tagSearchEnabled && ent.Comp is TagComponent tags && (detector.Criteria.RequireAll
-                    ? _tagSystem.HasAllTags(tags, detector.Criteria.Tags!)
-                    : _tagSystem.HasAnyTag(tags, detector.Criteria.Tags!)))
+            if (detectAttempt.Cancelled)
                 continue;
-            var distance = (_transform.GetWorldPosition(xform, xformQuery) - _transform.GetWorldPosition(ent, xformQuery)).Length();
-            if (CheckDetectConditions(ent, distance, owner, detector))
-            {
-                detections.Add((ent, distance));
-            }
-        }
-        UpdateTargetFromClosest(owner, detector, detections);
-    }
 
-    private void CheckForAllComponentsPresent(ProximityDetectorComponent detector, ref HashSet<Entity<IComponent>> foundEnts, bool tagSearchEnabled)
-    {
-        var validEnts = new HashSet<Entity<IComponent>>(foundEnts.Count);
-        for (var i = 1; i < detector.Criteria.Components!.Length; i++)
-        {
-            validEnts.Clear();
-            var compType = EntityManager.ComponentFactory.GetRegistration(detector.Criteria.Components[i]).Type;
-            foreach (var ent in foundEnts)
-            {
-                if (!HasComp(ent, compType))
-                    continue;
-                validEnts.Add(ent);
-            }
-            (foundEnts, validEnts) = (validEnts, foundEnts);
+            closestDistance = distance;
+            closestUid = uid;
         }
-        validEnts.Clear();
-        if (tagSearchEnabled)
-        {
-            foreach (var ent in foundEnts)
-            {
-                if (!HasComp<TagComponent>(ent))
-                    continue;
-                validEnts.Add(ent);
-            }
-            (foundEnts, validEnts) = (validEnts, foundEnts);
-            validEnts.Clear();
-        }
-    }
 
+        var newDistance = component.Distance != closestDistance;
+        var newTarget = component.Target != closestUid;
 
-    private bool CheckDetectConditions(EntityUid targetEntity, float dist, EntityUid owner, ProximityDetectorComponent detector)
-    {
-        var detectAttempt = new ProximityDetectionAttemptEvent(false, dist, (owner, detector));
-        RaiseLocalEvent(targetEntity, ref detectAttempt);
-        return !detectAttempt.Cancel;
-    }
-
-    private void UpdateTargetFromClosest(EntityUid owner, ProximityDetectorComponent detector, List<(EntityUid TargetEnt, float Distance)> detections)
-    {
-        if (detections.Count == 0)
-        {
-            ClearTarget((owner, detector));
-            return;
-        }
-        var closestDistance = detections[0].Distance;
-        EntityUid closestEnt = default!;
-        foreach (var (ent,dist) in detections)
+        if (newDistance)
         {
-            if (dist >= closestDistance)
-                continue;
-            closestEnt = ent;
-            closestDistance = dist;
+            var updatedEv = new ProximityTargetUpdatedEvent(closestDistance, detector, closestUid);
+            RaiseLocalEvent(detector, ref updatedEv);
+
+            component.Distance = closestDistance;
+            DirtyField(detector, component, nameof(ProximityDetectorComponent.Distance));
         }
 
-        var newTarget = detector.TargetEnt != closestEnt;
-        var newData = newTarget || detector.Distance != closestDistance;
-        detector.TargetEnt = closestEnt;
-        detector.Distance = closestDistance;
-        Dirty(owner, detector);
         if (newTarget)
         {
-            var newTargetEvent = new NewProximityTargetEvent(detector, closestEnt);
-            RaiseLocalEvent(owner, ref newTargetEvent);
-        }
-
-        if (!newData)
-            return;
-        var targetUpdatedEvent = new ProximityTargetUpdatedEvent(detector, closestEnt, closestDistance);
-        RaiseLocalEvent(owner, ref targetUpdatedEvent);
-        Dirty(owner, detector);
-    }
+            var newTargetEv = new NewProximityTargetEvent(closestDistance, detector, closestUid);
+            RaiseLocalEvent(detector, ref newTargetEv);
 
-    public void SetRange(EntityUid owner, float newRange, ProximityDetectorComponent? detector = null)
-    {
-        if (!Resolve(owner, ref detector))
-            return;
-        detector.Range = newRange;
-        Dirty(owner, detector);
+            component.Target = closestUid;
+            DirtyField(detector, component, nameof(ProximityDetectorComponent.Target));
+        }
     }
 }
index 930b9c4926d1daff5e83e9420aca891d550dcf81..e0ec2f9fd314e106dcf8c9ee07c7f891089c1092 100644 (file)
@@ -24,9 +24,8 @@
   - type: ProximityBeeper
   - type: ProximityDetector
     range: 12
-    criteria:
-      components:
-      - Spectral # reacts to AI eye, intentional
+    components:
+    - type: Spectral # reacts to AI eye, intentional
   - type: Beeper
     isMuted: true
     minBeepInterval: 0.25
index 0872f0fc3eb0222dc8214517f116804bbb49ae2a..05ddb4616586c60e9b58684eb8013a28869f0456 100644 (file)
@@ -48,9 +48,8 @@
   - type: ProximityBeeper
   - type: ProximityDetector
     range: 20
-    criteria:
-      components:
-      - Anomaly
+    components:
+    - type: Anomaly
   - type: Beeper
     isMuted: true
     minBeepInterval: 0.15