]> git.smokeofanarchy.ru Git - space-station-14.git/commitdiff
add StationTrackerComponent (#36803)
authorMilon <milonpl.git@proton.me>
Wed, 30 Apr 2025 14:07:25 +0000 (16:07 +0200)
committerGitHub <noreply@github.com>
Wed, 30 Apr 2025 14:07:25 +0000 (00:07 +1000)
* maybe I am cooking

* logmissing

* copy paste oops

* add some stuff

* review

* fix

* rerun tests

---------

Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com>
Content.Client/Station/StationSystem.cs
Content.Server/Station/Systems/StationSystem.cs
Content.Shared/Station/Components/StationTrackerComponent.cs [new file with mode: 0644]
Content.Shared/Station/SharedStationSystem.cs [new file with mode: 0644]

index 2a3e4850cfa4f629a28ea4a74cd026560e3db1c2..5a4a1853d2464e8eab9215b31ef63789486b503b 100644 (file)
@@ -5,7 +5,7 @@ namespace Content.Client.Station;
 /// <summary>
 /// This handles letting the client know stations are a thing. Only really used by an admin menu.
 /// </summary>
-public sealed class StationSystem : EntitySystem
+public sealed partial class StationSystem : SharedStationSystem
 {
     private readonly List<(string Name, NetEntity Entity)> _stations = new();
 
@@ -15,11 +15,14 @@ public sealed class StationSystem : EntitySystem
     /// <remarks>
     /// I'd have this just invoke an entity query, but we're on the client and the client barely knows about stations.
     /// </remarks>
+    // TODO: Stations have a global PVS override now, this can probably be changed into a query.
     public IReadOnlyList<(string Name, NetEntity Entity)> Stations => _stations;
 
     /// <inheritdoc/>
     public override void Initialize()
     {
+        base.Initialize();
+
         SubscribeNetworkEvent<StationsUpdatedEvent>(StationsUpdated);
     }
 
index d80e2d08d1c37ff9d622b73e9a6406c30d2f6ad5..456d3c6b802f08a798657e61690360a34f5b2060 100644 (file)
@@ -27,7 +27,7 @@ namespace Content.Server.Station.Systems;
 /// For jobs, look at StationJobSystem. For spawning, look at StationSpawningSystem.
 /// </summary>
 [PublicAPI]
-public sealed class StationSystem : EntitySystem
+public sealed partial class StationSystem : SharedStationSystem
 {
     [Dependency] private readonly ILogManager _logManager = default!;
     [Dependency] private readonly IPlayerManager _player = default!;
@@ -48,6 +48,8 @@ public sealed class StationSystem : EntitySystem
     /// <inheritdoc/>
     public override void Initialize()
     {
+        base.Initialize();
+
         _sawmill = _logManager.GetSawmill("station");
 
         _gridQuery = GetEntityQuery<MapGridComponent>();
@@ -60,6 +62,9 @@ public sealed class StationSystem : EntitySystem
         SubscribeLocalEvent<StationMemberComponent, ComponentShutdown>(OnStationGridDeleted);
         SubscribeLocalEvent<StationMemberComponent, PostGridSplitEvent>(OnStationSplitEvent);
 
+        SubscribeLocalEvent<StationGridAddedEvent>(OnStationGridAdded);
+        SubscribeLocalEvent<StationGridRemovedEvent>(OnStationGridRemoved);
+
         _player.PlayerStatusChanged += OnPlayerStatusChanged;
     }
 
@@ -90,6 +95,18 @@ public sealed class StationSystem : EntitySystem
         }
     }
 
+    private void UpdateTrackersOnGrid(EntityUid gridId, EntityUid? station)
+    {
+        var query = EntityQueryEnumerator<StationTrackerComponent, TransformComponent>();
+        while (query.MoveNext(out var uid, out var tracker, out var xform))
+        {
+            if (xform.GridUid == gridId)
+            {
+                SetStation((uid, tracker), station);
+            }
+        }
+    }
+
     #region Event handlers
 
     private void OnStationAdd(EntityUid uid, StationDataComponent component, ComponentStartup args)
@@ -107,6 +124,9 @@ public sealed class StationSystem : EntitySystem
         foreach (var grid in component.Grids)
         {
             RemComp<StationMemberComponent>(grid);
+
+            // If the station gets deleted, we raise the event for every grid that was a part of it
+            RaiseLocalEvent(new StationGridRemovedEvent(grid, uid));
         }
 
         RaiseNetworkEvent(new StationsUpdatedEvent(GetStationNames()), Filter.Broadcast());
@@ -159,6 +179,18 @@ public sealed class StationSystem : EntitySystem
         }
     }
 
+    private void OnStationGridAdded(StationGridAddedEvent ev)
+    {
+        // When a grid is added to a station, update all trackers on that grid
+        UpdateTrackersOnGrid(ev.GridId, ev.Station);
+    }
+
+    private void OnStationGridRemoved(StationGridRemovedEvent ev)
+    {
+        // When a grid is removed from a station, update all trackers on that grid to null
+        UpdateTrackersOnGrid(ev.GridId, null);
+    }
+
     #endregion Event handlers
 
     /// <summary>
@@ -353,7 +385,7 @@ public sealed class StationSystem : EntitySystem
         stationMember.Station = station;
         stationData.Grids.Add(mapGrid);
 
-        RaiseLocalEvent(station, new StationGridAddedEvent(mapGrid, false), true);
+        RaiseLocalEvent(station, new StationGridAddedEvent(mapGrid, station, false), true);
 
         _sawmill.Info($"Adding grid {mapGrid} to station {Name(station)} ({station})");
     }
@@ -376,7 +408,7 @@ public sealed class StationSystem : EntitySystem
         RemComp<StationMemberComponent>(mapGrid);
         stationData.Grids.Remove(mapGrid);
 
-        RaiseLocalEvent(station, new StationGridRemovedEvent(mapGrid), true);
+        RaiseLocalEvent(station, new StationGridRemovedEvent(mapGrid, station), true);
         _sawmill.Info($"Removing grid {mapGrid} from station {Name(station)} ({station})");
     }
 
@@ -553,15 +585,21 @@ public sealed class StationGridAddedEvent : EntityEventArgs
     /// </summary>
     public EntityUid GridId;
 
+    /// <summary>
+    /// EntityUid of the station this grid was added to.
+    /// </summary>
+    public EntityUid Station;
+
     /// <summary>
     /// Indicates that the event was fired during station setup,
     /// so that it can be ignored if StationInitializedEvent was already handled.
     /// </summary>
     public bool IsSetup;
 
-    public StationGridAddedEvent(EntityUid gridId, bool isSetup)
+    public StationGridAddedEvent(EntityUid gridId, EntityUid station, bool isSetup)
     {
         GridId = gridId;
+        Station = station;
         IsSetup = isSetup;
     }
 }
@@ -577,9 +615,15 @@ public sealed class StationGridRemovedEvent : EntityEventArgs
     /// </summary>
     public EntityUid GridId;
 
-    public StationGridRemovedEvent(EntityUid gridId)
+    /// <summary>
+    /// EntityUid of the station this grid was added to.
+    /// </summary>
+    public EntityUid Station;
+
+    public StationGridRemovedEvent(EntityUid gridId, EntityUid station)
     {
         GridId = gridId;
+        Station = station;
     }
 }
 
diff --git a/Content.Shared/Station/Components/StationTrackerComponent.cs b/Content.Shared/Station/Components/StationTrackerComponent.cs
new file mode 100644 (file)
index 0000000..6272b28
--- /dev/null
@@ -0,0 +1,18 @@
+using Robust.Shared.GameStates;
+
+namespace Content.Shared.Station.Components;
+
+/// <summary>
+/// Component that tracks which station an entity is currently on.
+/// Mainly used for UI purposes on the client to easily get station-specific data like alert levels.
+/// </summary>
+[RegisterComponent, NetworkedComponent, AutoGenerateComponentState, Access(typeof(SharedStationSystem))]
+public sealed partial class StationTrackerComponent : Component
+{
+    /// <summary>
+    /// The station this entity is currently on, if any.
+    /// Null when in space or not on any grid.
+    /// </summary>
+    [DataField, AutoNetworkedField]
+    public EntityUid? Station;
+}
diff --git a/Content.Shared/Station/SharedStationSystem.cs b/Content.Shared/Station/SharedStationSystem.cs
new file mode 100644 (file)
index 0000000..c067af6
--- /dev/null
@@ -0,0 +1,111 @@
+using Content.Shared.Station.Components;
+using JetBrains.Annotations;
+using Robust.Shared.Map;
+
+namespace Content.Shared.Station;
+
+public abstract partial class SharedStationSystem : EntitySystem
+{
+    [Dependency] private readonly MetaDataSystem _meta = default!;
+
+    private EntityQuery<TransformComponent> _xformQuery;
+    private EntityQuery<StationMemberComponent> _stationMemberQuery;
+
+    /// <inheritdoc/>
+    public override void Initialize()
+    {
+        base.Initialize();
+
+        _xformQuery = GetEntityQuery<TransformComponent>();
+        _stationMemberQuery = GetEntityQuery<StationMemberComponent>();
+
+        SubscribeLocalEvent<StationTrackerComponent, MapInitEvent>(OnTrackerMapInit);
+        SubscribeLocalEvent<StationTrackerComponent, ComponentRemove>(OnTrackerRemove);
+        SubscribeLocalEvent<StationTrackerComponent, GridUidChangedEvent>(OnTrackerGridChanged);
+        SubscribeLocalEvent<StationTrackerComponent, MetaFlagRemoveAttemptEvent>(OnMetaFlagRemoveAttempt);
+    }
+
+    private void OnTrackerMapInit(Entity<StationTrackerComponent> ent, ref MapInitEvent args)
+    {
+        _meta.AddFlag(ent, MetaDataFlags.ExtraTransformEvents);
+        UpdateStationTracker(ent.AsNullable());
+    }
+
+    private void OnTrackerRemove(Entity<StationTrackerComponent> ent, ref ComponentRemove args)
+    {
+        _meta.RemoveFlag(ent, MetaDataFlags.ExtraTransformEvents);
+    }
+
+    private void OnTrackerGridChanged(Entity<StationTrackerComponent> ent, ref GridUidChangedEvent args)
+    {
+        UpdateStationTracker((ent, ent.Comp, args.Transform));
+    }
+
+    private void OnMetaFlagRemoveAttempt(Entity<StationTrackerComponent> ent, ref MetaFlagRemoveAttemptEvent args)
+    {
+        if ((args.ToRemove & MetaDataFlags.ExtraTransformEvents) != 0 &&
+            ent.Comp.LifeStage <= ComponentLifeStage.Running)
+        {
+            args.ToRemove &= ~MetaDataFlags.ExtraTransformEvents;
+        }
+    }
+
+    /// <summary>
+    /// Updates the station tracker component based on entity's current location.
+    /// </summary>
+    [PublicAPI]
+    public void UpdateStationTracker(Entity<StationTrackerComponent?, TransformComponent?> ent)
+    {
+        if (!Resolve(ent, ref ent.Comp1))
+            return;
+
+        var xform = ent.Comp2;
+
+        if (!_xformQuery.Resolve(ent, ref xform))
+            return;
+
+        // Entity is in nullspace or not on a grid
+        if (xform.MapID == MapId.Nullspace || xform.GridUid == null)
+        {
+            SetStation(ent, null);
+            return;
+        }
+
+        // Check if the grid is part of a station
+        if (!_stationMemberQuery.TryGetComponent(xform.GridUid.Value, out var stationMember))
+        {
+            SetStation(ent, null);
+            return;
+        }
+
+        SetStation(ent, stationMember.Station);
+    }
+
+    /// <summary>
+    /// Sets the station for a StationTrackerComponent.
+    /// </summary>
+    [PublicAPI]
+    public void SetStation(Entity<StationTrackerComponent?> ent, EntityUid? station)
+    {
+        if (!Resolve(ent, ref ent.Comp))
+            return;
+
+        if (ent.Comp.Station == station)
+            return;
+
+        ent.Comp.Station = station;
+        Dirty(ent);
+    }
+
+    /// <summary>
+    /// Gets the station an entity is currently on, if any.
+    /// </summary>
+    [PublicAPI]
+    public EntityUid? GetCurrentStation(Entity<StationTrackerComponent?> ent)
+    {
+        if (!Resolve(ent, ref ent.Comp, logMissing: false))
+            return null;
+
+        return ent.Comp.Station;
+    }
+}