Adds even more important Admin Logging (#10268)
This commit is contained in:
@@ -1,8 +1,11 @@
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Content.Server.Access.Systems;
|
using Content.Server.Access.Systems;
|
||||||
|
using Content.Server.Administration.Logs;
|
||||||
using Content.Server.UserInterface;
|
using Content.Server.UserInterface;
|
||||||
using Content.Shared.Access.Components;
|
using Content.Shared.Access.Components;
|
||||||
using Content.Shared.Access.Systems;
|
using Content.Shared.Access.Systems;
|
||||||
|
using Content.Shared.Containers.ItemSlots;
|
||||||
|
using Content.Shared.Database;
|
||||||
using Robust.Server.GameObjects;
|
using Robust.Server.GameObjects;
|
||||||
|
|
||||||
namespace Content.Server.Access.Components
|
namespace Content.Server.Access.Components
|
||||||
@@ -12,6 +15,7 @@ namespace Content.Server.Access.Components
|
|||||||
public sealed class IdCardConsoleComponent : SharedIdCardConsoleComponent
|
public sealed class IdCardConsoleComponent : SharedIdCardConsoleComponent
|
||||||
{
|
{
|
||||||
[Dependency] private readonly IEntityManager _entities = default!;
|
[Dependency] private readonly IEntityManager _entities = default!;
|
||||||
|
[Dependency] private readonly IAdminLogManager _adminLogger = default!;
|
||||||
|
|
||||||
[ViewVariables] private BoundUserInterface? UserInterface => Owner.GetUIOrNull(IdCardConsoleUiKey.Key);
|
[ViewVariables] private BoundUserInterface? UserInterface => Owner.GetUIOrNull(IdCardConsoleUiKey.Key);
|
||||||
|
|
||||||
@@ -26,6 +30,7 @@ namespace Content.Server.Access.Components
|
|||||||
{
|
{
|
||||||
UserInterface.OnReceiveMessage += OnUiReceiveMessage;
|
UserInterface.OnReceiveMessage += OnUiReceiveMessage;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnUiReceiveMessage(ServerBoundUserInterfaceMessage obj)
|
private void OnUiReceiveMessage(ServerBoundUserInterfaceMessage obj)
|
||||||
@@ -38,7 +43,7 @@ namespace Content.Server.Access.Components
|
|||||||
switch (obj.Message)
|
switch (obj.Message)
|
||||||
{
|
{
|
||||||
case WriteToTargetIdMessage msg:
|
case WriteToTargetIdMessage msg:
|
||||||
TryWriteToTargetId(msg.FullName, msg.JobTitle, msg.AccessList);
|
TryWriteToTargetId(msg.FullName, msg.JobTitle, msg.AccessList, player);
|
||||||
UpdateUserInterface();
|
UpdateUserInterface();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -60,17 +65,17 @@ namespace Content.Server.Access.Components
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Called when the "Submit" button in the UI gets pressed.
|
/// Called whenever an access button is pressed, adding or removing that access from the target ID card.
|
||||||
/// Writes data passed from the UI into the ID stored in <see cref="TargetIdSlot"/>, if present.
|
/// Writes data passed from the UI into the ID stored in <see cref="TargetIdSlot"/>, if present.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void TryWriteToTargetId(string newFullName, string newJobTitle, List<string> newAccessList)
|
private void TryWriteToTargetId(string newFullName, string newJobTitle, List<string> newAccessList, EntityUid player)
|
||||||
{
|
{
|
||||||
if (TargetIdSlot.Item is not {Valid: true} targetIdEntity || !PrivilegedIdIsAuthorized())
|
if (TargetIdSlot.Item is not {Valid: true} targetIdEntity || !PrivilegedIdIsAuthorized())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var cardSystem = EntitySystem.Get<IdCardSystem>();
|
var cardSystem = EntitySystem.Get<IdCardSystem>();
|
||||||
cardSystem.TryChangeFullName(targetIdEntity, newFullName);
|
cardSystem.TryChangeFullName(targetIdEntity, newFullName, player: player);
|
||||||
cardSystem.TryChangeJobTitle(targetIdEntity, newJobTitle);
|
cardSystem.TryChangeJobTitle(targetIdEntity, newJobTitle, player: player);
|
||||||
|
|
||||||
if (!newAccessList.TrueForAll(x => AccessLevels.Contains(x)))
|
if (!newAccessList.TrueForAll(x => AccessLevels.Contains(x)))
|
||||||
{
|
{
|
||||||
@@ -80,6 +85,12 @@ namespace Content.Server.Access.Components
|
|||||||
|
|
||||||
var accessSystem = EntitySystem.Get<AccessSystem>();
|
var accessSystem = EntitySystem.Get<AccessSystem>();
|
||||||
accessSystem.TrySetTags(targetIdEntity, newAccessList);
|
accessSystem.TrySetTags(targetIdEntity, newAccessList);
|
||||||
|
|
||||||
|
/*TODO: ECS IdCardConsoleComponent and then log on card ejection, together with the save.
|
||||||
|
This current implementation is pretty shit as it logs 27 entries (27 lines) if someone decides to give themselves AA*/
|
||||||
|
_adminLogger.Add(LogType.Action, LogImpact.Medium,
|
||||||
|
$"{_entities.ToPrettyString(player):player} has modified {_entities.ToPrettyString(targetIdEntity):entity} with the following accesses: [{string.Join(", ", newAccessList)}]");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void UpdateUserInterface()
|
public void UpdateUserInterface()
|
||||||
|
|||||||
@@ -2,11 +2,13 @@ using Content.Shared.Hands.Components;
|
|||||||
using Content.Shared.Inventory;
|
using Content.Shared.Inventory;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using Content.Server.Administration.Logs;
|
||||||
using Content.Server.Kitchen.Components;
|
using Content.Server.Kitchen.Components;
|
||||||
using Content.Server.Popups;
|
using Content.Server.Popups;
|
||||||
using Content.Shared.Access;
|
using Content.Shared.Access;
|
||||||
using Content.Shared.Access.Components;
|
using Content.Shared.Access.Components;
|
||||||
using Content.Shared.Access.Systems;
|
using Content.Shared.Access.Systems;
|
||||||
|
using Content.Shared.Database;
|
||||||
using Content.Shared.PDA;
|
using Content.Shared.PDA;
|
||||||
using Content.Shared.Popups;
|
using Content.Shared.Popups;
|
||||||
using Robust.Shared.Player;
|
using Robust.Shared.Player;
|
||||||
@@ -21,6 +23,7 @@ namespace Content.Server.Access.Systems
|
|||||||
[Dependency] private readonly PopupSystem _popupSystem = default!;
|
[Dependency] private readonly PopupSystem _popupSystem = default!;
|
||||||
[Dependency] private readonly IRobustRandom _random = default!;
|
[Dependency] private readonly IRobustRandom _random = default!;
|
||||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||||
|
[Dependency] private readonly IAdminLogManager _adminLogger = default!;
|
||||||
|
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
@@ -73,22 +76,39 @@ namespace Content.Server.Access.Systems
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool TryChangeJobTitle(EntityUid uid, string jobTitle, IdCardComponent? id = null)
|
/// <summary>
|
||||||
|
/// Attempts to change the job title of a card.
|
||||||
|
/// Returns true/false.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// If provided with a player's EntityUid to the player parameter, adds the change to the admin logs.
|
||||||
|
/// </remarks>
|
||||||
|
public bool TryChangeJobTitle(EntityUid uid, string jobTitle, IdCardComponent? id = null, EntityUid? player = null)
|
||||||
{
|
{
|
||||||
if (!Resolve(uid, ref id))
|
if (!Resolve(uid, ref id))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// TODO: Whenever we get admin logging these should be logged
|
|
||||||
if (jobTitle.Length > SharedIdCardConsoleComponent.MaxJobTitleLength)
|
if (jobTitle.Length > SharedIdCardConsoleComponent.MaxJobTitleLength)
|
||||||
jobTitle = jobTitle[..SharedIdCardConsoleComponent.MaxJobTitleLength];
|
jobTitle = jobTitle[..SharedIdCardConsoleComponent.MaxJobTitleLength];
|
||||||
|
|
||||||
id.JobTitle = jobTitle;
|
id.JobTitle = jobTitle;
|
||||||
Dirty(id);
|
Dirty(id);
|
||||||
UpdateEntityName(uid, id);
|
UpdateEntityName(uid, id);
|
||||||
|
|
||||||
|
if (player != null)
|
||||||
|
_adminLogger.Add(LogType.Identity, LogImpact.Low,
|
||||||
|
$"{ToPrettyString(player.Value):player} has changed the job title of {ToPrettyString(id.Owner):entity} to {jobTitle} ");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool TryChangeFullName(EntityUid uid, string fullName, IdCardComponent? id = null)
|
/// <summary>
|
||||||
|
/// Attempts to change the full name of a card.
|
||||||
|
/// Returns true/false.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// If provided with a player's EntityUid to the player parameter, adds the change to the admin logs.
|
||||||
|
/// </remarks>
|
||||||
|
public bool TryChangeFullName(EntityUid uid, string fullName, IdCardComponent? id = null, EntityUid? player = null)
|
||||||
{
|
{
|
||||||
if (!Resolve(uid, ref id))
|
if (!Resolve(uid, ref id))
|
||||||
return false;
|
return false;
|
||||||
@@ -99,6 +119,10 @@ namespace Content.Server.Access.Systems
|
|||||||
id.FullName = fullName;
|
id.FullName = fullName;
|
||||||
Dirty(id);
|
Dirty(id);
|
||||||
UpdateEntityName(uid, id);
|
UpdateEntityName(uid, id);
|
||||||
|
|
||||||
|
if (player != null)
|
||||||
|
_adminLogger.Add(LogType.Identity, LogImpact.Low,
|
||||||
|
$"{ToPrettyString(player.Value):player} has changed the name of {ToPrettyString(id.Owner):entity} to {fullName} ");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -131,7 +131,15 @@ namespace Content.Server.Atmos.Piping.Unary.EntitySystems
|
|||||||
&& containerManager.TryGetContainer(canister.ContainerName, out var container))
|
&& containerManager.TryGetContainer(canister.ContainerName, out var container))
|
||||||
impact = container.ContainedEntities.Count != 0 ? LogImpact.Medium : LogImpact.High;
|
impact = container.ContainedEntities.Count != 0 ? LogImpact.Medium : LogImpact.High;
|
||||||
|
|
||||||
_adminLogger.Add(LogType.CanisterValve, impact, $"{ToPrettyString(args.Session.AttachedEntity.GetValueOrDefault()):player} set the valve on {ToPrettyString(uid):canister} to {args.Valve:valveState}");
|
var containedGasDict = new Dictionary<Gas, float>();
|
||||||
|
var containedGasArray = Gas.GetValues(typeof(Gas));
|
||||||
|
|
||||||
|
for (int i = 0; i < containedGasArray.Length; i++)
|
||||||
|
{
|
||||||
|
containedGasDict.Add((Gas)i, canister.Air.Moles[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
_adminLogger.Add(LogType.CanisterValve, impact, $"{ToPrettyString(args.Session.AttachedEntity.GetValueOrDefault()):player} set the valve on {ToPrettyString(uid):canister} to {args.Valve:valveState} while it contained [{string.Join(", ", containedGasDict)}]");
|
||||||
|
|
||||||
canister.ReleaseValve = args.Valve;
|
canister.ReleaseValve = args.Valve;
|
||||||
DirtyUI(uid, canister);
|
DirtyUI(uid, canister);
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ using Content.Server.Administration.Managers;
|
|||||||
using Content.Server.Chat.Managers;
|
using Content.Server.Chat.Managers;
|
||||||
using Content.Server.GameTicking;
|
using Content.Server.GameTicking;
|
||||||
using Content.Server.Ghost.Components;
|
using Content.Server.Ghost.Components;
|
||||||
|
using Content.Server.Mind.Components;
|
||||||
using Content.Server.Players;
|
using Content.Server.Players;
|
||||||
using Content.Server.Popups;
|
using Content.Server.Popups;
|
||||||
using Content.Server.Radio.EntitySystems;
|
using Content.Server.Radio.EntitySystems;
|
||||||
@@ -18,6 +19,7 @@ using Content.Shared.Chat;
|
|||||||
using Content.Shared.Database;
|
using Content.Shared.Database;
|
||||||
using Content.Shared.IdentityManagement;
|
using Content.Shared.IdentityManagement;
|
||||||
using Content.Shared.Inventory;
|
using Content.Shared.Inventory;
|
||||||
|
using Robust.Server.GameObjects;
|
||||||
using Robust.Server.Player;
|
using Robust.Server.Player;
|
||||||
using Robust.Shared.Audio;
|
using Robust.Shared.Audio;
|
||||||
using Robust.Shared.Configuration;
|
using Robust.Shared.Configuration;
|
||||||
@@ -268,6 +270,10 @@ public sealed partial class ChatSystem : SharedChatSystem
|
|||||||
var ev = new EntitySpokeEvent(message);
|
var ev = new EntitySpokeEvent(message);
|
||||||
RaiseLocalEvent(source, ev);
|
RaiseLocalEvent(source, ev);
|
||||||
|
|
||||||
|
// To avoid logging any messages sent by entities that are not players, like vendors, cloning, etc.
|
||||||
|
if (!TryComp(source, out ActorComponent? mind))
|
||||||
|
return;
|
||||||
|
|
||||||
if (originalMessage == message)
|
if (originalMessage == message)
|
||||||
_adminLogger.Add(LogType.Chat, LogImpact.Low, $"Say from {ToPrettyString(source):user}: {originalMessage}.");
|
_adminLogger.Add(LogType.Chat, LogImpact.Low, $"Say from {ToPrettyString(source):user}: {originalMessage}.");
|
||||||
else
|
else
|
||||||
@@ -362,18 +368,18 @@ public sealed partial class ChatSystem : SharedChatSystem
|
|||||||
messageWrap = Loc.GetString("chat-manager-send-admin-dead-chat-wrap-message",
|
messageWrap = Loc.GetString("chat-manager-send-admin-dead-chat-wrap-message",
|
||||||
("adminChannelName", Loc.GetString("chat-manager-admin-channel-name")),
|
("adminChannelName", Loc.GetString("chat-manager-admin-channel-name")),
|
||||||
("userName", player.ConnectedClient.UserName));
|
("userName", player.ConnectedClient.UserName));
|
||||||
_adminLogger.Add(LogType.Chat, LogImpact.Low, $"Dead chat from {player:Player}: {message}");
|
_adminLogger.Add(LogType.Chat, LogImpact.Low, $"Admin dead chat from {player:Player}: {message}");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
messageWrap = Loc.GetString("chat-manager-send-dead-chat-wrap-message",
|
messageWrap = Loc.GetString("chat-manager-send-dead-chat-wrap-message",
|
||||||
("deadChannelName", Loc.GetString("chat-manager-dead-channel-name")),
|
("deadChannelName", Loc.GetString("chat-manager-dead-channel-name")),
|
||||||
("playerName", (playerName)));
|
("playerName", (playerName)));
|
||||||
_adminLogger.Add(LogType.Chat, LogImpact.Low, $"Admin dead chat from {player:Player}: {message}");
|
_adminLogger.Add(LogType.Chat, LogImpact.Low, $"Dead chat from {player:Player}: {message}");
|
||||||
}
|
}
|
||||||
|
|
||||||
_chatManager.ChatMessageToMany(ChatChannel.Dead, message, messageWrap, source, hideChat, clients.ToList());
|
_chatManager.ChatMessageToMany(ChatChannel.Dead, message, messageWrap, source, hideChat, clients.ToList());
|
||||||
_adminLogger.Add(LogType.Chat, LogImpact.Low, $"Dead chat from {player:Player}: {message}");
|
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using Content.Server.Administration.Logs;
|
||||||
using Content.Server.Chemistry.EntitySystems;
|
using Content.Server.Chemistry.EntitySystems;
|
||||||
using Content.Server.Power.Components;
|
using Content.Server.Power.Components;
|
||||||
using Content.Server.UserInterface;
|
using Content.Server.UserInterface;
|
||||||
using Content.Shared.Chemistry.Components;
|
using Content.Shared.Chemistry.Components;
|
||||||
using Content.Shared.Chemistry.Dispenser;
|
using Content.Shared.Chemistry.Dispenser;
|
||||||
|
using Content.Shared.Database;
|
||||||
using Content.Shared.FixedPoint;
|
using Content.Shared.FixedPoint;
|
||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
using Robust.Server.GameObjects;
|
using Robust.Server.GameObjects;
|
||||||
@@ -29,6 +31,7 @@ namespace Content.Server.Chemistry.Components
|
|||||||
|
|
||||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||||
[Dependency] private readonly IEntityManager _entities = default!;
|
[Dependency] private readonly IEntityManager _entities = default!;
|
||||||
|
[Dependency] private readonly IAdminLogManager _adminLogger = default!;
|
||||||
|
|
||||||
[ViewVariables] [DataField("pack", customTypeSerializer:typeof(PrototypeIdSerializer<ReagentDispenserInventoryPrototype>))] private string _packPrototypeId = "";
|
[ViewVariables] [DataField("pack", customTypeSerializer:typeof(PrototypeIdSerializer<ReagentDispenserInventoryPrototype>))] private string _packPrototypeId = "";
|
||||||
|
|
||||||
@@ -171,8 +174,11 @@ namespace Content.Server.Chemistry.Components
|
|||||||
if (BeakerSlot.HasItem)
|
if (BeakerSlot.HasItem)
|
||||||
{
|
{
|
||||||
TryDispense(msg.DispenseIndex);
|
TryDispense(msg.DispenseIndex);
|
||||||
|
// Ew
|
||||||
|
if (BeakerSlot.Item != null)
|
||||||
|
_adminLogger.Add(LogType.ChemicalReaction, LogImpact.Medium,
|
||||||
|
$"{_entities.ToPrettyString(obj.Session.AttachedEntity.Value):player} dispensed {_dispenseAmount}u of {Inventory[msg.DispenseIndex].ID} into {_entities.ToPrettyString(BeakerSlot.Item.Value):entity}");
|
||||||
}
|
}
|
||||||
Logger.Info($"User {obj.Session.UserId.UserId} ({obj.Session.Name}) dispensed {_dispenseAmount}u of {Inventory[msg.DispenseIndex].ID}");
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
|
using Content.Server.Administration.Logs;
|
||||||
using Content.Shared.Verbs;
|
using Content.Shared.Verbs;
|
||||||
using Content.Server.Chemistry.Components;
|
using Content.Server.Chemistry.Components;
|
||||||
using Content.Server.Chemistry.Components.SolutionManager;
|
using Content.Server.Chemistry.Components.SolutionManager;
|
||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
using Robust.Server.GameObjects;
|
using Robust.Server.GameObjects;
|
||||||
using Content.Shared.Chemistry.Components;
|
using Content.Shared.Chemistry.Components;
|
||||||
|
using Content.Shared.Database;
|
||||||
using Content.Shared.FixedPoint;
|
using Content.Shared.FixedPoint;
|
||||||
using Content.Shared.Interaction;
|
using Content.Shared.Interaction;
|
||||||
using Content.Shared.Popups;
|
using Content.Shared.Popups;
|
||||||
@@ -14,6 +16,7 @@ namespace Content.Server.Chemistry.EntitySystems
|
|||||||
public sealed class SolutionTransferSystem : EntitySystem
|
public sealed class SolutionTransferSystem : EntitySystem
|
||||||
{
|
{
|
||||||
[Dependency] private readonly SolutionContainerSystem _solutionContainer = default!;
|
[Dependency] private readonly SolutionContainerSystem _solutionContainer = default!;
|
||||||
|
[Dependency] private readonly IAdminLogManager _adminLogger = default!;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Default transfer amounts for the set-transfer verb.
|
/// Default transfer amounts for the set-transfer verb.
|
||||||
@@ -181,6 +184,9 @@ namespace Content.Server.Chemistry.EntitySystems
|
|||||||
var solution = solutionSystem.Drain(sourceEntity, source, actualAmount);
|
var solution = solutionSystem.Drain(sourceEntity, source, actualAmount);
|
||||||
solutionSystem.Refill(targetEntity, target, solution);
|
solutionSystem.Refill(targetEntity, target, solution);
|
||||||
|
|
||||||
|
_adminLogger.Add(LogType.Action, LogImpact.Medium,
|
||||||
|
$"{EntityManager.ToPrettyString(user):player} transferred {string.Join(", ", solution.Contents)} to {EntityManager.ToPrettyString(targetEntity):entity}, which now contains {string.Join(", ", target.Contents)}");
|
||||||
|
|
||||||
return actualAmount;
|
return actualAmount;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Content.Server.Access.Systems;
|
using Content.Server.Access.Systems;
|
||||||
|
using Content.Server.Administration.Logs;
|
||||||
using Content.Server.AlertLevel;
|
using Content.Server.AlertLevel;
|
||||||
using Content.Server.Chat;
|
using Content.Server.Chat;
|
||||||
using Content.Server.Chat.Systems;
|
using Content.Server.Chat.Systems;
|
||||||
@@ -13,6 +14,7 @@ using Content.Shared.Access.Components;
|
|||||||
using Content.Shared.Access.Systems;
|
using Content.Shared.Access.Systems;
|
||||||
using Content.Shared.CCVar;
|
using Content.Shared.CCVar;
|
||||||
using Content.Shared.Communications;
|
using Content.Shared.Communications;
|
||||||
|
using Content.Shared.Database;
|
||||||
using Content.Shared.Examine;
|
using Content.Shared.Examine;
|
||||||
using Content.Shared.Popups;
|
using Content.Shared.Popups;
|
||||||
using Robust.Server.GameObjects;
|
using Robust.Server.GameObjects;
|
||||||
@@ -33,6 +35,7 @@ namespace Content.Server.Communications
|
|||||||
[Dependency] private readonly ShuttleSystem _shuttle = default!;
|
[Dependency] private readonly ShuttleSystem _shuttle = default!;
|
||||||
[Dependency] private readonly StationSystem _stationSystem = default!;
|
[Dependency] private readonly StationSystem _stationSystem = default!;
|
||||||
[Dependency] private readonly IConfigurationManager _cfg = default!;
|
[Dependency] private readonly IConfigurationManager _cfg = default!;
|
||||||
|
[Dependency] private readonly IAdminLogManager _adminLogger = default!;
|
||||||
|
|
||||||
private const int MaxMessageLength = 256;
|
private const int MaxMessageLength = 256;
|
||||||
private const float UIUpdateInterval = 5.0f;
|
private const float UIUpdateInterval = 5.0f;
|
||||||
@@ -251,9 +254,16 @@ namespace Content.Server.Communications
|
|||||||
if (comp.AnnounceGlobal)
|
if (comp.AnnounceGlobal)
|
||||||
{
|
{
|
||||||
_chatSystem.DispatchGlobalAnnouncement(msg, title, announcementSound: comp.AnnouncementSound, colorOverride: comp.AnnouncementColor);
|
_chatSystem.DispatchGlobalAnnouncement(msg, title, announcementSound: comp.AnnouncementSound, colorOverride: comp.AnnouncementColor);
|
||||||
|
|
||||||
|
if (message.Session.AttachedEntity != null)
|
||||||
|
_adminLogger.Add(LogType.Chat, LogImpact.Low, $"{ToPrettyString(message.Session.AttachedEntity.Value):player} has sent the following global announcement: {msg}");
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_chatSystem.DispatchStationAnnouncement(uid, msg, title, colorOverride: comp.AnnouncementColor);
|
_chatSystem.DispatchStationAnnouncement(uid, msg, title, colorOverride: comp.AnnouncementColor);
|
||||||
|
|
||||||
|
if (message.Session.AttachedEntity != null)
|
||||||
|
_adminLogger.Add(LogType.Chat, LogImpact.Low, $"{ToPrettyString(message.Session.AttachedEntity.Value):player} has sent the following station announcement: {msg}");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnCallShuttleMessage(EntityUid uid, CommunicationsConsoleComponent comp, CommunicationsConsoleCallEmergencyShuttleMessage message)
|
private void OnCallShuttleMessage(EntityUid uid, CommunicationsConsoleComponent comp, CommunicationsConsoleCallEmergencyShuttleMessage message)
|
||||||
@@ -266,6 +276,7 @@ namespace Content.Server.Communications
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_roundEndSystem.RequestRoundEnd(uid);
|
_roundEndSystem.RequestRoundEnd(uid);
|
||||||
|
_adminLogger.Add(LogType.Action, LogImpact.Extreme, $"{ToPrettyString(mob):player} has called the shuttle.");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnRecallShuttleMessage(EntityUid uid, CommunicationsConsoleComponent comp, CommunicationsConsoleRecallEmergencyShuttleMessage message)
|
private void OnRecallShuttleMessage(EntityUid uid, CommunicationsConsoleComponent comp, CommunicationsConsoleRecallEmergencyShuttleMessage message)
|
||||||
@@ -279,6 +290,7 @@ namespace Content.Server.Communications
|
|||||||
}
|
}
|
||||||
|
|
||||||
_roundEndSystem.CancelRoundEndCountdown(uid);
|
_roundEndSystem.CancelRoundEndCountdown(uid);
|
||||||
|
_adminLogger.Add(LogType.Action, LogImpact.Extreme, $"{ToPrettyString(mob):player} has recalled the shuttle.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
|
using Content.Server.Administration.Logs;
|
||||||
using Content.Server.Construction.Components;
|
using Content.Server.Construction.Components;
|
||||||
using Content.Server.DoAfter;
|
using Content.Server.DoAfter;
|
||||||
using Content.Shared.Construction;
|
using Content.Shared.Construction;
|
||||||
using Content.Shared.Construction.EntitySystems;
|
using Content.Shared.Construction.EntitySystems;
|
||||||
using Content.Shared.Construction.Steps;
|
using Content.Shared.Construction.Steps;
|
||||||
|
using Content.Shared.Database;
|
||||||
using Content.Shared.Interaction;
|
using Content.Shared.Interaction;
|
||||||
using Robust.Shared.Containers;
|
using Robust.Shared.Containers;
|
||||||
|
|
||||||
@@ -10,6 +12,8 @@ namespace Content.Server.Construction
|
|||||||
{
|
{
|
||||||
public sealed partial class ConstructionSystem
|
public sealed partial class ConstructionSystem
|
||||||
{
|
{
|
||||||
|
[Dependency] private readonly IAdminLogManager _adminLogger = default!;
|
||||||
|
|
||||||
private readonly HashSet<EntityUid> _constructionUpdateQueue = new();
|
private readonly HashSet<EntityUid> _constructionUpdateQueue = new();
|
||||||
|
|
||||||
private void InitializeInteractions()
|
private void InitializeInteractions()
|
||||||
@@ -38,7 +42,7 @@ namespace Content.Server.Construction
|
|||||||
/// <remarks>When <see cref="validation"/> is true, this method will simply return whether the interaction
|
/// <remarks>When <see cref="validation"/> is true, this method will simply return whether the interaction
|
||||||
/// would be handled by the entity or not. It essentially becomes a pure method that modifies nothing.</remarks>
|
/// would be handled by the entity or not. It essentially becomes a pure method that modifies nothing.</remarks>
|
||||||
/// <returns>The result of this interaction with the entity.</returns>
|
/// <returns>The result of this interaction with the entity.</returns>
|
||||||
private HandleResult HandleEvent(EntityUid uid, object ev, bool validation, ConstructionComponent? construction = null)
|
private HandleResult HandleEvent(EntityUid uid, object ev, bool validation, ConstructionComponent? construction = null, InteractUsingEvent? args = null)
|
||||||
{
|
{
|
||||||
if (!Resolve(uid, ref construction))
|
if (!Resolve(uid, ref construction))
|
||||||
return HandleResult.False;
|
return HandleResult.False;
|
||||||
@@ -160,6 +164,9 @@ namespace Content.Server.Construction
|
|||||||
|
|
||||||
// We change the node now.
|
// We change the node now.
|
||||||
ChangeNode(uid, user, edge.Target, true, construction);
|
ChangeNode(uid, user, edge.Target, true, construction);
|
||||||
|
|
||||||
|
if (ev is ConstructionDoAfterComplete event1 && event1.WrappedEvent is InteractUsingEvent event2)
|
||||||
|
_adminLogger.Add(LogType.Action, LogImpact.Medium, $"{ToPrettyString(event2.User):player} changed {ToPrettyString(uid):entity}'s node to {edge.Target}");
|
||||||
}
|
}
|
||||||
|
|
||||||
return HandleResult.True;
|
return HandleResult.True;
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using Content.Server.Administration.Logs;
|
||||||
using Content.Server.DoAfter;
|
using Content.Server.DoAfter;
|
||||||
using Content.Server.Hands.Components;
|
using Content.Server.Hands.Components;
|
||||||
using Content.Shared.ActionBlocker;
|
using Content.Shared.ActionBlocker;
|
||||||
using Content.Shared.Alert;
|
using Content.Shared.Alert;
|
||||||
using Content.Shared.Cuffs.Components;
|
using Content.Shared.Cuffs.Components;
|
||||||
|
using Content.Shared.Database;
|
||||||
using Content.Shared.Hands.EntitySystems;
|
using Content.Shared.Hands.EntitySystems;
|
||||||
using Content.Shared.Interaction;
|
using Content.Shared.Interaction;
|
||||||
using Content.Shared.Popups;
|
using Content.Shared.Popups;
|
||||||
@@ -25,6 +27,7 @@ namespace Content.Server.Cuffs.Components
|
|||||||
[Dependency] private readonly IEntityManager _entMan = default!;
|
[Dependency] private readonly IEntityManager _entMan = default!;
|
||||||
[Dependency] private readonly IEntitySystemManager _sysMan = default!;
|
[Dependency] private readonly IEntitySystemManager _sysMan = default!;
|
||||||
[Dependency] private readonly IComponentFactory _componentFactory = default!;
|
[Dependency] private readonly IComponentFactory _componentFactory = default!;
|
||||||
|
[Dependency] private readonly IAdminLogManager _adminLogger = default!;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// How many of this entity's hands are currently cuffed.
|
/// How many of this entity's hands are currently cuffed.
|
||||||
@@ -280,6 +283,16 @@ namespace Content.Server.Cuffs.Components
|
|||||||
{
|
{
|
||||||
user.PopupMessage(Owner, Loc.GetString("cuffable-component-remove-cuffs-by-other-success-message", ("otherName", user)));
|
user.PopupMessage(Owner, Loc.GetString("cuffable-component-remove-cuffs-by-other-success-message", ("otherName", user)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (user == Owner)
|
||||||
|
{
|
||||||
|
_adminLogger.Add(LogType.Action, LogImpact.Medium, $"{_entMan.ToPrettyString(user):player} has successfully uncuffed themselves");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_adminLogger.Add(LogType.Action, LogImpact.Medium, $"{_entMan.ToPrettyString(user):player} has successfully uncuffed {_entMan.ToPrettyString(Owner):player}");
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Content.Server.Administration.Components;
|
using Content.Server.Administration.Components;
|
||||||
|
using Content.Server.Administration.Logs;
|
||||||
using Content.Server.DoAfter;
|
using Content.Server.DoAfter;
|
||||||
using Content.Server.Hands.Components;
|
using Content.Server.Hands.Components;
|
||||||
using Content.Shared.Cuffs.Components;
|
using Content.Shared.Cuffs.Components;
|
||||||
|
using Content.Shared.Database;
|
||||||
using Content.Shared.Interaction;
|
using Content.Shared.Interaction;
|
||||||
using Content.Shared.Popups;
|
using Content.Shared.Popups;
|
||||||
using Content.Shared.Stunnable;
|
using Content.Shared.Stunnable;
|
||||||
@@ -16,6 +18,7 @@ namespace Content.Server.Cuffs.Components
|
|||||||
public sealed class HandcuffComponent : SharedHandcuffComponent
|
public sealed class HandcuffComponent : SharedHandcuffComponent
|
||||||
{
|
{
|
||||||
[Dependency] private readonly IEntityManager _entities = default!;
|
[Dependency] private readonly IEntityManager _entities = default!;
|
||||||
|
[Dependency] private readonly IAdminLogManager _adminLogger = default!;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The time it takes to apply a <see cref="CuffedComponent"/> to an entity.
|
/// The time it takes to apply a <see cref="CuffedComponent"/> to an entity.
|
||||||
@@ -173,11 +176,13 @@ namespace Content.Server.Cuffs.Components
|
|||||||
if (target == user)
|
if (target == user)
|
||||||
{
|
{
|
||||||
user.PopupMessage(Loc.GetString("handcuff-component-cuff-self-success-message"));
|
user.PopupMessage(Loc.GetString("handcuff-component-cuff-self-success-message"));
|
||||||
|
_adminLogger.Add(LogType.Action, LogImpact.Medium, $"{_entities.ToPrettyString(user):player} has cuffed himself");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
user.PopupMessage(Loc.GetString("handcuff-component-cuff-other-success-message",("otherName", target)));
|
user.PopupMessage(Loc.GetString("handcuff-component-cuff-other-success-message",("otherName", target)));
|
||||||
target.PopupMessage(Loc.GetString("handcuff-component-cuff-by-other-success-message", ("otherName", user)));
|
target.PopupMessage(Loc.GetString("handcuff-component-cuff-by-other-success-message", ("otherName", user)));
|
||||||
|
_adminLogger.Add(LogType.Action, LogImpact.Medium, $"{_entities.ToPrettyString(user):player} has cuffed {_entities.ToPrettyString(target):player}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ using Content.Shared.Popups;
|
|||||||
using Content.Shared.Verbs;
|
using Content.Shared.Verbs;
|
||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
using Robust.Shared.Player;
|
using Robust.Shared.Player;
|
||||||
using Content.Shared.Hands.EntitySystems;
|
|
||||||
using Content.Shared.Interaction;
|
using Content.Shared.Interaction;
|
||||||
using Robust.Shared.Audio;
|
using Robust.Shared.Audio;
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
|
using System.Linq;
|
||||||
using Content.Server.Administration.Logs;
|
using Content.Server.Administration.Logs;
|
||||||
|
using Content.Server.Chemistry.Components.SolutionManager;
|
||||||
using Content.Server.Explosion.Components;
|
using Content.Server.Explosion.Components;
|
||||||
using Content.Server.Flash;
|
using Content.Server.Flash;
|
||||||
using Content.Server.Flash.Components;
|
using Content.Server.Flash.Components;
|
||||||
@@ -13,7 +15,10 @@ using Content.Shared.Trigger;
|
|||||||
using Content.Shared.Database;
|
using Content.Shared.Database;
|
||||||
using Content.Shared.Explosion;
|
using Content.Shared.Explosion;
|
||||||
using Content.Shared.Interaction;
|
using Content.Shared.Interaction;
|
||||||
|
using Content.Shared.Payload.Components;
|
||||||
using Content.Shared.StepTrigger.Systems;
|
using Content.Shared.StepTrigger.Systems;
|
||||||
|
using Robust.Server.Containers;
|
||||||
|
using Robust.Shared.Containers;
|
||||||
|
|
||||||
namespace Content.Server.Explosion.EntitySystems
|
namespace Content.Server.Explosion.EntitySystems
|
||||||
{
|
{
|
||||||
@@ -39,7 +44,8 @@ namespace Content.Server.Explosion.EntitySystems
|
|||||||
[Dependency] private readonly FixtureSystem _fixtures = default!;
|
[Dependency] private readonly FixtureSystem _fixtures = default!;
|
||||||
[Dependency] private readonly FlashSystem _flashSystem = default!;
|
[Dependency] private readonly FlashSystem _flashSystem = default!;
|
||||||
[Dependency] private readonly SharedBroadphaseSystem _broadphase = default!;
|
[Dependency] private readonly SharedBroadphaseSystem _broadphase = default!;
|
||||||
[Dependency] private readonly IAdminLogManager _adminLogger= default!;
|
[Dependency] private readonly IAdminLogManager _adminLogger = default!;
|
||||||
|
[Dependency] private readonly SharedContainerSystem _container = default!;
|
||||||
|
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
@@ -118,8 +124,24 @@ namespace Content.Server.Explosion.EntitySystems
|
|||||||
|
|
||||||
if (user != null)
|
if (user != null)
|
||||||
{
|
{
|
||||||
_adminLogger.Add(LogType.Trigger,
|
// Check if entity is bomb/mod. grenade/etc
|
||||||
$"{ToPrettyString(user.Value):user} started a {delay} second timer trigger on entity {ToPrettyString(uid):timer}");
|
if (_container.TryGetContainer(uid, "payload", out IContainer? container) &&
|
||||||
|
TryComp(container.ContainedEntities.First(), out ChemicalPayloadComponent? chemicalPayloadComponent))
|
||||||
|
{
|
||||||
|
// If a beaker is missing, the entity won't explode, so no reason to log it
|
||||||
|
if (!TryComp(chemicalPayloadComponent?.BeakerSlotA.Item, out SolutionContainerManagerComponent? beakerA) ||
|
||||||
|
!TryComp(chemicalPayloadComponent?.BeakerSlotB.Item, out SolutionContainerManagerComponent? beakerB))
|
||||||
|
return;
|
||||||
|
|
||||||
|
_adminLogger.Add(LogType.Trigger,
|
||||||
|
$"{ToPrettyString(user.Value):user} started a {delay} second timer trigger on entity {ToPrettyString(uid):timer}, which contains [{string.Join(", ", beakerA.Solutions.Values.First())}] in one beaker and [{string.Join(", ", beakerB.Solutions.Values.First())}] in the other.");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_adminLogger.Add(LogType.Trigger,
|
||||||
|
$"{ToPrettyString(user.Value):user} started a {delay} second timer trigger on entity {ToPrettyString(uid):timer}");
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -225,10 +225,7 @@ namespace Content.Server.Interaction
|
|||||||
RaiseLocalEvent(item.Value, ev, false);
|
RaiseLocalEvent(item.Value, ev, false);
|
||||||
|
|
||||||
if (ev.Handled)
|
if (ev.Handled)
|
||||||
{
|
|
||||||
_adminLogger.Add(LogType.AttackArmedWide, LogImpact.Low, $"{ToPrettyString(user):user} wide attacked with {ToPrettyString(item.Value):used} at {coordinates}");
|
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -236,20 +233,7 @@ namespace Content.Server.Interaction
|
|||||||
RaiseLocalEvent(item.Value, ev, false);
|
RaiseLocalEvent(item.Value, ev, false);
|
||||||
|
|
||||||
if (ev.Handled)
|
if (ev.Handled)
|
||||||
{
|
|
||||||
if (target != null)
|
|
||||||
{
|
|
||||||
_adminLogger.Add(LogType.AttackArmedClick, LogImpact.Low,
|
|
||||||
$"{ToPrettyString(user):user} attacked {ToPrettyString(target.Value):target} with {ToPrettyString(item.Value):used} at {coordinates}");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_adminLogger.Add(LogType.AttackArmedClick, LogImpact.Low,
|
|
||||||
$"{ToPrettyString(user):user} attacked with {ToPrettyString(item.Value):used} at {coordinates}");
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (!wideAttack && target != null && HasComp<ItemComponent>(target.Value))
|
else if (!wideAttack && target != null && HasComp<ItemComponent>(target.Value))
|
||||||
@@ -279,19 +263,6 @@ namespace Content.Server.Interaction
|
|||||||
{
|
{
|
||||||
var ev = new ClickAttackEvent(used, user, coordinates, target);
|
var ev = new ClickAttackEvent(used, user, coordinates, target);
|
||||||
RaiseLocalEvent(used, ev, false);
|
RaiseLocalEvent(used, ev, false);
|
||||||
if (ev.Handled)
|
|
||||||
{
|
|
||||||
if (target != null)
|
|
||||||
{
|
|
||||||
_adminLogger.Add(LogType.AttackUnarmedClick, LogImpact.Low,
|
|
||||||
$"{ToPrettyString(user):user} attacked {ToPrettyString(target.Value):target} at {coordinates}");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_adminLogger.Add(LogType.AttackUnarmedClick, LogImpact.Low,
|
|
||||||
$"{ToPrettyString(user):user} attacked at {coordinates}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
|
using Content.Server.Administration.Logs;
|
||||||
using Content.Server.Popups;
|
using Content.Server.Popups;
|
||||||
using Content.Server.UserInterface;
|
using Content.Server.UserInterface;
|
||||||
|
using Content.Shared.Database;
|
||||||
using Content.Shared.Examine;
|
using Content.Shared.Examine;
|
||||||
using Content.Shared.IdentityManagement;
|
using Content.Shared.IdentityManagement;
|
||||||
using Content.Shared.Interaction;
|
using Content.Shared.Interaction;
|
||||||
@@ -16,6 +18,8 @@ namespace Content.Server.Paper
|
|||||||
[Dependency] private readonly TagSystem _tagSystem = default!;
|
[Dependency] private readonly TagSystem _tagSystem = default!;
|
||||||
[Dependency] private readonly PopupSystem _popupSystem = default!;
|
[Dependency] private readonly PopupSystem _popupSystem = default!;
|
||||||
[Dependency] private readonly UserInterfaceSystem _uiSystem = default!;
|
[Dependency] private readonly UserInterfaceSystem _uiSystem = default!;
|
||||||
|
[Dependency] private readonly IAdminLogManager _adminLogger = default!;
|
||||||
|
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
base.Initialize();
|
base.Initialize();
|
||||||
@@ -77,7 +81,7 @@ namespace Content.Server.Paper
|
|||||||
if (_tagSystem.HasTag(args.Used, "Write"))
|
if (_tagSystem.HasTag(args.Used, "Write"))
|
||||||
{
|
{
|
||||||
if (!TryComp<ActorComponent>(args.User, out var actor))
|
if (!TryComp<ActorComponent>(args.User, out var actor))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
paperComp.Mode = PaperAction.Write;
|
paperComp.Mode = PaperAction.Write;
|
||||||
UpdateUserInterface(uid, paperComp);
|
UpdateUserInterface(uid, paperComp);
|
||||||
@@ -110,6 +114,10 @@ namespace Content.Server.Paper
|
|||||||
if (TryComp<MetaDataComponent>(uid, out var meta))
|
if (TryComp<MetaDataComponent>(uid, out var meta))
|
||||||
meta.EntityDescription = "";
|
meta.EntityDescription = "";
|
||||||
|
|
||||||
|
if (args.Session.AttachedEntity != null)
|
||||||
|
_adminLogger.Add(LogType.Chat, LogImpact.Low,
|
||||||
|
$"{ToPrettyString(args.Session.AttachedEntity.Value):player} has written on {ToPrettyString(uid):entity} the following text: {args.Text}");
|
||||||
|
|
||||||
UpdateUserInterface(uid, paperComp);
|
UpdateUserInterface(uid, paperComp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ namespace Content.Shared.Throwing
|
|||||||
if (!EntityManager.TryGetComponent(component.Owner, out FixturesComponent? fixturesComponent) ||
|
if (!EntityManager.TryGetComponent(component.Owner, out FixturesComponent? fixturesComponent) ||
|
||||||
fixturesComponent.Fixtures.Count != 1) return;
|
fixturesComponent.Fixtures.Count != 1) return;
|
||||||
if (!EntityManager.TryGetComponent(component.Owner, out PhysicsComponent? physicsComponent)) return;
|
if (!EntityManager.TryGetComponent(component.Owner, out PhysicsComponent? physicsComponent)) return;
|
||||||
|
|
||||||
if (fixturesComponent.Fixtures.ContainsKey(ThrowingFixture))
|
if (fixturesComponent.Fixtures.ContainsKey(ThrowingFixture))
|
||||||
{
|
{
|
||||||
Logger.Error($"Found existing throwing fixture on {component.Owner}");
|
Logger.Error($"Found existing throwing fixture on {component.Owner}");
|
||||||
|
|||||||
Reference in New Issue
Block a user