Add a crew monitoring server (#7542)
This commit is contained in:
@@ -0,0 +1,22 @@
|
|||||||
|
using Robust.Shared.GameObjects;
|
||||||
|
using Robust.Shared.Map;
|
||||||
|
|
||||||
|
namespace Content.Server.DeviceNetwork.Components
|
||||||
|
{
|
||||||
|
[RegisterComponent]
|
||||||
|
public sealed class StationLimitedNetworkComponent : Component
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The station id the device is limited to.
|
||||||
|
/// </summary>
|
||||||
|
[ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public EntityUid? StationId;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether the entity is allowed to receive packets from entities that are not tied to any station
|
||||||
|
/// </summary>
|
||||||
|
[DataField("allowNonStationPackets")]
|
||||||
|
[ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public bool AllowNonStationPackets = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -82,7 +82,7 @@ public sealed class DeviceNet
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public bool Remove(DeviceNetworkComponent device)
|
public bool Remove(DeviceNetworkComponent device)
|
||||||
{
|
{
|
||||||
if (device.Address == null || Devices.Remove(device.Address))
|
if (device.Address == null || !Devices.Remove(device.Address))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (device.ReceiveFrequency is not uint freq)
|
if (device.ReceiveFrequency is not uint freq)
|
||||||
|
|||||||
@@ -48,18 +48,22 @@ namespace Content.Server.DeviceNetwork.Systems
|
|||||||
/// <param name="address">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)</param>
|
/// <param name="address">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)</param>
|
||||||
/// <param name="frequency">The frequency to send on</param>
|
/// <param name="frequency">The frequency to send on</param>
|
||||||
/// <param name="data">The data to be sent</param>
|
/// <param name="data">The data to be sent</param>
|
||||||
public void QueuePacket(EntityUid uid, string? address, NetworkPayload data, uint? frequency = null, DeviceNetworkComponent? device = null)
|
/// <returns>Returns true when the packet was successfully enqueued.</returns>
|
||||||
|
public bool QueuePacket(EntityUid uid, string? address, NetworkPayload data, uint? frequency = null, DeviceNetworkComponent? device = null)
|
||||||
{
|
{
|
||||||
if (!Resolve(uid, ref device, false))
|
if (!Resolve(uid, ref device, false))
|
||||||
return;
|
return false;
|
||||||
|
|
||||||
if (device.Address == string.Empty)
|
if (device.Address == string.Empty)
|
||||||
return;
|
return false;
|
||||||
|
|
||||||
frequency ??= device.TransmitFrequency;
|
frequency ??= device.TransmitFrequency;
|
||||||
|
|
||||||
if (frequency != null)
|
if (frequency == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
_packets.Enqueue(new DeviceNetworkPacketEvent(device.DeviceNetId, address, frequency.Value, device.Address, uid, data));
|
_packets.Enqueue(new DeviceNetworkPacketEvent(device.DeviceNetId, address, frequency.Value, device.Address, uid, data));
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnExamine(EntityUid uid, DeviceNetworkComponent device, ExaminedEvent args)
|
private void OnExamine(EntityUid uid, DeviceNetworkComponent device, ExaminedEvent args)
|
||||||
@@ -137,6 +141,32 @@ namespace Content.Server.DeviceNetwork.Systems
|
|||||||
return GetNetwork(device.DeviceNetId).Remove(device);
|
return GetNetwork(device.DeviceNetId).Remove(device);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if a device is already connected to its network
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>True if the device was found in the network with its corresponding network id</returns>
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if an address exists in the network with the given netId
|
||||||
|
/// </summary>
|
||||||
|
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)
|
public void SetReceiveFrequency(EntityUid uid, uint? frequency, DeviceNetworkComponent? device = null)
|
||||||
{
|
{
|
||||||
if (!Resolve(uid, ref device, false))
|
if (!Resolve(uid, ref device, false))
|
||||||
|
|||||||
@@ -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
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// This system requires the StationLimitedNetworkComponent to be on the the sending entity as well as the receiving entity
|
||||||
|
/// </summary>
|
||||||
|
[UsedImplicitly]
|
||||||
|
public sealed class StationLimitedNetworkSystem : EntitySystem
|
||||||
|
{
|
||||||
|
[Dependency] private readonly StationSystem _stationSystem = default!;
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
SubscribeLocalEvent<StationLimitedNetworkComponent, MapInitEvent>(OnMapInit);
|
||||||
|
SubscribeLocalEvent<StationLimitedNetworkComponent, BeforePacketSentEvent>(OnBeforePacketSent);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the station id the device is limited to.
|
||||||
|
/// </summary>
|
||||||
|
public void SetStation(EntityUid uid, EntityUid? stationId, StationLimitedNetworkComponent? component = null)
|
||||||
|
{
|
||||||
|
if (!Resolve(uid, ref component))
|
||||||
|
return;
|
||||||
|
|
||||||
|
component.StationId = stationId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Set the station id to the one the entity is on when the station limited component is added
|
||||||
|
/// </summary>
|
||||||
|
private void OnMapInit(EntityUid uid, StationLimitedNetworkComponent networkComponent, MapInitEvent args)
|
||||||
|
{
|
||||||
|
networkComponent.StationId = _stationSystem.GetOwningStation(uid);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if both devices are limited to the same station
|
||||||
|
/// </summary>
|
||||||
|
private void OnBeforePacketSent(EntityUid uid, StationLimitedNetworkComponent component, BeforePacketSentEvent args)
|
||||||
|
{
|
||||||
|
if (!CheckStationId(args.Sender, component.AllowNonStationPackets, component.StationId))
|
||||||
|
{
|
||||||
|
args.Cancel();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 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.
|
||||||
|
/// </summary>
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,9 +1,11 @@
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using Content.Server.DeviceNetwork;
|
||||||
using Content.Server.DeviceNetwork.Systems;
|
using Content.Server.DeviceNetwork.Systems;
|
||||||
using Content.Server.Medical.SuitSensors;
|
using Content.Server.Medical.SuitSensors;
|
||||||
using Content.Server.UserInterface;
|
using Content.Server.UserInterface;
|
||||||
using Content.Shared.Medical.CrewMonitoring;
|
using Content.Shared.Medical.CrewMonitoring;
|
||||||
using Robust.Shared.Map;
|
using Robust.Shared.Map;
|
||||||
|
using Content.Shared.Medical.SuitSensor;
|
||||||
using Robust.Shared.Timing;
|
using Robust.Shared.Timing;
|
||||||
|
|
||||||
namespace Content.Server.Medical.CrewMonitoring
|
namespace Content.Server.Medical.CrewMonitoring
|
||||||
@@ -13,33 +15,14 @@ namespace Content.Server.Medical.CrewMonitoring
|
|||||||
[Dependency] private readonly SuitSensorSystem _sensors = default!;
|
[Dependency] private readonly SuitSensorSystem _sensors = default!;
|
||||||
[Dependency] private readonly SharedTransformSystem _xform = default!;
|
[Dependency] private readonly SharedTransformSystem _xform = default!;
|
||||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||||
|
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||||
private const float UpdateRate = 3f;
|
|
||||||
private float _updateDif;
|
|
||||||
|
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
base.Initialize();
|
base.Initialize();
|
||||||
SubscribeLocalEvent<CrewMonitoringConsoleComponent, ComponentRemove>(OnRemove);
|
SubscribeLocalEvent<CrewMonitoringConsoleComponent, ComponentRemove>(OnRemove);
|
||||||
SubscribeLocalEvent<CrewMonitoringConsoleComponent, DeviceNetworkPacketEvent>(OnPacketReceived);
|
SubscribeLocalEvent<CrewMonitoringConsoleComponent, DeviceNetworkPacketEvent>(OnPacketReceived);
|
||||||
}
|
SubscribeLocalEvent<CrewMonitoringConsoleComponent, BoundUIOpenedEvent>(OnUIOpened);
|
||||||
|
|
||||||
public override void Update(float frameTime)
|
|
||||||
{
|
|
||||||
base.Update(frameTime);
|
|
||||||
|
|
||||||
// check update rate
|
|
||||||
_updateDif += frameTime;
|
|
||||||
if (_updateDif < UpdateRate)
|
|
||||||
return;
|
|
||||||
_updateDif = 0f;
|
|
||||||
|
|
||||||
var consoles = EntityManager.EntityQuery<CrewMonitoringConsoleComponent>();
|
|
||||||
foreach (var console in consoles)
|
|
||||||
{
|
|
||||||
UpdateTimeouts(console.Owner, console);
|
|
||||||
UpdateUserInterface(console.Owner, console);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnRemove(EntityUid uid, CrewMonitoringConsoleComponent component, ComponentRemove args)
|
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)
|
private void OnPacketReceived(EntityUid uid, CrewMonitoringConsoleComponent component, DeviceNetworkPacketEvent args)
|
||||||
{
|
{
|
||||||
var suitSensor = _sensors.PacketToSuitSensor(args.Data);
|
var payload = args.Data;
|
||||||
if (suitSensor == null)
|
// 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<string, SuitSensorStatus>? sensorStatus))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
suitSensor.Timestamp = _gameTiming.CurTime;
|
component.ConnectedSensors = sensorStatus;
|
||||||
component.ConnectedSensors[args.SenderAddress] = suitSensor;
|
UpdateUserInterface(uid, component);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnUIOpened(EntityUid uid, CrewMonitoringConsoleComponent component, BoundUIOpenedEvent args)
|
||||||
|
{
|
||||||
|
UpdateUserInterface(uid, component);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UpdateUserInterface(EntityUid uid, CrewMonitoringConsoleComponent? component = null)
|
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);
|
var uiState = new CrewMonitoringState(allSensors, xform.WorldPosition, component.Snap, component.Precision);
|
||||||
ui.SetState(uiState);
|
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
{
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// List of all currently connected sensors to this server.
|
||||||
|
/// </summary>
|
||||||
|
public readonly Dictionary<string, SuitSensorStatus> SensorStatus = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// After what time sensor consider to be lost.
|
||||||
|
/// </summary>
|
||||||
|
[DataField("sensorTimeout"), ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public float SensorTimeout = 10f;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether the server can become the currently active server. The server being unavailable usually means that it isn't powered
|
||||||
|
/// </summary>
|
||||||
|
[ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public bool Available = true;
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether the server is the currently active server for the station it's on
|
||||||
|
/// </summary>
|
||||||
|
[ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public bool Active = true;
|
||||||
|
}
|
||||||
@@ -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<CrewMonitoringServerComponent, ComponentRemove>(OnRemove);
|
||||||
|
SubscribeLocalEvent<CrewMonitoringServerComponent, DeviceNetworkPacketEvent>(OnPacketReceived);
|
||||||
|
SubscribeLocalEvent<CrewMonitoringServerComponent, PowerChangedEvent>(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<CrewMonitoringServerComponent>();
|
||||||
|
List<EntityUid> 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the address of the currently active server for the given station id if there is one
|
||||||
|
/// </summary>
|
||||||
|
public bool TryGetActiveServerAddress(EntityUid stationId, out string? address)
|
||||||
|
{
|
||||||
|
var servers = EntityManager.EntityQuery<CrewMonitoringServerComponent, DeviceNetworkComponent>();
|
||||||
|
(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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds or updates a sensor status entry if the received package is a sensor status update
|
||||||
|
/// </summary>
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Clears the servers sensor status list
|
||||||
|
/// </summary>
|
||||||
|
private void OnRemove(EntityUid uid, CrewMonitoringServerComponent component, ComponentRemove args)
|
||||||
|
{
|
||||||
|
component.SensorStatus.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Disconnects the server losing power
|
||||||
|
/// </summary>
|
||||||
|
private void OnPowerChanged(EntityUid uid, CrewMonitoringServerComponent component, ref PowerChangedEvent args)
|
||||||
|
{
|
||||||
|
component.Available = args.Powered;
|
||||||
|
|
||||||
|
if (!args.Powered)
|
||||||
|
DisconnectServer(uid, component);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Drop the sensor status if it hasn't been updated for to long
|
||||||
|
/// </summary>
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Broadcasts the status of all connected sensors
|
||||||
|
/// </summary>
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Disconnects a server from the device network and clears the currently active server
|
||||||
|
/// </summary>
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -56,5 +56,19 @@ namespace Content.Server.Medical.SuitSensors
|
|||||||
/// Last time when sensor updated owners status
|
/// Last time when sensor updated owners status
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public TimeSpan LastUpdate = TimeSpan.Zero;
|
public TimeSpan LastUpdate = TimeSpan.Zero;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 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.
|
||||||
|
/// </summary>
|
||||||
|
[DataField("station")]
|
||||||
|
public EntityUid? StationId = null;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 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.
|
||||||
|
/// </summary>
|
||||||
|
[DataField("server")]
|
||||||
|
public string? ConnectedServer = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,9 @@ using Content.Server.Access.Systems;
|
|||||||
using Content.Server.DeviceNetwork;
|
using Content.Server.DeviceNetwork;
|
||||||
using Content.Server.DeviceNetwork.Components;
|
using Content.Server.DeviceNetwork.Components;
|
||||||
using Content.Server.DeviceNetwork.Systems;
|
using Content.Server.DeviceNetwork.Systems;
|
||||||
|
using Content.Server.Medical.CrewMonitoring;
|
||||||
using Content.Server.Popups;
|
using Content.Server.Popups;
|
||||||
|
using Content.Server.Station.Systems;
|
||||||
using Content.Shared.Damage;
|
using Content.Shared.Damage;
|
||||||
using Content.Shared.Examine;
|
using Content.Shared.Examine;
|
||||||
using Content.Shared.Inventory.Events;
|
using Content.Shared.Inventory.Events;
|
||||||
@@ -25,6 +27,8 @@ namespace Content.Server.Medical.SuitSensors
|
|||||||
[Dependency] private readonly MobStateSystem _mobStateSystem = default!;
|
[Dependency] private readonly MobStateSystem _mobStateSystem = default!;
|
||||||
[Dependency] private readonly DeviceNetworkSystem _deviceNetworkSystem = default!;
|
[Dependency] private readonly DeviceNetworkSystem _deviceNetworkSystem = default!;
|
||||||
[Dependency] private readonly IGameTiming _gameTiming = 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!;
|
[Dependency] private readonly SharedTransformSystem _xform = default!;
|
||||||
|
|
||||||
private const float UpdateRate = 1f;
|
private const float UpdateRate = 1f;
|
||||||
@@ -57,27 +61,48 @@ namespace Content.Server.Medical.SuitSensors
|
|||||||
var sensors = EntityManager.EntityQuery<SuitSensorComponent, DeviceNetworkComponent>();
|
var sensors = EntityManager.EntityQuery<SuitSensorComponent, DeviceNetworkComponent>();
|
||||||
foreach (var (sensor, device) in sensors)
|
foreach (var (sensor, device) in sensors)
|
||||||
{
|
{
|
||||||
if (device.TransmitFrequency is not uint frequency)
|
if (!device.TransmitFrequency.HasValue || !sensor.StationId.HasValue)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// check if sensor is ready to update
|
// check if sensor is ready to update
|
||||||
if (curTime - sensor.LastUpdate < sensor.UpdateRate)
|
if (curTime - sensor.LastUpdate < sensor.UpdateRate)
|
||||||
continue;
|
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
|
// get sensor status
|
||||||
var status = GetSensorState(sensor.Owner, sensor);
|
var status = GetSensorState(sensor.Owner, sensor);
|
||||||
if (status == null)
|
if (status == null)
|
||||||
continue;
|
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);
|
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)
|
private void OnMapInit(EntityUid uid, SuitSensorComponent component, MapInitEvent args)
|
||||||
{
|
{
|
||||||
|
component.StationId = _stationSystem.GetOwningStation(uid);
|
||||||
|
|
||||||
// generate random mode
|
// generate random mode
|
||||||
if (component.RandomMode)
|
if (component.RandomMode)
|
||||||
{
|
{
|
||||||
@@ -277,7 +302,7 @@ namespace Content.Server.Medical.SuitSensors
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Serialize suit sensor status into device network package.
|
/// Serialize create a device network package from the suit sensors status.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public NetworkPayload SuitSensorToPacket(SuitSensorStatus status)
|
public NetworkPayload SuitSensorToPacket(SuitSensorStatus status)
|
||||||
{
|
{
|
||||||
@@ -299,7 +324,7 @@ namespace Content.Server.Medical.SuitSensors
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Try to deserialize device network message into suit sensor status
|
/// Try to create the suit sensors status from the device network message
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public SuitSensorStatus? PacketToSuitSensor(NetworkPayload payload)
|
public SuitSensorStatus? PacketToSuitSensor(NetworkPayload payload)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -51,5 +51,8 @@ namespace Content.Shared.Medical.SuitSensor
|
|||||||
public const string NET_IS_ALIVE = "alive";
|
public const string NET_IS_ALIVE = "alive";
|
||||||
public const string NET_TOTAL_DAMAGE = "vitals";
|
public const string NET_TOTAL_DAMAGE = "vitals";
|
||||||
public const string NET_CORDINATES = "cords";
|
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";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
# named frequencies
|
# named frequencies
|
||||||
device-frequency-prototype-name-atmos = Atmospheric Devices
|
device-frequency-prototype-name-atmos = Atmospheric Devices
|
||||||
device-frequency-prototype-name-suit-sensors = Suit Sensors
|
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-lights = Smart Lights
|
||||||
device-frequency-prototype-name-mailing-units = Mailing Units
|
device-frequency-prototype-name-mailing-units = Mailing Units
|
||||||
device-frequency-prototype-name-pdas = PDAs
|
device-frequency-prototype-name-pdas = PDAs
|
||||||
|
|||||||
@@ -53,12 +53,19 @@
|
|||||||
name: device-frequency-prototype-name-atmos
|
name: device-frequency-prototype-name-atmos
|
||||||
frequency: 1621
|
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
|
- type: deviceFrequency
|
||||||
id: SuitSensor
|
id: SuitSensor
|
||||||
name: device-frequency-prototype-name-suit-sensors
|
name: device-frequency-prototype-name-suit-sensors
|
||||||
frequency: 1262
|
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.
|
# This frequency will likely have a LARGE number of listening entities. Please don't broadcast on this frequency.
|
||||||
- type: deviceFrequency
|
- type: deviceFrequency
|
||||||
id: SmartLight #used by powered lights.
|
id: SmartLight #used by powered lights.
|
||||||
|
|||||||
@@ -35,7 +35,8 @@
|
|||||||
deviceNetId: Wireless
|
deviceNetId: Wireless
|
||||||
transmitFrequencyId: SuitSensor
|
transmitFrequencyId: SuitSensor
|
||||||
- type: WirelessNetworkConnection
|
- type: WirelessNetworkConnection
|
||||||
range: 500
|
range: 1200
|
||||||
|
- type: StationLimitedNetwork
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
abstract: true
|
abstract: true
|
||||||
|
|||||||
@@ -98,9 +98,10 @@
|
|||||||
precision: 3
|
precision: 3
|
||||||
- type: DeviceNetwork
|
- type: DeviceNetwork
|
||||||
deviceNetId: Wireless
|
deviceNetId: Wireless
|
||||||
receiveFrequencyId: SuitSensor
|
receiveFrequencyId: CrewMonitor
|
||||||
- type: WirelessNetworkConnection
|
- type: WirelessNetworkConnection
|
||||||
range: 500
|
range: 500
|
||||||
|
- type: StationLimitedNetwork
|
||||||
- type: Thieving
|
- type: Thieving
|
||||||
stripTimeReduction: 9999
|
stripTimeReduction: 9999
|
||||||
stealthy: true
|
stealthy: true
|
||||||
|
|||||||
@@ -302,6 +302,21 @@
|
|||||||
Glass: 5
|
Glass: 5
|
||||||
Cable: 1
|
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
|
- type: entity
|
||||||
id: CryoPodMachineCircuitboard
|
id: CryoPodMachineCircuitboard
|
||||||
parent: BaseMachineCircuitboard
|
parent: BaseMachineCircuitboard
|
||||||
|
|||||||
@@ -17,8 +17,9 @@
|
|||||||
- type: CrewMonitoringConsole
|
- type: CrewMonitoringConsole
|
||||||
- type: DeviceNetwork
|
- type: DeviceNetwork
|
||||||
deviceNetId: Wireless
|
deviceNetId: Wireless
|
||||||
receiveFrequencyId: SuitSensor
|
receiveFrequencyId: CrewMonitor
|
||||||
- type: WirelessNetworkConnection
|
- type: WirelessNetworkConnection
|
||||||
range: 500
|
range: 500
|
||||||
|
- type: StationLimitedNetwork
|
||||||
- type: StaticPrice
|
- type: StaticPrice
|
||||||
price: 500
|
price: 500
|
||||||
|
|||||||
@@ -311,9 +311,9 @@
|
|||||||
- type: CrewMonitoringConsole
|
- type: CrewMonitoringConsole
|
||||||
- type: DeviceNetwork
|
- type: DeviceNetwork
|
||||||
deviceNetId: Wireless
|
deviceNetId: Wireless
|
||||||
receiveFrequencyId: SuitSensor
|
receiveFrequencyId: CrewMonitor
|
||||||
- type: WirelessNetworkConnection
|
- type: WirelessNetworkConnection
|
||||||
range: 500
|
range: 1200
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
parent: BaseComputer
|
parent: BaseComputer
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -26,6 +26,10 @@
|
|||||||
{
|
{
|
||||||
"name": "server_o"
|
"name": "server_o"
|
||||||
|
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "variant-crew"
|
||||||
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 167 B |
Reference in New Issue
Block a user