diff --git a/Content.Server/Administration/Logs/Converters/GasMixtureStringRepresentationConverter.cs b/Content.Server/Administration/Logs/Converters/GasMixtureStringRepresentationConverter.cs new file mode 100644 index 0000000000..7fe05022be --- /dev/null +++ b/Content.Server/Administration/Logs/Converters/GasMixtureStringRepresentationConverter.cs @@ -0,0 +1,26 @@ +using System.Text.Json; +using Content.Server.Atmos; + +namespace Content.Server.Administration.Logs.Converters; + +[AdminLogConverter] +public sealed class GasMixtureStringRepresentationConverter : AdminLogConverter +{ + public override void Write(Utf8JsonWriter writer, GasMixtureStringRepresentation value, JsonSerializerOptions options) + { + writer.WriteStartObject(); + + writer.WriteNumber("mol", value.TotalMoles); + writer.WriteNumber("temperature", value.Temperature); + writer.WriteNumber("pressure", value.Pressure); + + writer.WriteStartObject("gases"); + foreach (var x in value.MolesPerGas) + { + writer.WriteNumber(x.Key, x.Value); + } + writer.WriteEndObject(); + + writer.WriteEndObject(); + } +} diff --git a/Content.Server/Atmos/GasMixture.cs b/Content.Server/Atmos/GasMixture.cs index bacd3a709c..cca2e0c4f8 100644 --- a/Content.Server/Atmos/GasMixture.cs +++ b/Content.Server/Atmos/GasMixture.cs @@ -207,6 +207,20 @@ namespace Content.Server.Atmos Array.Resize(ref Moles, Atmospherics.AdjustedNumberOfGases); } + public GasMixtureStringRepresentation ToPrettyString() + { + var molesPerGas = new Dictionary(); + for (int i = 0; i < Moles.Length; i++) + { + if (Moles[i] == 0) + continue; + + molesPerGas.Add(((Gas) i).ToString(), Moles[i]); + } + + return new GasMixtureStringRepresentation(TotalMoles, Temperature, Pressure, molesPerGas); + } + public override bool Equals(object? obj) { if (obj is GasMixture mix) diff --git a/Content.Server/Atmos/GasMixtureStringRepresentation.cs b/Content.Server/Atmos/GasMixtureStringRepresentation.cs new file mode 100644 index 0000000000..de87e6e295 --- /dev/null +++ b/Content.Server/Atmos/GasMixtureStringRepresentation.cs @@ -0,0 +1,18 @@ +using System.Linq; + +namespace Content.Server.Atmos; + +public readonly record struct GasMixtureStringRepresentation(float TotalMoles, float Temperature, float Pressure, Dictionary MolesPerGas) : IFormattable +{ + public override string ToString() + { + return $"{Temperature}K {Pressure} kPa"; + } + + public string ToString(string? format, IFormatProvider? formatProvider) + { + return ToString(); + } + + public static implicit operator string(GasMixtureStringRepresentation rep) => rep.ToString(); +} diff --git a/Content.Server/Medical/CryoPodSystem.cs b/Content.Server/Medical/CryoPodSystem.cs index 52fdad6018..74cfaf18df 100644 --- a/Content.Server/Medical/CryoPodSystem.cs +++ b/Content.Server/Medical/CryoPodSystem.cs @@ -1,3 +1,4 @@ +using Content.Server.Administration.Logs; using Content.Server.Atmos; using Content.Server.Atmos.EntitySystems; using Content.Server.Atmos.Piping.Components; @@ -17,6 +18,7 @@ using Content.Shared.Chemistry; using Content.Shared.Chemistry.Components; using Content.Shared.Chemistry.Reagent; using Content.Shared.Containers.ItemSlots; +using Content.Shared.Database; using Content.Shared.DoAfter; using Content.Shared.DragDrop; using Content.Shared.Emag.Systems; @@ -46,6 +48,7 @@ public sealed partial class CryoPodSystem: SharedCryoPodSystem [Dependency] private readonly IGameTiming _gameTiming = default!; [Dependency] private readonly MetaDataSystem _metaDataSystem = default!; [Dependency] private readonly ReactiveSystem _reactiveSystem = default!; + [Dependency] private readonly IAdminLogManager _adminLogger = default!; public override void Initialize() { @@ -110,14 +113,15 @@ public sealed partial class CryoPodSystem: SharedCryoPodSystem } } - public override void EjectBody(EntityUid uid, CryoPodComponent? cryoPodComponent) + public override EntityUid? EjectBody(EntityUid uid, CryoPodComponent? cryoPodComponent) { if (!Resolve(uid, ref cryoPodComponent)) - return; + return null; if (cryoPodComponent.BodyContainer.ContainedEntity is not {Valid: true} contained) - return; + return null; base.EjectBody(uid, cryoPodComponent); _climbSystem.ForciblySetClimbing(contained, uid); + return contained; } #region Interaction @@ -143,7 +147,15 @@ public sealed partial class CryoPodSystem: SharedCryoPodSystem if (args.Cancelled || args.Handled || args.Args.Target == null) return; - InsertBody(uid, args.Args.Target.Value, component); + if (InsertBody(uid, args.Args.Target.Value, component)) + { + if (!TryComp(uid, out CryoPodAirComponent? cryoPodAir)) + _adminLogger.Add(LogType.Action, LogImpact.Medium, + $"{ToPrettyString(args.User)} inserted {ToPrettyString(args.Args.Target.Value)} into {ToPrettyString(uid)}"); + + _adminLogger.Add(LogType.Action, LogImpact.Medium, + $"{ToPrettyString(args.User)} inserted {ToPrettyString(args.Args.Target.Value)} into {ToPrettyString(uid)} which contains gas: {cryoPodAir!.Air.ToPrettyString():gasMix}"); + } args.Handled = true; } diff --git a/Content.Shared/Medical/Cryogenics/SharedCryoPodSystem.cs b/Content.Shared/Medical/Cryogenics/SharedCryoPodSystem.cs index c1eeb34202..650e49e881 100644 --- a/Content.Shared/Medical/Cryogenics/SharedCryoPodSystem.cs +++ b/Content.Shared/Medical/Cryogenics/SharedCryoPodSystem.cs @@ -1,5 +1,7 @@ using Content.Server.Medical.Components; +using Content.Shared.Administration.Logs; using Content.Shared.Body.Components; +using Content.Shared.Database; using Content.Shared.DoAfter; using Content.Shared.DragDrop; using Content.Shared.Emag.Systems; @@ -21,6 +23,7 @@ public abstract partial class SharedCryoPodSystem: EntitySystem [Dependency] private readonly MobStateSystem _mobStateSystem = default!; [Dependency] private readonly SharedPopupSystem _popupSystem = default!; [Dependency] private readonly SharedContainerSystem _containerSystem = default!; + [Dependency] private readonly ISharedAdminLogManager _adminLogger = default!; public override void Initialize() { @@ -60,13 +63,13 @@ public abstract partial class SharedCryoPodSystem: EntitySystem _appearanceSystem.SetData(uid, CryoPodComponent.CryoPodVisuals.IsOn, cryoPodEnabled, appearance); } - public void InsertBody(EntityUid uid, EntityUid target, CryoPodComponent cryoPodComponent) + public bool InsertBody(EntityUid uid, EntityUid target, CryoPodComponent cryoPodComponent) { if (cryoPodComponent.BodyContainer.ContainedEntity != null) - return; + return false; if (!HasComp(target)) - return; + return false; var xform = Transform(target); cryoPodComponent.BodyContainer.Insert(target, transform: xform); @@ -75,6 +78,7 @@ public abstract partial class SharedCryoPodSystem: EntitySystem _standingStateSystem.Stand(target, force: true); // Force-stand the mob so that the cryo pod sprite overlays it fully UpdateAppearance(uid, cryoPodComponent); + return true; } public void TryEjectBody(EntityUid uid, EntityUid userId, CryoPodComponent? cryoPodComponent) @@ -90,16 +94,24 @@ public abstract partial class SharedCryoPodSystem: EntitySystem return; } - EjectBody(uid, cryoPodComponent); + var ejected = EjectBody(uid, cryoPodComponent); + if (ejected != null) + _adminLogger.Add(LogType.Action, LogImpact.Medium, $"{ToPrettyString(ejected.Value)} ejected from {ToPrettyString(uid)} by {ToPrettyString(userId)}"); } - public virtual void EjectBody(EntityUid uid, CryoPodComponent? cryoPodComponent) + /// + /// Ejects the contained body + /// + /// The cryopod entity + /// Cryopod component of + /// Ejected entity + public virtual EntityUid? EjectBody(EntityUid uid, CryoPodComponent? cryoPodComponent) { if (!Resolve(uid, ref cryoPodComponent)) - return; + return null; if (cryoPodComponent.BodyContainer.ContainedEntity is not {Valid: true} contained) - return; + return null; cryoPodComponent.BodyContainer.Remove(contained); // InsideCryoPodComponent is removed automatically in its EntGotRemovedFromContainerMessage listener @@ -116,6 +128,7 @@ public abstract partial class SharedCryoPodSystem: EntitySystem } UpdateAppearance(uid, cryoPodComponent); + return contained; } protected void AddAlternativeVerbs(EntityUid uid, CryoPodComponent cryoPodComponent, GetVerbsEvent args) @@ -153,7 +166,9 @@ public abstract partial class SharedCryoPodSystem: EntitySystem if (args.Cancelled) return; - EjectBody(uid, cryoPodComponent); + var ejected = EjectBody(uid, cryoPodComponent); + if (ejected != null) + _adminLogger.Add(LogType.Action, LogImpact.Medium, $"{ToPrettyString(ejected.Value)} pried out of {ToPrettyString(uid)} by {ToPrettyString(args.User)}"); } [Serializable, NetSerializable]