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
{