Add air alarm admin logs (#33426)

This commit is contained in:
Palladinium
2025-02-06 16:10:21 +11:00
committed by GitHub
parent 434d1584fc
commit 001d61c6d5
6 changed files with 236 additions and 1 deletions

View File

@@ -9,10 +9,12 @@ using Content.Server.Power.Components;
using Content.Server.Power.EntitySystems;
using Content.Shared.Access.Components;
using Content.Shared.Access.Systems;
using Content.Shared.Administration.Logs;
using Content.Shared.Atmos;
using Content.Shared.Atmos.Monitor;
using Content.Shared.Atmos.Monitor.Components;
using Content.Shared.Atmos.Piping.Unary.Components;
using Content.Shared.Database;
using Content.Shared.DeviceLinking;
using Content.Shared.DeviceNetwork;
using Content.Shared.DeviceNetwork.Systems;
@@ -37,6 +39,7 @@ namespace Content.Server.Atmos.Monitor.Systems;
public sealed class AirAlarmSystem : EntitySystem
{
[Dependency] private readonly AccessReaderSystem _access = default!;
[Dependency] private readonly ISharedAdminLogManager _adminLogger = default!;
[Dependency] private readonly AtmosAlarmableSystem _atmosAlarmable = default!;
[Dependency] private readonly AtmosDeviceNetworkSystem _atmosDevNet = default!;
[Dependency] private readonly DeviceNetworkSystem _deviceNet = default!;
@@ -296,6 +299,7 @@ public sealed class AirAlarmSystem : EntitySystem
addr = netConn.Address;
}
_adminLogger.Add(LogType.AtmosDeviceSetting, LogImpact.Medium, $"{ToPrettyString(args.Actor)} changed {ToPrettyString(uid)} mode to {args.Mode}");
SetMode(uid, addr, args.Mode, false);
}
else
@@ -307,15 +311,26 @@ public sealed class AirAlarmSystem : EntitySystem
private void OnUpdateAutoMode(EntityUid uid, AirAlarmComponent component, AirAlarmUpdateAutoModeMessage args)
{
component.AutoMode = args.Enabled;
_adminLogger.Add(LogType.AtmosDeviceSetting, LogImpact.Medium, $"{ToPrettyString(args.Actor)} changed {ToPrettyString(uid)} auto mode to {args.Enabled}");
UpdateUI(uid, component);
}
private void OnUpdateThreshold(EntityUid uid, AirAlarmComponent component, AirAlarmUpdateAlarmThresholdMessage args)
{
if (AccessCheck(uid, args.Actor, component))
{
if (args.Gas != null)
_adminLogger.Add(LogType.AtmosDeviceSetting, LogImpact.Medium, $"{ToPrettyString(args.Actor)} changed {args.Address} {args.Gas} {args.Type} threshold using {ToPrettyString(uid)}");
else
_adminLogger.Add(LogType.AtmosDeviceSetting, LogImpact.Medium, $"{ToPrettyString(args.Actor)} changed {args.Address} {args.Type} threshold using {ToPrettyString(uid)}");
SetThreshold(uid, args.Address, args.Type, args.Threshold, args.Gas);
}
else
{
UpdateUI(uid, component);
}
}
private void OnUpdateDeviceData(EntityUid uid, AirAlarmComponent component, AirAlarmUpdateDeviceDataMessage args)
@@ -323,6 +338,8 @@ public sealed class AirAlarmSystem : EntitySystem
if (AccessCheck(uid, args.Actor, component)
&& _deviceList.ExistsInDeviceList(uid, args.Address))
{
_adminLogger.Add(LogType.AtmosDeviceSetting, LogImpact.Medium, $"{ToPrettyString(args.Actor)} changed {args.Address} settings using {ToPrettyString(uid)}");
SetDeviceData(uid, args.Address, args.Data);
}
else
@@ -344,6 +361,7 @@ public sealed class AirAlarmSystem : EntitySystem
case GasVentPumpData ventData:
foreach (string addr in component.VentData.Keys)
{
_adminLogger.Add(LogType.AtmosDeviceSetting, LogImpact.Medium, $"{ToPrettyString(args.Actor)} copied settings to vent {addr}");
SetData(uid, addr, args.Data);
}
break;
@@ -351,6 +369,7 @@ public sealed class AirAlarmSystem : EntitySystem
case GasVentScrubberData scrubberData:
foreach (string addr in component.ScrubberData.Keys)
{
_adminLogger.Add(LogType.AtmosDeviceSetting, LogImpact.Medium, $"{ToPrettyString(args.Actor)} copied settings to scrubber {addr}");
SetData(uid, addr, args.Data);
}
break;
@@ -379,6 +398,7 @@ public sealed class AirAlarmSystem : EntitySystem
if (!_access.IsAllowed(user.Value, uid, reader))
{
_popup.PopupEntity(Loc.GetString("air-alarm-ui-access-denied"), user.Value, user.Value);
_adminLogger.Add(LogType.AtmosDeviceSetting, LogImpact.Low, $"{ToPrettyString(user)} attempted to access {ToPrettyString(uid)} without access");
return false;
}

View File

@@ -9,9 +9,11 @@ using Content.Server.NodeContainer.EntitySystems;
using Content.Server.NodeContainer.Nodes;
using Content.Server.Power.Components;
using Content.Server.Power.EntitySystems;
using Content.Shared.Administration.Logs;
using Content.Shared.Atmos;
using Content.Shared.Atmos.Monitor;
using Content.Shared.Atmos.Piping.Components;
using Content.Shared.Database;
using Content.Shared.DeviceNetwork;
using Content.Shared.Power;
using Content.Shared.Tag;
@@ -25,6 +27,7 @@ namespace Content.Server.Atmos.Monitor.Systems;
// a danger), and atmos (which triggers based on set thresholds).
public sealed class AtmosMonitorSystem : EntitySystem
{
[Dependency] private readonly ISharedAdminLogManager _adminLogger = default!;
[Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!;
[Dependency] private readonly AtmosDeviceSystem _atmosDeviceSystem = default!;
[Dependency] private readonly DeviceNetworkSystem _deviceNetSystem = default!;
@@ -393,21 +396,74 @@ public sealed class AtmosMonitorSystem : EntitySystem
if (!Resolve(uid, ref monitor))
return;
// Used for logging after the switch statement
string logPrefix = "";
string logValueSuffix = "";
AtmosAlarmThreshold? logPreviousThreshold = null;
switch (type)
{
case AtmosMonitorThresholdType.Pressure:
logPrefix = "pressure";
logValueSuffix = "kPa";
logPreviousThreshold = monitor.PressureThreshold;
monitor.PressureThreshold = threshold;
break;
case AtmosMonitorThresholdType.Temperature:
logPrefix = "temperature";
logValueSuffix = "K";
logPreviousThreshold = monitor.TemperatureThreshold;
monitor.TemperatureThreshold = threshold;
break;
case AtmosMonitorThresholdType.Gas:
if (gas == null || monitor.GasThresholds == null)
return;
logPrefix = ((Gas) gas).ToString();
logValueSuffix = "kPa";
monitor.GasThresholds.TryGetValue((Gas) gas, out logPreviousThreshold);
monitor.GasThresholds[(Gas) gas] = threshold;
break;
}
// Admin log each change separately rather than logging the whole state
if (logPreviousThreshold != null)
{
if (threshold.Ignore != logPreviousThreshold.Ignore)
{
string enabled = threshold.Ignore ? "disabled" : "enabled";
_adminLogger.Add(
LogType.AtmosDeviceSetting,
LogImpact.Medium,
$"{ToPrettyString(uid)} {logPrefix} thresholds {enabled}"
);
}
foreach (var change in threshold.GetChanges(logPreviousThreshold))
{
if (change.Current.Enabled != change.Previous?.Enabled)
{
string enabled = change.Current.Enabled ? "enabled" : "disabled";
_adminLogger.Add(
LogType.AtmosDeviceSetting,
LogImpact.Medium,
$"{ToPrettyString(uid)} {logPrefix} {change.Type} {enabled}"
);
}
if (change.Current.Value != change.Previous?.Value)
{
_adminLogger.Add(
LogType.AtmosDeviceSetting,
LogImpact.Medium,
$"{ToPrettyString(uid)} {logPrefix} {change.Type} changed from {change.Previous?.Value} {logValueSuffix} to {change.Current.Value} {logValueSuffix}"
);
}
}
}
}
/// <summary>

View File

@@ -9,6 +9,7 @@ using Content.Server.DeviceNetwork.Components;
using Content.Server.DeviceNetwork.Systems;
using Content.Server.NodeContainer.EntitySystems;
using Content.Server.NodeContainer.Nodes;
using Content.Shared.Administration.Logs;
using Content.Shared.Atmos;
using Content.Shared.Atmos.Monitor;
using Content.Shared.Atmos.Piping.Components;
@@ -16,6 +17,7 @@ using Content.Shared.Atmos.Piping.Unary;
using Content.Shared.Atmos.Piping.Unary.Components;
using Content.Shared.Atmos.Visuals;
using Content.Shared.Audio;
using Content.Shared.Database;
using Content.Shared.DeviceNetwork;
using Content.Shared.DoAfter;
using Content.Shared.Examine;
@@ -30,6 +32,7 @@ namespace Content.Server.Atmos.Piping.Unary.EntitySystems
[UsedImplicitly]
public sealed class GasVentPumpSystem : EntitySystem
{
[Dependency] private readonly ISharedAdminLogManager _adminLogger = default!;
[Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!;
[Dependency] private readonly DeviceNetworkSystem _deviceNetSystem = default!;
[Dependency] private readonly DeviceLinkSystem _signalSystem = default!;
@@ -232,6 +235,44 @@ namespace Content.Server.Atmos.Piping.Unary.EntitySystems
if (!args.Data.TryGetValue(DeviceNetworkConstants.CmdSetState, out GasVentPumpData? setData))
break;
var previous = component.ToAirAlarmData();
if (previous.Enabled != setData.Enabled)
{
string enabled = setData.Enabled ? "enabled" : "disabled" ;
_adminLogger.Add(LogType.AtmosDeviceSetting, LogImpact.Medium, $"{ToPrettyString(uid)} {enabled}");
}
if (previous.PumpDirection != setData.PumpDirection)
_adminLogger.Add(LogType.AtmosDeviceSetting, LogImpact.Medium, $"{ToPrettyString(uid)} direction changed to {setData.PumpDirection}");
if (previous.PressureChecks != setData.PressureChecks)
_adminLogger.Add(LogType.AtmosDeviceSetting, LogImpact.Medium, $"{ToPrettyString(uid)} pressure check changed to {setData.PressureChecks}");
if (previous.ExternalPressureBound != setData.ExternalPressureBound)
{
_adminLogger.Add(
LogType.AtmosDeviceSetting,
LogImpact.Medium,
$"{ToPrettyString(uid)} external pressure bound changed from {previous.ExternalPressureBound} kPa to {setData.ExternalPressureBound} kPa"
);
}
if (previous.InternalPressureBound != setData.InternalPressureBound)
{
_adminLogger.Add(
LogType.AtmosDeviceSetting,
LogImpact.Medium,
$"{ToPrettyString(uid)} internal pressure bound changed from {previous.InternalPressureBound} kPa to {setData.InternalPressureBound} kPa"
);
}
if (previous.PressureLockoutOverride != setData.PressureLockoutOverride)
{
string enabled = setData.PressureLockoutOverride ? "enabled" : "disabled" ;
_adminLogger.Add(LogType.AtmosDeviceSetting, LogImpact.Medium, $"{ToPrettyString(uid)} pressure lockout override {enabled}");
}
component.FromAirAlarmData(setData);
UpdateState(uid, component);

View File

@@ -10,12 +10,14 @@ using Content.Server.NodeContainer;
using Content.Server.NodeContainer.EntitySystems;
using Content.Server.NodeContainer.Nodes;
using Content.Server.Power.Components;
using Content.Shared.Administration.Logs;
using Content.Shared.Atmos;
using Content.Shared.Atmos.Piping.Unary.Visuals;
using Content.Shared.Atmos.Monitor;
using Content.Shared.Atmos.Piping.Components;
using Content.Shared.Atmos.Piping.Unary.Components;
using Content.Shared.Audio;
using Content.Shared.Database;
using Content.Shared.DeviceNetwork;
using Content.Shared.Power;
using Content.Shared.Tools.Systems;
@@ -27,6 +29,7 @@ namespace Content.Server.Atmos.Piping.Unary.EntitySystems
[UsedImplicitly]
public sealed class GasVentScrubberSystem : EntitySystem
{
[Dependency] private readonly ISharedAdminLogManager _adminLogger = default!;
[Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!;
[Dependency] private readonly DeviceNetworkSystem _deviceNetSystem = default!;
[Dependency] private readonly NodeContainerSystem _nodeContainer = default!;
@@ -163,6 +166,43 @@ namespace Content.Server.Atmos.Piping.Unary.EntitySystems
if (!args.Data.TryGetValue(DeviceNetworkConstants.CmdSetState, out GasVentScrubberData? setData))
break;
var previous = component.ToAirAlarmData();
if (previous.Enabled != setData.Enabled)
{
string enabled = setData.Enabled ? "enabled" : "disabled" ;
_adminLogger.Add(LogType.AtmosDeviceSetting, LogImpact.Medium, $"{ToPrettyString(uid)} {enabled}");
}
// TODO: IgnoreAlarms?
if (previous.PumpDirection != setData.PumpDirection)
_adminLogger.Add(LogType.AtmosDeviceSetting, LogImpact.Medium, $"{ToPrettyString(uid)} direction changed to {setData.PumpDirection}");
// TODO: This is iterating through both sets, it could probably be faster but they're both really small sets anyways
foreach (Gas gas in previous.FilterGases)
if (!setData.FilterGases.Contains(gas))
_adminLogger.Add(LogType.AtmosDeviceSetting, LogImpact.Medium, $"{ToPrettyString(uid)} {gas} filtering disabled");
foreach (Gas gas in setData.FilterGases)
if (!previous.FilterGases.Contains(gas))
_adminLogger.Add(LogType.AtmosDeviceSetting, LogImpact.Medium, $"{ToPrettyString(uid)} {gas} filtering enabled");
if (previous.VolumeRate != setData.VolumeRate)
{
_adminLogger.Add(
LogType.AtmosDeviceSetting,
LogImpact.Medium,
$"{ToPrettyString(uid)} volume rate changed from {previous.VolumeRate} L to {setData.VolumeRate} L"
);
}
if (previous.WideNet != setData.WideNet)
{
string enabled = setData.WideNet ? "enabled" : "disabled" ;
_adminLogger.Add(LogType.AtmosDeviceSetting, LogImpact.Medium, $"{ToPrettyString(uid)} WideNet {enabled}");
}
component.FromAirAlarmData(setData);
UpdateState(uid, component);

View File

@@ -444,4 +444,9 @@ public enum LogType
/// A player interacted with a PDA or its cartridge component
/// </summary>
PdaInteract = 96,
/// <summary>
/// An atmos networked device (such as a vent or pump) has had its settings changed, usually through an air alarm
/// </summary>
AtmosDeviceSetting = 97,
}

View File

@@ -253,10 +253,57 @@ public sealed partial class AtmosAlarmThreshold
break;
}
}
/// <summary>
/// Iterates through the changes that these threshold settings would make from a
/// previous instance. Basically, diffs the two settings.
/// </summary>
public IEnumerable<AtmosAlarmThresholdChange> GetChanges(AtmosAlarmThreshold previous)
{
if (LowerBound != previous.LowerBound)
yield return new AtmosAlarmThresholdChange(AtmosMonitorLimitType.LowerDanger, previous.LowerBound, LowerBound);
if (LowerWarningBound != previous.LowerWarningBound)
yield return new AtmosAlarmThresholdChange(AtmosMonitorLimitType.LowerWarning, previous.LowerWarningBound, LowerWarningBound);
if (UpperBound != previous.UpperBound)
yield return new AtmosAlarmThresholdChange(AtmosMonitorLimitType.UpperDanger, previous.UpperBound, UpperBound);
if (UpperWarningBound != previous.UpperWarningBound)
yield return new AtmosAlarmThresholdChange(AtmosMonitorLimitType.UpperWarning, previous.UpperWarningBound, UpperWarningBound);
}
}
/// <summary>
/// A change of a single value between two AtmosAlarmThreshold, for a given AtmosMonitorLimitType
/// </summary>
public readonly struct AtmosAlarmThresholdChange
{
/// <summary>
/// The type of change between the two threshold sets
/// </summary>
public readonly AtmosMonitorLimitType Type;
/// <summary>
/// The value in the old threshold set
/// </summary>
public readonly AlarmThresholdSetting? Previous;
/// <summary>
/// The value in the new threshold set
/// </summary>
public readonly AlarmThresholdSetting Current;
public AtmosAlarmThresholdChange(AtmosMonitorLimitType type, AlarmThresholdSetting? previous, AlarmThresholdSetting current)
{
Type = type;
Previous = previous;
Current = current;
}
}
[DataDefinition, Serializable]
public readonly partial struct AlarmThresholdSetting
public readonly partial struct AlarmThresholdSetting: IEquatable<AlarmThresholdSetting>
{
[DataField("enabled")]
public bool Enabled { get; init; } = true;
@@ -289,6 +336,32 @@ public readonly partial struct AlarmThresholdSetting
{
return this with {Enabled = enabled};
}
public bool Equals(AlarmThresholdSetting other)
{
if (Enabled != other.Enabled)
return false;
if (Value != other.Value)
return false;
return true;
}
public static bool operator ==(AlarmThresholdSetting lhs, AlarmThresholdSetting rhs)
{
return lhs.Equals(rhs);
}
public static bool operator !=(AlarmThresholdSetting lhs, AlarmThresholdSetting rhs)
{
return !lhs.Equals(rhs);
}
public override int GetHashCode()
{
return HashCode.Combine(Enabled, Value);
}
}
public enum AtmosMonitorThresholdBound