/// <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();
/// <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);
}
/// 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!;
/// <inheritdoc/>
public override void Initialize()
{
+ base.Initialize();
+
_sawmill = _logManager.GetSawmill("station");
_gridQuery = GetEntityQuery<MapGridComponent>();
SubscribeLocalEvent<StationMemberComponent, ComponentShutdown>(OnStationGridDeleted);
SubscribeLocalEvent<StationMemberComponent, PostGridSplitEvent>(OnStationSplitEvent);
+ SubscribeLocalEvent<StationGridAddedEvent>(OnStationGridAdded);
+ SubscribeLocalEvent<StationGridRemovedEvent>(OnStationGridRemoved);
+
_player.PlayerStatusChanged += OnPlayerStatusChanged;
}
}
}
+ 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)
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());
}
}
+ 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>
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})");
}
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})");
}
/// </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;
}
}
/// </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;
}
}
--- /dev/null
+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;
+}
--- /dev/null
+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;
+ }
+}