diff --git a/Content.Client/Verbs/VerbSystem.cs b/Content.Client/Verbs/VerbSystem.cs index b447b077cb..ba948a62fc 100644 --- a/Content.Client/Verbs/VerbSystem.cs +++ b/Content.Client/Verbs/VerbSystem.cs @@ -206,7 +206,11 @@ namespace Content.Client.Verbs return; } - ExecuteVerb(verb); + var user = _playerManager.LocalPlayer?.ControlledEntityUid; + if (user == null) + return; + + ExecuteVerb(verb, user.Value, target); if (!verb.ClientExclusive) { diff --git a/Content.Server/Administration/AdminVerbSystem.cs b/Content.Server/Administration/AdminVerbSystem.cs index 7eeb5f7f5e..b923787471 100644 --- a/Content.Server/Administration/AdminVerbSystem.cs +++ b/Content.Server/Administration/AdminVerbSystem.cs @@ -15,6 +15,7 @@ using Content.Server.Mind.Commands; using Content.Server.Mind.Components; using Content.Server.Players; using Content.Shared.Administration; +using Content.Shared.Administration.Logs; using Content.Shared.Body.Components; using Content.Shared.GameTicking; using Content.Shared.Interaction.Helpers; @@ -67,6 +68,7 @@ namespace Content.Server.Administration verb.Category = VerbCategory.Debug; verb.IconTexture = "/Textures/Interface/VerbIcons/delete_transparent.svg.192dpi.png"; verb.Act = () => args.Target.Delete(); + verb.Impact = LogImpact.Medium; args.Verbs.Add(verb); } @@ -78,6 +80,7 @@ namespace Content.Server.Administration verb.Category = VerbCategory.Debug; verb.IconTexture = "/Textures/Interface/VerbIcons/rejuvenate.svg.192dpi.png"; verb.Act = () => RejuvenateCommand.PerformRejuvenate(args.Target); + verb.Impact = LogImpact.Medium; args.Verbs.Add(verb); } @@ -95,6 +98,7 @@ namespace Content.Server.Administration { player.ContentData()?.Mind?.TransferTo(args.Target.Uid, ghostCheckOverride: true); }; + verb.Impact = LogImpact.High; args.Verbs.Add(verb); } @@ -108,9 +112,11 @@ namespace Content.Server.Administration verb.Category = VerbCategory.Debug; verb.IconTexture = "/Textures/Interface/VerbIcons/sentient.svg.192dpi.png"; verb.Act = () => MakeSentientCommand.MakeSentient(args.Target.Uid, EntityManager); + verb.Impact = LogImpact.Medium; args.Verbs.Add(verb); } + // Atillery if (_adminManager.HasAdminFlag(player, AdminFlags.Fun)) { Verb verb = new(); @@ -125,6 +131,7 @@ namespace Content.Server.Administration body.Gib(); } }; + verb.Impact = LogImpact.Extreme; // if you're just outright killing a person, I guess that deserves to be extreme? args.Verbs.Add(verb); } @@ -137,6 +144,7 @@ namespace Content.Server.Administration verb.Category = VerbCategory.Debug; verb.IconTexture = "/Textures/Interface/VerbIcons/outfit.svg.192dpi.png"; verb.Act = () => _euiManager.OpenEui(new SetOutfitEui(args.Target), player); + verb.Impact = LogImpact.Medium; args.Verbs.Add(verb); } @@ -179,6 +187,7 @@ namespace Content.Server.Administration // TODO VERB ICON add ghost icon // Where is the national park service icon for haunted forests? verb.Act = () => _ghostRoleSystem.OpenMakeGhostRoleEui(player, args.Target.Uid); + verb.Impact = LogImpact.Medium; args.Verbs.Add(verb); } @@ -203,6 +212,7 @@ namespace Content.Server.Administration verb.Category = VerbCategory.Debug; verb.IconTexture = "/Textures/Interface/VerbIcons/spill.svg.192dpi.png"; verb.Act = () => OpenEditSolutionsEui(player, args.Target.Uid); + verb.Impact = LogImpact.Medium; // maybe high depending on WHAT reagents they add... args.Verbs.Add(verb); } } diff --git a/Content.Server/Fluids/EntitySystems/PuddleSystem.cs b/Content.Server/Fluids/EntitySystems/PuddleSystem.cs index 1be9a87859..00749f5200 100644 --- a/Content.Server/Fluids/EntitySystems/PuddleSystem.cs +++ b/Content.Server/Fluids/EntitySystems/PuddleSystem.cs @@ -5,6 +5,7 @@ using System.Linq; using Content.Server.Chemistry.EntitySystems; using Content.Server.Construction.Components; using Content.Server.Fluids.Components; +using Content.Shared.Administration.Logs; using Content.Shared.Chemistry.Components; using Content.Shared.Chemistry.Reagent; using Content.Shared.Directions; @@ -104,6 +105,7 @@ namespace Content.Server.Fluids.EntitySystems // TODO VERB ICONS spill icon? pouring out a glass/beaker? verb.Act = () => _solutionContainerSystem.SplitSolution(args.Target.Uid, solution, solution.DrainAvailable).SpillAt(args.Target.Transform.Coordinates, "PuddleSmear"); + verb.Impact = LogImpact.Medium; // dangerous reagent reaction are logged separately. args.Verbs.Add(verb); } diff --git a/Content.Server/Morgue/MorgueSystem.cs b/Content.Server/Morgue/MorgueSystem.cs index 26e44b7629..1907bfc885 100644 --- a/Content.Server/Morgue/MorgueSystem.cs +++ b/Content.Server/Morgue/MorgueSystem.cs @@ -1,4 +1,5 @@ using Content.Server.Morgue.Components; +using Content.Shared.Administration.Logs; using Content.Shared.Verbs; using JetBrains.Annotations; using Robust.Shared.GameObjects; @@ -28,6 +29,7 @@ namespace Content.Server.Morgue verb.Text = Loc.GetString("cremate-verb-get-data-text"); // TODO VERB ICON add flame/burn symbol? verb.Act = () => component.TryCremate(); + verb.Impact = LogImpact.Medium; // could be a body? or evidence? I dunno. args.Verbs.Add(verb); } diff --git a/Content.Server/Verbs/Commands/InvokeVerbCommand.cs b/Content.Server/Verbs/Commands/InvokeVerbCommand.cs index 7985fc761b..e8a87981c1 100644 --- a/Content.Server/Verbs/Commands/InvokeVerbCommand.cs +++ b/Content.Server/Verbs/Commands/InvokeVerbCommand.cs @@ -78,7 +78,7 @@ namespace Content.Server.Verbs.Commands verbs.TryGetValue(key, out var vset) && vset.Any()) { - verbSystem.ExecuteVerb(vset.First()); + verbSystem.ExecuteVerb(vset.First(), playerEntity.Uid, target.Uid, forced: true); shell.WriteLine(Loc.GetString("invoke-verb-command-success", ("verb", verbName), ("target", target), ("player", playerEntity))); return; } @@ -89,7 +89,7 @@ namespace Content.Server.Verbs.Commands { if (verb.Text.ToLowerInvariant() == verbName) { - verbSystem.ExecuteVerb(verb); + verbSystem.ExecuteVerb(verb, playerEntity.Uid, target.Uid, forced: true); shell.WriteLine(Loc.GetString("invoke-verb-command-success", ("verb", verb.Text), ("target", target), ("player", playerEntity))); return; } diff --git a/Content.Server/Verbs/VerbSystem.cs b/Content.Server/Verbs/VerbSystem.cs index 0ca59c62f5..906038366c 100644 --- a/Content.Server/Verbs/VerbSystem.cs +++ b/Content.Server/Verbs/VerbSystem.cs @@ -45,7 +45,7 @@ namespace Content.Server.Verbs // Find the requested verb. if (verbs.TryGetValue(args.RequestedVerb, out var verb)) - ExecuteVerb(verb); + ExecuteVerb(verb, userEntity.Uid, args.Target); else // 404 Verb not found. Note that this could happen due to something as simple as opening the verb menu, walking away, then trying // to run the pickup-item verb. So maybe this shouldn't even be logged? diff --git a/Content.Shared/Administration/Logs/LogType.cs b/Content.Shared/Administration/Logs/LogType.cs index 052edeb2e3..0da00c4213 100644 --- a/Content.Shared/Administration/Logs/LogType.cs +++ b/Content.Shared/Administration/Logs/LogType.cs @@ -12,6 +12,7 @@ public enum LogType EventStarted = 6, EventRan = 16, EventStopped = 7, + Verb = 19, ShuttleCalled = 8, ShuttleRecalled = 9, ChemicalReaction = 17, diff --git a/Content.Shared/Interaction/SharedInteractionSystem.cs b/Content.Shared/Interaction/SharedInteractionSystem.cs index ebe839b24d..fe6e234e5f 100644 --- a/Content.Shared/Interaction/SharedInteractionSystem.cs +++ b/Content.Shared/Interaction/SharedInteractionSystem.cs @@ -530,7 +530,7 @@ namespace Content.Shared.Interaction if (verb.Disabled) continue; - _verbSystem.ExecuteVerb(verb); + _verbSystem.ExecuteVerb(verb, user.Uid, target.Uid); break; } } diff --git a/Content.Shared/Verbs/SharedVerbSystem.cs b/Content.Shared/Verbs/SharedVerbSystem.cs index ab89188823..04a8d1a0d1 100644 --- a/Content.Shared/Verbs/SharedVerbSystem.cs +++ b/Content.Shared/Verbs/SharedVerbSystem.cs @@ -1,21 +1,26 @@ using System.Collections.Generic; +using Content.Shared.Administration.Logs; +using Content.Shared.Hands.Components; using Robust.Shared.GameObjects; +using Robust.Shared.IoC; namespace Content.Shared.Verbs { - public class SharedVerbSystem : EntitySystem + public abstract class SharedVerbSystem : EntitySystem { + [Dependency] private readonly SharedAdminLogSystem _logSystem = default!; + /// /// Raises a number of events in order to get all verbs of the given type(s) defined in local systems. This /// does not request verbs from the server. /// - public virtual Dictionary> GetLocalVerbs(IEntity target, IEntity user, VerbType verbTypes, bool force=false) + public virtual Dictionary> GetLocalVerbs(IEntity target, IEntity user, VerbType verbTypes, bool force = false) { Dictionary> verbs = new(); if ((verbTypes & VerbType.Interaction) == VerbType.Interaction) { - GetInteractionVerbsEvent getVerbEvent = new(user, target, force); + GetInteractionVerbsEvent getVerbEvent = new(user, target, force); RaiseLocalEvent(target.Uid, getVerbEvent); verbs.Add(VerbType.Interaction, getVerbEvent.Verbs); } @@ -50,9 +55,12 @@ namespace Content.Shared.Verbs /// /// This will try to call the action delegates and raise the local events for the given verb. /// - public void ExecuteVerb(Verb verb) + public void ExecuteVerb(Verb verb, EntityUid user, EntityUid target, bool forced = false) { + // first, lets log the verb. Just in case it ends up crashing the server or something. + LogVerb(verb, user, target, forced); + // then invoke any relevant actions verb.Act?.Invoke(); // Maybe raise a local event @@ -64,5 +72,32 @@ namespace Content.Shared.Verbs RaiseLocalEvent(verb.ExecutionEventArgs); } } + + public void LogVerb(Verb verb, EntityUid user, EntityUid target, bool forced) + { + // first get the held item. again. + EntityUid? used = null; + if (EntityManager.TryGetComponent(user, out SharedHandsComponent? hands)) + { + hands.TryGetActiveHeldEntity(out var useEntityd); + used = useEntityd?.Uid; + if (used != null && EntityManager.TryGetComponent(used.Value, out HandVirtualItemComponent? pull)) + used = pull.BlockingEntity; + } + + // then prepare the basic log message body + var verbText = $"{verb.Category?.Text} {verb.Text}".Trim(); + var logText = forced + ? $"was forced to execute the '{verbText}' verb targeting " // let's not frame people, eh? + : $"executed '{verbText}' verb targeting "; + + // then log with entity information + if (used != null) + _logSystem.Add(LogType.Verb, verb.Impact, + $"{user} {logText} {target} while holding {used}"); + else + _logSystem.Add(LogType.Verb, verb.Impact, + $"{user} {logText} {target}"); + } } } diff --git a/Content.Shared/Verbs/Verb.cs b/Content.Shared/Verbs/Verb.cs index 72469a8c53..79377a71c9 100644 --- a/Content.Shared/Verbs/Verb.cs +++ b/Content.Shared/Verbs/Verb.cs @@ -1,3 +1,4 @@ +using Content.Shared.Administration.Logs; using Robust.Shared.GameObjects; using Robust.Shared.Serialization; using Robust.Shared.Utility; @@ -125,6 +126,14 @@ namespace Content.Shared.Verbs /// public bool CloseMenu = true; + /// + /// How important is this verb, for the purposes of admin logging? + /// + /// + /// If this is just opening a UI or ejecting an id card, this should probably be low. + /// + public LogImpact Impact = LogImpact.Low; + /// /// Compares two verbs based on their , , , /// and . diff --git a/Content.Shared/Verbs/VerbEvents.cs b/Content.Shared/Verbs/VerbEvents.cs index 72375e124f..dc61af7b25 100644 --- a/Content.Shared/Verbs/VerbEvents.cs +++ b/Content.Shared/Verbs/VerbEvents.cs @@ -7,6 +7,7 @@ using Robust.Shared.GameObjects; using Robust.Shared.Serialization; using Robust.Shared.IoC; using Content.Shared.Interaction; +using System.Collections.Immutable; namespace Content.Shared.Verbs {