From 1ed63d43b00ebacafb5d6bc50d6c39348c7e8b39 Mon Sep 17 00:00:00 2001 From: Julian Giebel Date: Mon, 4 Mar 2024 01:45:37 +0100 Subject: [PATCH] System for single device net server per station functionality (#23946) --- .../SingletonDeviceNetServerComponent.cs | 20 +++ .../Systems/SingletonDeviceNetServerSystem.cs | 137 ++++++++++++++++++ .../CrewMonitoringServerComponent.cs | 13 -- .../CrewMonitoringServerSystem.cs | 103 +------------ .../Medical/SuitSensors/SuitSensorSystem.cs | 3 +- .../Machines/crew_monitor_server.yml | 2 + 6 files changed, 169 insertions(+), 109 deletions(-) create mode 100644 Content.Server/DeviceNetwork/Components/SingletonDeviceNetServerComponent.cs create mode 100644 Content.Server/DeviceNetwork/Systems/SingletonDeviceNetServerSystem.cs 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