diff --git a/Content.Server/DeviceNetwork/Components/SingletonDeviceNetServerComponent.cs b/Content.Server/DeviceNetwork/Components/SingletonDeviceNetServerComponent.cs
new file mode 100644
index 0000000000..48272ff041
--- /dev/null
+++ b/Content.Server/DeviceNetwork/Components/SingletonDeviceNetServerComponent.cs
@@ -0,0 +1,20 @@
+using Content.Server.DeviceNetwork.Systems;
+
+namespace Content.Server.DeviceNetwork.Components;
+
+[RegisterComponent]
+[Access(typeof(SingletonDeviceNetServerSystem))]
+public sealed partial class SingletonDeviceNetServerComponent : Component
+{
+ ///
+ /// Whether the server can become the currently active server. The server being unavailable usually means that it isn't powered
+ ///
+ [DataField, ViewVariables(VVAccess.ReadWrite)]
+ public bool Available = true;
+
+ ///
+ /// Whether the server is the currently active server for the station it's on
+ ///
+ [DataField, ViewVariables(VVAccess.ReadWrite)]
+ public bool Active = true;
+}
diff --git a/Content.Server/DeviceNetwork/Systems/SingletonDeviceNetServerSystem.cs b/Content.Server/DeviceNetwork/Systems/SingletonDeviceNetServerSystem.cs
new file mode 100644
index 0000000000..fd62514d00
--- /dev/null
+++ b/Content.Server/DeviceNetwork/Systems/SingletonDeviceNetServerSystem.cs
@@ -0,0 +1,137 @@
+using Content.Server.DeviceNetwork.Components;
+using Content.Server.Medical.CrewMonitoring;
+using Content.Server.Power.Components;
+using Content.Server.Station.Systems;
+
+namespace Content.Server.DeviceNetwork.Systems;
+
+///
+/// Keeps one active server entity per station. Activates another available one if the currently active server becomes unavailable
+/// Server in this context means an entity that manages the devicenet packets like the
+///
+public sealed class SingletonDeviceNetServerSystem : EntitySystem
+{
+ [Dependency] private readonly DeviceNetworkSystem _deviceNetworkSystem = default!;
+ [Dependency] private readonly StationSystem _stationSystem = default!;
+
+ public override void Initialize()
+ {
+ base.Initialize();
+ SubscribeLocalEvent(OnPowerChanged);
+ }
+
+ ///
+ /// Returns whether the given entity is an active server or not
+ ///
+ public bool IsActiveServer(EntityUid serverId, SingletonDeviceNetServerComponent? serverComponent = default)
+ {
+ return Resolve(serverId, ref serverComponent) && serverComponent.Active;
+ }
+
+ ///
+ /// Returns the address of the currently active server for the given station id if there is one.
+ /// What kind of server you're trying to get the active instance of is determined by the component type parameter TComp.
+ ///
+ /// Setting TComp to , for example, gives you the address of an entity containing the crew monitoring server component.
+ ///
+ /// The entityUid of the station
+ /// The address of the active server if it exists
+ /// The component type that determines what type of server you're getting the address of
+ /// True if there is an active serve. False otherwise
+ public bool TryGetActiveServerAddress(EntityUid stationId, out string? address) where TComp : IComponent
+ {
+ var servers = EntityQueryEnumerator<
+ SingletonDeviceNetServerComponent,
+ DeviceNetworkComponent,
+ TComp
+ >();
+
+ (EntityUid id, SingletonDeviceNetServerComponent server, DeviceNetworkComponent device)? last = default;
+
+ while (servers.MoveNext(out var uid, out var server, out var device, out _))
+ {
+ if (!_stationSystem.GetOwningStation(uid)?.Equals(stationId) ?? true)
+ continue;
+
+ if (!server.Available)
+ {
+ DisconnectServer(uid,server, device);
+ continue;
+ }
+
+ last = (uid, server, device);
+
+ if (!server.Active)
+ continue;
+
+ address = device.Address;
+ return true;
+ }
+
+ //If there was no active server for the station make the last available inactive one active
+ if (last.HasValue)
+ {
+ ConnectServer(last.Value.id, last.Value.server, last.Value.device);
+ address = last.Value.device.Address;
+ return true;
+ }
+
+ address = null;
+ return address != null;
+ }
+
+ ///
+ /// Disconnects the server losing power
+ ///
+ private void OnPowerChanged(EntityUid uid, SingletonDeviceNetServerComponent component, ref PowerChangedEvent args)
+ {
+ component.Available = args.Powered;
+
+ if (!args.Powered && component.Active)
+ DisconnectServer(uid, component);
+ }
+
+ private void ConnectServer(EntityUid uid, SingletonDeviceNetServerComponent? server = null, DeviceNetworkComponent? device = null)
+ {
+ if (!Resolve(uid, ref server, ref device))
+ return;
+
+ server.Active = true;
+
+ var connectedEvent = new DeviceNetServerConnectedEvent();
+ RaiseLocalEvent(uid, ref connectedEvent);
+
+ if (_deviceNetworkSystem.IsDeviceConnected(uid, device))
+ return;
+
+ _deviceNetworkSystem.ConnectDevice(uid, device);
+ }
+
+ ///
+ /// Disconnects a server from the device network and clears the currently active server
+ ///
+ private void DisconnectServer(EntityUid uid, SingletonDeviceNetServerComponent? server = null, DeviceNetworkComponent? device = null)
+ {
+ if (!Resolve(uid, ref server, ref device))
+ return;
+
+ server.Active = false;
+
+ var disconnectedEvent = new DeviceNetServerDisconnectedEvent();
+ RaiseLocalEvent(uid, ref disconnectedEvent);
+
+ _deviceNetworkSystem.DisconnectDevice(uid, device, false);
+ }
+}
+
+///
+/// Raised when a server gets activated and connected to the device net
+///
+[ByRefEvent]
+public record struct DeviceNetServerConnectedEvent;
+
+///
+/// Raised when a server gets disconnected
+///
+[ByRefEvent]
+public record struct DeviceNetServerDisconnectedEvent;
diff --git a/Content.Server/Medical/CrewMonitoring/CrewMonitoringServerComponent.cs b/Content.Server/Medical/CrewMonitoring/CrewMonitoringServerComponent.cs
index 0d95cc48c7..7eeafb49c6 100644
--- a/Content.Server/Medical/CrewMonitoring/CrewMonitoringServerComponent.cs
+++ b/Content.Server/Medical/CrewMonitoring/CrewMonitoringServerComponent.cs
@@ -18,17 +18,4 @@ public sealed partial class CrewMonitoringServerComponent : Component
///
[DataField("sensorTimeout"), ViewVariables(VVAccess.ReadWrite)]
public float SensorTimeout = 10f;
-
- ///
- /// Whether the server can become the currently active server. The server being unavailable usually means that it isn't powered
- ///
- [ViewVariables(VVAccess.ReadWrite)]
- public bool Available = true;
-
-
- ///
- /// Whether the server is the currently active server for the station it's on
- ///
- [ViewVariables(VVAccess.ReadWrite)]
- public bool Active = true;
}
diff --git a/Content.Server/Medical/CrewMonitoring/CrewMonitoringServerSystem.cs b/Content.Server/Medical/CrewMonitoring/CrewMonitoringServerSystem.cs
index bff7f1565a..864ce2c55e 100644
--- a/Content.Server/Medical/CrewMonitoring/CrewMonitoringServerSystem.cs
+++ b/Content.Server/Medical/CrewMonitoring/CrewMonitoringServerSystem.cs
@@ -2,8 +2,6 @@
using Content.Server.DeviceNetwork.Components;
using Content.Server.DeviceNetwork.Systems;
using Content.Server.Medical.SuitSensors;
-using Content.Server.Power.Components;
-using Content.Server.Station.Systems;
using Content.Shared.DeviceNetwork;
using Content.Shared.Medical.SuitSensor;
using Robust.Shared.Timing;
@@ -15,7 +13,7 @@ public sealed class CrewMonitoringServerSystem : EntitySystem
[Dependency] private readonly SuitSensorSystem _sensors = default!;
[Dependency] private readonly IGameTiming _gameTiming = default!;
[Dependency] private readonly DeviceNetworkSystem _deviceNetworkSystem = default!;
- [Dependency] private readonly StationSystem _stationSystem = default!;
+ [Dependency] private readonly SingletonDeviceNetServerSystem _singletonServerSystem = default!;
private const float UpdateRate = 3f;
private float _updateDiff;
@@ -25,7 +23,7 @@ public sealed class CrewMonitoringServerSystem : EntitySystem
base.Initialize();
SubscribeLocalEvent(OnRemove);
SubscribeLocalEvent(OnPacketReceived);
- SubscribeLocalEvent(OnPowerChanged);
+ SubscribeLocalEvent(OnDisconnected);
}
public override void Update(float frameTime)
@@ -39,70 +37,15 @@ public sealed class CrewMonitoringServerSystem : EntitySystem
_updateDiff -= UpdateRate;
var servers = EntityQueryEnumerator();
- List activeServers = new();
while (servers.MoveNext(out var id, out var server))
{
- //Make sure the server is disconnected when it becomes unavailable
- if (!server.Available)
- {
- if (server.Active)
- DisconnectServer(id, server);
-
- continue;
- }
-
- if (!server.Active)
+ if (!_singletonServerSystem.IsActiveServer(id))
continue;
- activeServers.Add(id);
+ UpdateTimeout(id);
+ BroadcastSensorStatus(id, server);
}
-
- foreach (var activeServer in activeServers)
- {
- UpdateTimeout(activeServer);
- BroadcastSensorStatus(activeServer);
- }
- }
-
- ///
- /// Returns the address of the currently active server for the given station id if there is one
- ///
- public bool TryGetActiveServerAddress(EntityUid stationId, out string? address)
- {
- var servers = EntityQueryEnumerator();
- (EntityUid id, CrewMonitoringServerComponent server, DeviceNetworkComponent device)? last = default;
-
- while (servers.MoveNext(out var uid, out var server, out var device))
- {
- if (!_stationSystem.GetOwningStation(uid)?.Equals(stationId) ?? true)
- continue;
-
- if (!server.Available)
- {
- DisconnectServer(uid,server, device);
- continue;
- }
-
- last = (uid, server, device);
-
- if (server.Active)
- {
- address = device.Address;
- return true;
- }
- }
-
- //If there was no active server for the station make the last available inactive one active
- if (last.HasValue)
- {
- ConnectServer(last.Value.id, last.Value.server, last.Value.device);
- address = last.Value.device.Address;
- return true;
- }
-
- address = null;
- return address != null;
}
///
@@ -126,17 +69,6 @@ public sealed class CrewMonitoringServerSystem : EntitySystem
component.SensorStatus.Clear();
}
- ///
- /// Disconnects the server losing power
- ///
- private void OnPowerChanged(EntityUid uid, CrewMonitoringServerComponent component, ref PowerChangedEvent args)
- {
- component.Available = args.Powered;
-
- if (!args.Powered)
- DisconnectServer(uid, component);
- }
-
///
/// Drop the sensor status if it hasn't been updated for to long
///
@@ -170,30 +102,11 @@ public sealed class CrewMonitoringServerSystem : EntitySystem
_deviceNetworkSystem.QueuePacket(uid, null, payload, device: device);
}
- private void ConnectServer(EntityUid uid, CrewMonitoringServerComponent? server = null, DeviceNetworkComponent? device = null)
- {
- if (!Resolve(uid, ref server, ref device))
- return;
-
- server.Active = true;
-
- if (_deviceNetworkSystem.IsDeviceConnected(uid, device))
- return;
-
- _deviceNetworkSystem.ConnectDevice(uid, device);
- }
-
///
- /// Disconnects a server from the device network and clears the currently active server
+ /// Clears sensor data on disconnect
///
- private void DisconnectServer(EntityUid uid, CrewMonitoringServerComponent? server = null, DeviceNetworkComponent? device = null)
+ private void OnDisconnected(EntityUid uid, CrewMonitoringServerComponent component, ref DeviceNetServerDisconnectedEvent _)
{
- if (!Resolve(uid, ref server, ref device))
- return;
-
- server.SensorStatus.Clear();
- server.Active = false;
-
- _deviceNetworkSystem.DisconnectDevice(uid, device, false);
+ component.SensorStatus.Clear();
}
}
diff --git a/Content.Server/Medical/SuitSensors/SuitSensorSystem.cs b/Content.Server/Medical/SuitSensors/SuitSensorSystem.cs
index 94ecf5fd0e..b807b63e21 100644
--- a/Content.Server/Medical/SuitSensors/SuitSensorSystem.cs
+++ b/Content.Server/Medical/SuitSensors/SuitSensorSystem.cs
@@ -34,6 +34,7 @@ public sealed class SuitSensorSystem : EntitySystem
[Dependency] private readonly PopupSystem _popupSystem = default!;
[Dependency] private readonly SharedTransformSystem _transform = default!;
[Dependency] private readonly StationSystem _stationSystem = default!;
+ [Dependency] private readonly SingletonDeviceNetServerSystem _singletonServerSystem = default!;
public override void Initialize()
{
@@ -80,7 +81,7 @@ public sealed class SuitSensorSystem : EntitySystem
//Retrieve active server address if the sensor isn't connected to a server
if (sensor.ConnectedServer == null)
{
- if (!_monitoringServerSystem.TryGetActiveServerAddress(sensor.StationId!.Value, out var address))
+ if (!_singletonServerSystem.TryGetActiveServerAddress(sensor.StationId!.Value, out var address))
continue;
sensor.ConnectedServer = address;
diff --git a/Resources/Prototypes/Entities/Structures/Machines/crew_monitor_server.yml b/Resources/Prototypes/Entities/Structures/Machines/crew_monitor_server.yml
index 30e42d59ab..b069d28a33 100644
--- a/Resources/Prototypes/Entities/Structures/Machines/crew_monitor_server.yml
+++ b/Resources/Prototypes/Entities/Structures/Machines/crew_monitor_server.yml
@@ -22,6 +22,8 @@
machine_board: !type:Container
machine_parts: !type:Container
- type: CrewMonitoringServer
+ - type: SingletonDeviceNetServer
+ ServerType: CrewMonitoringServer
- type: DeviceNetwork
deviceNetId: Wireless
transmitFrequencyId: CrewMonitor