add StationTrackerComponent (#36803)

* 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>
This commit is contained in:
Milon
2025-04-30 16:07:25 +02:00
committed by GitHub
parent feffbea9f9
commit a8ff999b08
4 changed files with 182 additions and 6 deletions

View File

@@ -5,7 +5,7 @@ namespace Content.Client.Station;
/// <summary> /// <summary>
/// This handles letting the client know stations are a thing. Only really used by an admin menu. /// This handles letting the client know stations are a thing. Only really used by an admin menu.
/// </summary> /// </summary>
public sealed class StationSystem : EntitySystem public sealed partial class StationSystem : SharedStationSystem
{ {
private readonly List<(string Name, NetEntity Entity)> _stations = new(); private readonly List<(string Name, NetEntity Entity)> _stations = new();
@@ -15,11 +15,14 @@ public sealed class StationSystem : EntitySystem
/// <remarks> /// <remarks>
/// I'd have this just invoke an entity query, but we're on the client and the client barely knows about stations. /// I'd have this just invoke an entity query, but we're on the client and the client barely knows about stations.
/// </remarks> /// </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; public IReadOnlyList<(string Name, NetEntity Entity)> Stations => _stations;
/// <inheritdoc/> /// <inheritdoc/>
public override void Initialize() public override void Initialize()
{ {
base.Initialize();
SubscribeNetworkEvent<StationsUpdatedEvent>(StationsUpdated); SubscribeNetworkEvent<StationsUpdatedEvent>(StationsUpdated);
} }

View File

@@ -27,7 +27,7 @@ namespace Content.Server.Station.Systems;
/// For jobs, look at StationJobSystem. For spawning, look at StationSpawningSystem. /// For jobs, look at StationJobSystem. For spawning, look at StationSpawningSystem.
/// </summary> /// </summary>
[PublicAPI] [PublicAPI]
public sealed class StationSystem : EntitySystem public sealed partial class StationSystem : SharedStationSystem
{ {
[Dependency] private readonly ILogManager _logManager = default!; [Dependency] private readonly ILogManager _logManager = default!;
[Dependency] private readonly IPlayerManager _player = default!; [Dependency] private readonly IPlayerManager _player = default!;
@@ -48,6 +48,8 @@ public sealed class StationSystem : EntitySystem
/// <inheritdoc/> /// <inheritdoc/>
public override void Initialize() public override void Initialize()
{ {
base.Initialize();
_sawmill = _logManager.GetSawmill("station"); _sawmill = _logManager.GetSawmill("station");
_gridQuery = GetEntityQuery<MapGridComponent>(); _gridQuery = GetEntityQuery<MapGridComponent>();
@@ -60,6 +62,9 @@ public sealed class StationSystem : EntitySystem
SubscribeLocalEvent<StationMemberComponent, ComponentShutdown>(OnStationGridDeleted); SubscribeLocalEvent<StationMemberComponent, ComponentShutdown>(OnStationGridDeleted);
SubscribeLocalEvent<StationMemberComponent, PostGridSplitEvent>(OnStationSplitEvent); SubscribeLocalEvent<StationMemberComponent, PostGridSplitEvent>(OnStationSplitEvent);
SubscribeLocalEvent<StationGridAddedEvent>(OnStationGridAdded);
SubscribeLocalEvent<StationGridRemovedEvent>(OnStationGridRemoved);
_player.PlayerStatusChanged += OnPlayerStatusChanged; _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 #region Event handlers
private void OnStationAdd(EntityUid uid, StationDataComponent component, ComponentStartup args) private void OnStationAdd(EntityUid uid, StationDataComponent component, ComponentStartup args)
@@ -107,6 +124,9 @@ public sealed class StationSystem : EntitySystem
foreach (var grid in component.Grids) foreach (var grid in component.Grids)
{ {
RemComp<StationMemberComponent>(grid); 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()); 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 #endregion Event handlers
/// <summary> /// <summary>
@@ -353,7 +385,7 @@ public sealed class StationSystem : EntitySystem
stationMember.Station = station; stationMember.Station = station;
stationData.Grids.Add(mapGrid); 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})"); _sawmill.Info($"Adding grid {mapGrid} to station {Name(station)} ({station})");
} }
@@ -376,7 +408,7 @@ public sealed class StationSystem : EntitySystem
RemComp<StationMemberComponent>(mapGrid); RemComp<StationMemberComponent>(mapGrid);
stationData.Grids.Remove(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})"); _sawmill.Info($"Removing grid {mapGrid} from station {Name(station)} ({station})");
} }
@@ -553,15 +585,21 @@ public sealed class StationGridAddedEvent : EntityEventArgs
/// </summary> /// </summary>
public EntityUid GridId; public EntityUid GridId;
/// <summary>
/// EntityUid of the station this grid was added to.
/// </summary>
public EntityUid Station;
/// <summary> /// <summary>
/// Indicates that the event was fired during station setup, /// Indicates that the event was fired during station setup,
/// so that it can be ignored if StationInitializedEvent was already handled. /// so that it can be ignored if StationInitializedEvent was already handled.
/// </summary> /// </summary>
public bool IsSetup; public bool IsSetup;
public StationGridAddedEvent(EntityUid gridId, bool isSetup) public StationGridAddedEvent(EntityUid gridId, EntityUid station, bool isSetup)
{ {
GridId = gridId; GridId = gridId;
Station = station;
IsSetup = isSetup; IsSetup = isSetup;
} }
} }
@@ -577,9 +615,15 @@ public sealed class StationGridRemovedEvent : EntityEventArgs
/// </summary> /// </summary>
public EntityUid GridId; 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; GridId = gridId;
Station = station;
} }
} }

View File

@@ -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;
}

View File

@@ -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;
}
}