From c2b87dfedab0b9c02df2444b0ba3daa88b5e7e24 Mon Sep 17 00:00:00 2001 From: Julian Giebel Date: Mon, 23 Jan 2023 02:07:57 +0100 Subject: [PATCH] Add a crew monitoring server (#7542) --- .../StationLimitedNetworkComponent.cs | 22 ++ Content.Server/DeviceNetwork/DeviceNet.cs | 2 +- .../Systems/DeviceNetworkSystem.cs | 40 +++- .../Systems/StationLimitedNetworkSystem.cs | 68 ++++++ .../CrewMonitoringConsoleSystem.cs | 57 ++--- .../CrewMonitoringServerComponent.cs | 34 +++ .../CrewMonitoringServerSystem.cs | 198 ++++++++++++++++++ .../SuitSensors/SuitSensorComponent.cs | 14 ++ .../Medical/SuitSensors/SuitSensorSystem.cs | 37 +++- .../Medical/SuitSensor/SharedSuitSensor.cs | 3 + .../Locale/en-US/devices/device-network.ftl | 1 + .../Device/devicenet_frequencies.yml | 9 +- .../Uniforms/base_clothinguniforms.yml | 3 +- .../Entities/Mobs/Player/admin_ghost.yml | 3 +- .../Circuitboards/Machine/production.yml | 17 +- .../Medical/handheld_crew_monitor.yml | 3 +- .../Machines/Computers/computers.yml | 4 +- .../Machines/crew_monitor_server.yml | 57 +++++ .../Structures/Machines/server.rsi/meta.json | 4 + .../Machines/server.rsi/variant-crew.png | Bin 0 -> 167 bytes 20 files changed, 518 insertions(+), 58 deletions(-) create mode 100644 Content.Server/DeviceNetwork/Components/StationLimitedNetworkComponent.cs create mode 100644 Content.Server/DeviceNetwork/Systems/StationLimitedNetworkSystem.cs create mode 100644 Content.Server/Medical/CrewMonitoring/CrewMonitoringServerComponent.cs create mode 100644 Content.Server/Medical/CrewMonitoring/CrewMonitoringServerSystem.cs create mode 100644 Resources/Prototypes/Entities/Structures/Machines/crew_monitor_server.yml create mode 100644 Resources/Textures/Structures/Machines/server.rsi/variant-crew.png diff --git a/Content.Server/DeviceNetwork/Components/StationLimitedNetworkComponent.cs b/Content.Server/DeviceNetwork/Components/StationLimitedNetworkComponent.cs new file mode 100644 index 0000000000..7d69c3c573 --- /dev/null +++ b/Content.Server/DeviceNetwork/Components/StationLimitedNetworkComponent.cs @@ -0,0 +1,22 @@ +using Robust.Shared.GameObjects; +using Robust.Shared.Map; + +namespace Content.Server.DeviceNetwork.Components +{ + [RegisterComponent] + public sealed class StationLimitedNetworkComponent : Component + { + /// + /// The station id the device is limited to. + /// + [ViewVariables(VVAccess.ReadWrite)] + public EntityUid? StationId; + + /// + /// Whether the entity is allowed to receive packets from entities that are not tied to any station + /// + [DataField("allowNonStationPackets")] + [ViewVariables(VVAccess.ReadWrite)] + public bool AllowNonStationPackets = false; + } +} diff --git a/Content.Server/DeviceNetwork/DeviceNet.cs b/Content.Server/DeviceNetwork/DeviceNet.cs index 7d25985efe..d4f67d2f79 100644 --- a/Content.Server/DeviceNetwork/DeviceNet.cs +++ b/Content.Server/DeviceNetwork/DeviceNet.cs @@ -82,7 +82,7 @@ public sealed class DeviceNet /// public bool Remove(DeviceNetworkComponent device) { - if (device.Address == null || Devices.Remove(device.Address)) + if (device.Address == null || !Devices.Remove(device.Address)) return false; if (device.ReceiveFrequency is not uint freq) diff --git a/Content.Server/DeviceNetwork/Systems/DeviceNetworkSystem.cs b/Content.Server/DeviceNetwork/Systems/DeviceNetworkSystem.cs index 4215485ff6..5a76c5ed66 100644 --- a/Content.Server/DeviceNetwork/Systems/DeviceNetworkSystem.cs +++ b/Content.Server/DeviceNetwork/Systems/DeviceNetworkSystem.cs @@ -48,18 +48,22 @@ namespace Content.Server.DeviceNetwork.Systems /// The address of the entity that the packet gets sent to. If null, the message is broadcast to all devices on that frequency (except the sender) /// The frequency to send on /// The data to be sent - public void QueuePacket(EntityUid uid, string? address, NetworkPayload data, uint? frequency = null, DeviceNetworkComponent? device = null) + /// Returns true when the packet was successfully enqueued. + public bool QueuePacket(EntityUid uid, string? address, NetworkPayload data, uint? frequency = null, DeviceNetworkComponent? device = null) { if (!Resolve(uid, ref device, false)) - return; + return false; if (device.Address == string.Empty) - return; + return false; frequency ??= device.TransmitFrequency; - if (frequency != null) - _packets.Enqueue(new DeviceNetworkPacketEvent(device.DeviceNetId, address, frequency.Value, device.Address, uid, data)); + if (frequency == null) + return false; + + _packets.Enqueue(new DeviceNetworkPacketEvent(device.DeviceNetId, address, frequency.Value, device.Address, uid, data)); + return true; } private void OnExamine(EntityUid uid, DeviceNetworkComponent device, ExaminedEvent args) @@ -137,6 +141,32 @@ namespace Content.Server.DeviceNetwork.Systems return GetNetwork(device.DeviceNetId).Remove(device); } + /// + /// Checks if a device is already connected to its network + /// + /// True if the device was found in the network with its corresponding network id + public bool IsDeviceConnected(EntityUid uid, DeviceNetworkComponent? device) + { + if (!Resolve(uid, ref device, false)) + return false; + + if (!_networks.TryGetValue(device.DeviceNetId, out var deviceNet)) + return false; + + return deviceNet.Devices.ContainsValue(device); + } + + /// + /// Checks if an address exists in the network with the given netId + /// + public bool IsAddressPresent(int netId, string? address) + { + if (address == null || !_networks.TryGetValue(netId, out var network)) + return false; + + return network.Devices.ContainsKey(address); + } + public void SetReceiveFrequency(EntityUid uid, uint? frequency, DeviceNetworkComponent? device = null) { if (!Resolve(uid, ref device, false)) diff --git a/Content.Server/DeviceNetwork/Systems/StationLimitedNetworkSystem.cs b/Content.Server/DeviceNetwork/Systems/StationLimitedNetworkSystem.cs new file mode 100644 index 0000000000..cdcc37e569 --- /dev/null +++ b/Content.Server/DeviceNetwork/Systems/StationLimitedNetworkSystem.cs @@ -0,0 +1,68 @@ +using Content.Server.DeviceNetwork.Components; +using Content.Server.Station.Systems; +using JetBrains.Annotations; +using Robust.Shared.Map; + +namespace Content.Server.DeviceNetwork.Systems +{ + /// + /// This system requires the StationLimitedNetworkComponent to be on the the sending entity as well as the receiving entity + /// + [UsedImplicitly] + public sealed class StationLimitedNetworkSystem : EntitySystem + { + [Dependency] private readonly StationSystem _stationSystem = default!; + public override void Initialize() + { + base.Initialize(); + SubscribeLocalEvent(OnMapInit); + SubscribeLocalEvent(OnBeforePacketSent); + } + + /// + /// Sets the station id the device is limited to. + /// + public void SetStation(EntityUid uid, EntityUid? stationId, StationLimitedNetworkComponent? component = null) + { + if (!Resolve(uid, ref component)) + return; + + component.StationId = stationId; + } + + /// + /// Set the station id to the one the entity is on when the station limited component is added + /// + private void OnMapInit(EntityUid uid, StationLimitedNetworkComponent networkComponent, MapInitEvent args) + { + networkComponent.StationId = _stationSystem.GetOwningStation(uid); + } + + /// + /// Checks if both devices are limited to the same station + /// + private void OnBeforePacketSent(EntityUid uid, StationLimitedNetworkComponent component, BeforePacketSentEvent args) + { + if (!CheckStationId(args.Sender, component.AllowNonStationPackets, component.StationId)) + { + args.Cancel(); + } + } + + /// + /// Compares the station IDs of the sending and receiving network components. + /// Returns false if either of them doesn't have a station ID or if their station ID isn't equal. + /// Returns true even when the sending entity isn't tied to a station if `allowNonStationPackets` is set to true. + /// + private bool CheckStationId(EntityUid senderUid, bool allowNonStationPackets, EntityUid? receiverStationId, StationLimitedNetworkComponent? sender = null) + { + if (!receiverStationId.HasValue) + return false; + + if (!Resolve(senderUid, ref sender, false)) + return allowNonStationPackets; + + return sender.StationId == receiverStationId; + } + } +} diff --git a/Content.Server/Medical/CrewMonitoring/CrewMonitoringConsoleSystem.cs b/Content.Server/Medical/CrewMonitoring/CrewMonitoringConsoleSystem.cs index ea77cef6bd..98c9c2d8cf 100644 --- a/Content.Server/Medical/CrewMonitoring/CrewMonitoringConsoleSystem.cs +++ b/Content.Server/Medical/CrewMonitoring/CrewMonitoringConsoleSystem.cs @@ -1,9 +1,11 @@ using System.Linq; +using Content.Server.DeviceNetwork; using Content.Server.DeviceNetwork.Systems; using Content.Server.Medical.SuitSensors; using Content.Server.UserInterface; using Content.Shared.Medical.CrewMonitoring; using Robust.Shared.Map; +using Content.Shared.Medical.SuitSensor; using Robust.Shared.Timing; namespace Content.Server.Medical.CrewMonitoring @@ -13,33 +15,14 @@ namespace Content.Server.Medical.CrewMonitoring [Dependency] private readonly SuitSensorSystem _sensors = default!; [Dependency] private readonly SharedTransformSystem _xform = default!; [Dependency] private readonly IGameTiming _gameTiming = default!; - - private const float UpdateRate = 3f; - private float _updateDif; + [Dependency] private readonly IMapManager _mapManager = default!; public override void Initialize() { base.Initialize(); SubscribeLocalEvent(OnRemove); SubscribeLocalEvent(OnPacketReceived); - } - - public override void Update(float frameTime) - { - base.Update(frameTime); - - // check update rate - _updateDif += frameTime; - if (_updateDif < UpdateRate) - return; - _updateDif = 0f; - - var consoles = EntityManager.EntityQuery(); - foreach (var console in consoles) - { - UpdateTimeouts(console.Owner, console); - UpdateUserInterface(console.Owner, console); - } + SubscribeLocalEvent(OnUIOpened); } private void OnRemove(EntityUid uid, CrewMonitoringConsoleComponent component, ComponentRemove args) @@ -49,12 +32,22 @@ namespace Content.Server.Medical.CrewMonitoring private void OnPacketReceived(EntityUid uid, CrewMonitoringConsoleComponent component, DeviceNetworkPacketEvent args) { - var suitSensor = _sensors.PacketToSuitSensor(args.Data); - if (suitSensor == null) + var payload = args.Data; + // check command + if (!payload.TryGetValue(DeviceNetworkConstants.Command, out string? command)) + return; + if (command != DeviceNetworkConstants.CmdUpdatedState) + return; + if (!payload.TryGetValue(SuitSensorConstants.NET_STATUS_COLLECTION, out Dictionary? sensorStatus)) return; - suitSensor.Timestamp = _gameTiming.CurTime; - component.ConnectedSensors[args.SenderAddress] = suitSensor; + component.ConnectedSensors = sensorStatus; + UpdateUserInterface(uid, component); + } + + private void OnUIOpened(EntityUid uid, CrewMonitoringConsoleComponent component, BoundUIOpenedEvent args) + { + UpdateUserInterface(uid, component); } private void UpdateUserInterface(EntityUid uid, CrewMonitoringConsoleComponent? component = null) @@ -72,19 +65,5 @@ namespace Content.Server.Medical.CrewMonitoring var uiState = new CrewMonitoringState(allSensors, xform.WorldPosition, component.Snap, component.Precision); ui.SetState(uiState); } - - private void UpdateTimeouts(EntityUid uid, CrewMonitoringConsoleComponent? component = null) - { - if (!Resolve(uid, ref component)) - return; - - foreach (var (address, sensor) in component.ConnectedSensors) - { - // if too many time passed - sensor just dropped connection - var dif = _gameTiming.CurTime - sensor.Timestamp; - if (dif.Seconds > component.SensorTimeout) - component.ConnectedSensors.Remove(address); - } - } } } diff --git a/Content.Server/Medical/CrewMonitoring/CrewMonitoringServerComponent.cs b/Content.Server/Medical/CrewMonitoring/CrewMonitoringServerComponent.cs new file mode 100644 index 0000000000..3a73f4d0b4 --- /dev/null +++ b/Content.Server/Medical/CrewMonitoring/CrewMonitoringServerComponent.cs @@ -0,0 +1,34 @@ +using Content.Shared.Medical.SuitSensor; +using Robust.Shared.Map; + +namespace Content.Server.Medical.CrewMonitoring; + +[RegisterComponent] +[Access(typeof(CrewMonitoringServerSystem))] +public sealed class CrewMonitoringServerComponent : Component +{ + + /// + /// List of all currently connected sensors to this server. + /// + public readonly Dictionary SensorStatus = new(); + + /// + /// After what time sensor consider to be lost. + /// + [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 new file mode 100644 index 0000000000..bb42542d78 --- /dev/null +++ b/Content.Server/Medical/CrewMonitoring/CrewMonitoringServerSystem.cs @@ -0,0 +1,198 @@ +using Content.Server.DeviceNetwork; +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.Medical.SuitSensor; +using Robust.Shared.Timing; + +namespace Content.Server.Medical.CrewMonitoring; + +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!; + + private const float UpdateRate = 3f; + private float _updateDiff; + + public override void Initialize() + { + base.Initialize(); + SubscribeLocalEvent(OnRemove); + SubscribeLocalEvent(OnPacketReceived); + SubscribeLocalEvent(OnPowerChanged); + } + + public override void Update(float frameTime) + { + base.Update(frameTime); + + // check update rate + _updateDiff += frameTime; + if (_updateDiff < UpdateRate) + return; + _updateDiff -= UpdateRate; + + var servers = EntityManager.EntityQuery(); + List activeServers = new(); + + foreach (var server in servers) + { + //Make sure the server is disconnected when it becomes unavailable + if (!server.Available) + { + if (server.Active) + DisconnectServer(server.Owner, server); + + continue; + } + + if (!server.Active) + continue; + + activeServers.Add(server.Owner); + } + + 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 = EntityManager.EntityQuery(); + (CrewMonitoringServerComponent, DeviceNetworkComponent)? last = default; + + foreach (var (server, device) in servers) + { + if (!_stationSystem.GetOwningStation(server.Owner)?.Equals(stationId) ?? false) + continue; + + if (!server.Available) + { + DisconnectServer(server.Owner,server, device); + continue; + } + + last = (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.Item1.Owner, last.Value.Item1, last.Value.Item2); + address = last.Value.Item2.Address; + return true; + } + + address = null; + return address != null; + } + + /// + /// Adds or updates a sensor status entry if the received package is a sensor status update + /// + private void OnPacketReceived(EntityUid uid, CrewMonitoringServerComponent component, DeviceNetworkPacketEvent args) + { + var sensorStatus = _sensors.PacketToSuitSensor(args.Data); + if (sensorStatus == null) + return; + + sensorStatus.Timestamp = _gameTiming.CurTime; + component.SensorStatus[args.SenderAddress] = sensorStatus; + } + + /// + /// Clears the servers sensor status list + /// + private void OnRemove(EntityUid uid, CrewMonitoringServerComponent component, ComponentRemove args) + { + 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 + /// + private void UpdateTimeout(EntityUid uid, CrewMonitoringServerComponent? component = null) + { + if (!Resolve(uid, ref component)) + return; + + foreach (var (address, sensor) in component.SensorStatus) + { + var dif = _gameTiming.CurTime - sensor.Timestamp; + if (dif.Seconds > component.SensorTimeout) + component.SensorStatus.Remove(address); + } + } + + /// + /// Broadcasts the status of all connected sensors + /// + private void BroadcastSensorStatus(EntityUid uid, CrewMonitoringServerComponent? serverComponent = null, DeviceNetworkComponent? device = null) + { + if (!Resolve(uid, ref serverComponent, ref device)) + return; + + var payload = new NetworkPayload() + { + [DeviceNetworkConstants.Command] = DeviceNetworkConstants.CmdUpdatedState, + [SuitSensorConstants.NET_STATUS_COLLECTION] = serverComponent.SensorStatus + }; + + _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 + /// + private void DisconnectServer(EntityUid uid, CrewMonitoringServerComponent? server = null, DeviceNetworkComponent? device = null) + { + if (!Resolve(uid, ref server, ref device)) + return; + + server.SensorStatus.Clear(); + server.Active = false; + + _deviceNetworkSystem.DisconnectDevice(uid, device, false); + } +} diff --git a/Content.Server/Medical/SuitSensors/SuitSensorComponent.cs b/Content.Server/Medical/SuitSensors/SuitSensorComponent.cs index 96a85064d9..891a5a8a67 100644 --- a/Content.Server/Medical/SuitSensors/SuitSensorComponent.cs +++ b/Content.Server/Medical/SuitSensors/SuitSensorComponent.cs @@ -56,5 +56,19 @@ namespace Content.Server.Medical.SuitSensors /// Last time when sensor updated owners status /// public TimeSpan LastUpdate = TimeSpan.Zero; + + /// + /// The station this suit sensor belongs to. If it's null the suit didn't spawn on a station and the sensor doesn't work. + /// + [DataField("station")] + public EntityUid? StationId = null; + + /// + /// The server the suit sensor sends it state to. + /// The suit sensor will try connecting to a new server when no server is connected. + /// It does this by calling the servers entity system for performance reasons. + /// + [DataField("server")] + public string? ConnectedServer = null; } } diff --git a/Content.Server/Medical/SuitSensors/SuitSensorSystem.cs b/Content.Server/Medical/SuitSensors/SuitSensorSystem.cs index e512489dac..eecb894255 100644 --- a/Content.Server/Medical/SuitSensors/SuitSensorSystem.cs +++ b/Content.Server/Medical/SuitSensors/SuitSensorSystem.cs @@ -2,7 +2,9 @@ using Content.Server.Access.Systems; using Content.Server.DeviceNetwork; using Content.Server.DeviceNetwork.Components; using Content.Server.DeviceNetwork.Systems; +using Content.Server.Medical.CrewMonitoring; using Content.Server.Popups; +using Content.Server.Station.Systems; using Content.Shared.Damage; using Content.Shared.Examine; using Content.Shared.Inventory.Events; @@ -25,6 +27,8 @@ namespace Content.Server.Medical.SuitSensors [Dependency] private readonly MobStateSystem _mobStateSystem = default!; [Dependency] private readonly DeviceNetworkSystem _deviceNetworkSystem = default!; [Dependency] private readonly IGameTiming _gameTiming = default!; + [Dependency] private readonly CrewMonitoringServerSystem _monitoringServerSystem = default!; + [Dependency] private readonly StationSystem _stationSystem = default!; [Dependency] private readonly SharedTransformSystem _xform = default!; private const float UpdateRate = 1f; @@ -57,27 +61,48 @@ namespace Content.Server.Medical.SuitSensors var sensors = EntityManager.EntityQuery(); foreach (var (sensor, device) in sensors) { - if (device.TransmitFrequency is not uint frequency) + if (!device.TransmitFrequency.HasValue || !sensor.StationId.HasValue) continue; // check if sensor is ready to update if (curTime - sensor.LastUpdate < sensor.UpdateRate) continue; - sensor.LastUpdate = curTime; + + // Add a random offset to the next update time that isn't longer than the sensors update rate + sensor.LastUpdate = curTime.Add(TimeSpan.FromSeconds(_random.Next(0, sensor.UpdateRate.Seconds))); // get sensor status var status = GetSensorState(sensor.Owner, sensor); if (status == null) continue; - // broadcast it to device network + //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)) + continue; + + sensor.ConnectedServer = address; + } + + // Send it to the connected server var payload = SuitSensorToPacket(status); - _deviceNetworkSystem.QueuePacket(sensor.Owner, null, payload, device: device); + + // Clear the connected server if its address isn't on the network + if (!_deviceNetworkSystem.IsAddressPresent(device.DeviceNetId, sensor.ConnectedServer)) + { + sensor.ConnectedServer = null; + continue; + } + + _deviceNetworkSystem.QueuePacket(sensor.Owner, sensor.ConnectedServer, payload, device: device); } } private void OnMapInit(EntityUid uid, SuitSensorComponent component, MapInitEvent args) { + component.StationId = _stationSystem.GetOwningStation(uid); + // generate random mode if (component.RandomMode) { @@ -277,7 +302,7 @@ namespace Content.Server.Medical.SuitSensors } /// - /// Serialize suit sensor status into device network package. + /// Serialize create a device network package from the suit sensors status. /// public NetworkPayload SuitSensorToPacket(SuitSensorStatus status) { @@ -299,7 +324,7 @@ namespace Content.Server.Medical.SuitSensors } /// - /// Try to deserialize device network message into suit sensor status + /// Try to create the suit sensors status from the device network message /// public SuitSensorStatus? PacketToSuitSensor(NetworkPayload payload) { diff --git a/Content.Shared/Medical/SuitSensor/SharedSuitSensor.cs b/Content.Shared/Medical/SuitSensor/SharedSuitSensor.cs index d7aefaedc4..cdf3ee070c 100644 --- a/Content.Shared/Medical/SuitSensor/SharedSuitSensor.cs +++ b/Content.Shared/Medical/SuitSensor/SharedSuitSensor.cs @@ -51,5 +51,8 @@ namespace Content.Shared.Medical.SuitSensor public const string NET_IS_ALIVE = "alive"; public const string NET_TOTAL_DAMAGE = "vitals"; public const string NET_CORDINATES = "cords"; + + ///Used by the CrewMonitoringServerSystem to send the status of all connected suit sensors to each crew monitor + public const string NET_STATUS_COLLECTION = "suit-status-collection"; } } diff --git a/Resources/Locale/en-US/devices/device-network.ftl b/Resources/Locale/en-US/devices/device-network.ftl index e39a465d9a..fee17d9c87 100644 --- a/Resources/Locale/en-US/devices/device-network.ftl +++ b/Resources/Locale/en-US/devices/device-network.ftl @@ -1,6 +1,7 @@ # named frequencies device-frequency-prototype-name-atmos = Atmospheric Devices device-frequency-prototype-name-suit-sensors = Suit Sensors +device-frequency-prototype-name-crew-monitor = Crew Monitor device-frequency-prototype-name-lights = Smart Lights device-frequency-prototype-name-mailing-units = Mailing Units device-frequency-prototype-name-pdas = PDAs diff --git a/Resources/Prototypes/Device/devicenet_frequencies.yml b/Resources/Prototypes/Device/devicenet_frequencies.yml index f19598ac5c..3762129fdb 100644 --- a/Resources/Prototypes/Device/devicenet_frequencies.yml +++ b/Resources/Prototypes/Device/devicenet_frequencies.yml @@ -53,12 +53,19 @@ name: device-frequency-prototype-name-atmos frequency: 1621 -# Only listen to this frequency if you are a health or GPS monitor. Otherwise you will just slow down the server by constantly receiving periodic broadcasts from every player-entity. +# Only listen to this frequency if you are a crew monitor server. Otherwise you will just slow down the server by constantly receiving periodic broadcasts from every player-entity. - type: deviceFrequency id: SuitSensor name: device-frequency-prototype-name-suit-sensors frequency: 1262 +# Crew monitors listen to this for a list of suit sensor statuses +- type: deviceFrequency + id: CrewMonitor + name: device-frequency-prototype-name-crew-monitor + frequency: 1261 + + # This frequency will likely have a LARGE number of listening entities. Please don't broadcast on this frequency. - type: deviceFrequency id: SmartLight #used by powered lights. diff --git a/Resources/Prototypes/Entities/Clothing/Uniforms/base_clothinguniforms.yml b/Resources/Prototypes/Entities/Clothing/Uniforms/base_clothinguniforms.yml index ac934f74c3..de52896e3b 100644 --- a/Resources/Prototypes/Entities/Clothing/Uniforms/base_clothinguniforms.yml +++ b/Resources/Prototypes/Entities/Clothing/Uniforms/base_clothinguniforms.yml @@ -35,7 +35,8 @@ deviceNetId: Wireless transmitFrequencyId: SuitSensor - type: WirelessNetworkConnection - range: 500 + range: 1200 + - type: StationLimitedNetwork - type: entity abstract: true diff --git a/Resources/Prototypes/Entities/Mobs/Player/admin_ghost.yml b/Resources/Prototypes/Entities/Mobs/Player/admin_ghost.yml index 6f3da2cce2..87d644fd6b 100644 --- a/Resources/Prototypes/Entities/Mobs/Player/admin_ghost.yml +++ b/Resources/Prototypes/Entities/Mobs/Player/admin_ghost.yml @@ -98,9 +98,10 @@ precision: 3 - type: DeviceNetwork deviceNetId: Wireless - receiveFrequencyId: SuitSensor + receiveFrequencyId: CrewMonitor - type: WirelessNetworkConnection range: 500 + - type: StationLimitedNetwork - type: Thieving stripTimeReduction: 9999 stealthy: true diff --git a/Resources/Prototypes/Entities/Objects/Devices/Circuitboards/Machine/production.yml b/Resources/Prototypes/Entities/Objects/Devices/Circuitboards/Machine/production.yml index 8e8a0800c1..42125ea3e4 100644 --- a/Resources/Prototypes/Entities/Objects/Devices/Circuitboards/Machine/production.yml +++ b/Resources/Prototypes/Entities/Objects/Devices/Circuitboards/Machine/production.yml @@ -176,7 +176,7 @@ ScanningModule: 1 Capacitor: 2 materialRequirements: - Steel: 5 + Steel: 5 Cable: 1 - type: entity @@ -302,6 +302,21 @@ Glass: 5 Cable: 1 +- type: entity + id: CrewMonitoringServerMachineCircuitboard + parent: BaseMachineCircuitboard + name: crew monitoring server machine board + description: A machine printed circuit board for a crew monitoring server + components: + - type: MachineBoard + prototype: CrewMonitoringServer + requirements: + Capacitor: 1 + ScanningModule: 2 + materialRequirements: + Steel: 1 + Cable: 2 + - type: entity id: CryoPodMachineCircuitboard parent: BaseMachineCircuitboard diff --git a/Resources/Prototypes/Entities/Objects/Specific/Medical/handheld_crew_monitor.yml b/Resources/Prototypes/Entities/Objects/Specific/Medical/handheld_crew_monitor.yml index c5091fea7b..6e692044f6 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/Medical/handheld_crew_monitor.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/Medical/handheld_crew_monitor.yml @@ -17,8 +17,9 @@ - type: CrewMonitoringConsole - type: DeviceNetwork deviceNetId: Wireless - receiveFrequencyId: SuitSensor + receiveFrequencyId: CrewMonitor - type: WirelessNetworkConnection range: 500 + - type: StationLimitedNetwork - type: StaticPrice price: 500 diff --git a/Resources/Prototypes/Entities/Structures/Machines/Computers/computers.yml b/Resources/Prototypes/Entities/Structures/Machines/Computers/computers.yml index db8d0437e5..a3dfac668e 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/Computers/computers.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/Computers/computers.yml @@ -311,9 +311,9 @@ - type: CrewMonitoringConsole - type: DeviceNetwork deviceNetId: Wireless - receiveFrequencyId: SuitSensor + receiveFrequencyId: CrewMonitor - type: WirelessNetworkConnection - range: 500 + range: 1200 - type: entity parent: BaseComputer diff --git a/Resources/Prototypes/Entities/Structures/Machines/crew_monitor_server.yml b/Resources/Prototypes/Entities/Structures/Machines/crew_monitor_server.yml new file mode 100644 index 0000000000..1e555edcf7 --- /dev/null +++ b/Resources/Prototypes/Entities/Structures/Machines/crew_monitor_server.yml @@ -0,0 +1,57 @@ +- type: entity + id: CrewMonitoringServer + parent: BaseMachinePowered + name: crew monitoring server + description: Receives and relays the status of all active suit sensors on the station. + components: + - type: Sprite + sprite: Structures/Machines/server.rsi + layers: + - state: server + - state: variant-crew + - type: Construction + graph: Machine + node: machine + containers: + - machine_board + - machine_parts + - type: Machine + board: CrewMonitoringServerMachineCircuitboard + - type: ContainerContainer + containers: + machine_board: !type:Container + machine_parts: !type:Container + - type: CrewMonitoringServer + - type: DeviceNetwork + deviceNetId: Wireless + transmitFrequencyId: CrewMonitor + receiveFrequencyId: SuitSensor + autoConnect: false + - type: WirelessNetworkConnection + range: 500 + - type: StationLimitedNetwork + - type: ApcPowerReceiver + powerLoad: 200 + priority: Low + - type: ExtensionCableReceiver + - type: Destructible + thresholds: + - trigger: + !type:DamageTrigger + damage: 300 + behaviors: + - !type:DoActsBehavior + acts: ["Destruction"] + - !type:PlaySoundBehavior + sound: + path: /Audio/Effects/metalbreak.ogg + - !type:SpawnEntitiesBehavior + spawn: + SheetSteel1: + min: 1 + max: 2 + - type: AmbientSound + volume: -9 + range: 5 + sound: + path: /Audio/Ambience/Objects/server_fans.ogg diff --git a/Resources/Textures/Structures/Machines/server.rsi/meta.json b/Resources/Textures/Structures/Machines/server.rsi/meta.json index 1dc63798ab..25ff730243 100644 --- a/Resources/Textures/Structures/Machines/server.rsi/meta.json +++ b/Resources/Textures/Structures/Machines/server.rsi/meta.json @@ -26,6 +26,10 @@ { "name": "server_o" + }, + { + "name": "variant-crew" + } ] } diff --git a/Resources/Textures/Structures/Machines/server.rsi/variant-crew.png b/Resources/Textures/Structures/Machines/server.rsi/variant-crew.png new file mode 100644 index 0000000000000000000000000000000000000000..e53a2ab166f9490ee67fb6f5ff06a93c2056a252 GIT binary patch literal 167 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}iJmTwArY;~ z2@zopr0Bt2S?EnA( literal 0 HcmV?d00001