Prevent admin-frozen players from ghosting or suiciding, add "Freeze And Mute" verb (#27813)
* prevent admin-frozen players from ghosting or suiciding * Add "Freeze and Mute" admin verb * Allow "Freeze And Mute" admin verb when player is already frozen but not muted * Remove redundant scream handler (scream action just emotes, duh) * AdminFrozenSystem: clean imports * Update Content.Server/Chat/Commands/SuicideCommand.cs Co-authored-by: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> * Update Ghost.cs * retrigger ci (empty commit) --------- Co-authored-by: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com>
This commit is contained in:
@@ -0,0 +1,7 @@
|
|||||||
|
using Content.Shared.Administration;
|
||||||
|
|
||||||
|
namespace Content.Client.Administration.Systems;
|
||||||
|
|
||||||
|
public sealed class AdminFrozenSystem : SharedAdminFrozenSystem
|
||||||
|
{
|
||||||
|
}
|
||||||
16
Content.Server/Administration/Systems/AdminFrozenSystem.cs
Normal file
16
Content.Server/Administration/Systems/AdminFrozenSystem.cs
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
using Content.Shared.Administration;
|
||||||
|
|
||||||
|
namespace Content.Server.Administration.Systems;
|
||||||
|
|
||||||
|
public sealed class AdminFrozenSystem : SharedAdminFrozenSystem
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Freezes and mutes the given entity.
|
||||||
|
/// </summary>
|
||||||
|
public void FreezeAndMute(EntityUid uid)
|
||||||
|
{
|
||||||
|
var comp = EnsureComp<AdminFrozenComponent>(uid);
|
||||||
|
comp.Muted = true;
|
||||||
|
Dirty(uid, comp);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -67,6 +67,7 @@ namespace Content.Server.Administration.Systems
|
|||||||
[Dependency] private readonly StationSystem _stations = default!;
|
[Dependency] private readonly StationSystem _stations = default!;
|
||||||
[Dependency] private readonly StationSpawningSystem _spawning = default!;
|
[Dependency] private readonly StationSpawningSystem _spawning = default!;
|
||||||
[Dependency] private readonly ExamineSystemShared _examine = default!;
|
[Dependency] private readonly ExamineSystemShared _examine = default!;
|
||||||
|
[Dependency] private readonly AdminFrozenSystem _freeze = default!;
|
||||||
|
|
||||||
private readonly Dictionary<ICommonSession, List<EditSolutionsEui>> _openSolutionUis = new();
|
private readonly Dictionary<ICommonSession, List<EditSolutionsEui>> _openSolutionUis = new();
|
||||||
|
|
||||||
@@ -131,24 +132,57 @@ namespace Content.Server.Administration.Systems
|
|||||||
args.Verbs.Add(prayerVerb);
|
args.Verbs.Add(prayerVerb);
|
||||||
|
|
||||||
// Freeze
|
// Freeze
|
||||||
var frozen = HasComp<AdminFrozenComponent>(args.Target);
|
var frozen = TryComp<AdminFrozenComponent>(args.Target, out var frozenComp);
|
||||||
args.Verbs.Add(new Verb
|
var frozenAndMuted = frozenComp?.Muted ?? false;
|
||||||
|
|
||||||
|
if (!frozen)
|
||||||
{
|
{
|
||||||
Priority = -1, // This is just so it doesn't change position in the menu between freeze/unfreeze.
|
args.Verbs.Add(new Verb
|
||||||
Text = frozen
|
|
||||||
? Loc.GetString("admin-verbs-unfreeze")
|
|
||||||
: Loc.GetString("admin-verbs-freeze"),
|
|
||||||
Category = VerbCategory.Admin,
|
|
||||||
Icon = new SpriteSpecifier.Texture(new ("/Textures/Interface/VerbIcons/snow.svg.192dpi.png")),
|
|
||||||
Act = () =>
|
|
||||||
{
|
{
|
||||||
if (frozen)
|
Priority = -1, // This is just so it doesn't change position in the menu between freeze/unfreeze.
|
||||||
RemComp<AdminFrozenComponent>(args.Target);
|
Text = Loc.GetString("admin-verbs-freeze"),
|
||||||
else
|
Category = VerbCategory.Admin,
|
||||||
|
Icon = new SpriteSpecifier.Texture(new ("/Textures/Interface/VerbIcons/snow.svg.192dpi.png")),
|
||||||
|
Act = () =>
|
||||||
|
{
|
||||||
EnsureComp<AdminFrozenComponent>(args.Target);
|
EnsureComp<AdminFrozenComponent>(args.Target);
|
||||||
},
|
},
|
||||||
Impact = LogImpact.Medium,
|
Impact = LogImpact.Medium,
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!frozenAndMuted)
|
||||||
|
{
|
||||||
|
// allow you to additionally mute someone when they are already frozen
|
||||||
|
args.Verbs.Add(new Verb
|
||||||
|
{
|
||||||
|
Priority = -1, // This is just so it doesn't change position in the menu between freeze/unfreeze.
|
||||||
|
Text = Loc.GetString("admin-verbs-freeze-and-mute"),
|
||||||
|
Category = VerbCategory.Admin,
|
||||||
|
Icon = new SpriteSpecifier.Texture(new ("/Textures/Interface/VerbIcons/snow.svg.192dpi.png")),
|
||||||
|
Act = () =>
|
||||||
|
{
|
||||||
|
_freeze.FreezeAndMute(args.Target);
|
||||||
|
},
|
||||||
|
Impact = LogImpact.Medium,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (frozen)
|
||||||
|
{
|
||||||
|
args.Verbs.Add(new Verb
|
||||||
|
{
|
||||||
|
Priority = -1, // This is just so it doesn't change position in the menu between freeze/unfreeze.
|
||||||
|
Text = Loc.GetString("admin-verbs-unfreeze"),
|
||||||
|
Category = VerbCategory.Admin,
|
||||||
|
Icon = new SpriteSpecifier.Texture(new ("/Textures/Interface/VerbIcons/snow.svg.192dpi.png")),
|
||||||
|
Act = () =>
|
||||||
|
{
|
||||||
|
RemComp<AdminFrozenComponent>(args.Target);
|
||||||
|
},
|
||||||
|
Impact = LogImpact.Medium,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Erase
|
// Erase
|
||||||
args.Verbs.Add(new Verb
|
args.Verbs.Add(new Verb
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using Content.Server.GameTicking;
|
using Content.Server.GameTicking;
|
||||||
|
using Content.Server.Popups;
|
||||||
using Content.Shared.Administration;
|
using Content.Shared.Administration;
|
||||||
using Content.Shared.Mind;
|
using Content.Shared.Mind;
|
||||||
using Robust.Shared.Console;
|
using Robust.Shared.Console;
|
||||||
@@ -26,17 +27,27 @@ namespace Content.Server.Chat.Commands
|
|||||||
if (player.Status != SessionStatus.InGame || player.AttachedEntity == null)
|
if (player.Status != SessionStatus.InGame || player.AttachedEntity == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var minds = IoCManager.Resolve<IEntityManager>().System<SharedMindSystem>();
|
var entityManager = IoCManager.Resolve<IEntityManager>();
|
||||||
|
var minds = entityManager.System<SharedMindSystem>();
|
||||||
// This check also proves mind not-null for at the end when the mob is ghosted.
|
// This check also proves mind not-null for at the end when the mob is ghosted.
|
||||||
if (!minds.TryGetMind(player, out var mindId, out var mind) ||
|
if (!minds.TryGetMind(player, out var mindId, out var mind) ||
|
||||||
mind.OwnedEntity is not { Valid: true } victim)
|
mind.OwnedEntity is not { Valid: true } victim)
|
||||||
{
|
{
|
||||||
shell.WriteLine("You don't have a mind!");
|
shell.WriteLine(Loc.GetString("suicide-command-no-mind"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var gameTicker = EntitySystem.Get<GameTicker>();
|
if (entityManager.HasComponent<AdminFrozenComponent>(victim))
|
||||||
var suicideSystem = EntitySystem.Get<SuicideSystem>();
|
{
|
||||||
|
var deniedMessage = Loc.GetString("suicide-command-denied");
|
||||||
|
shell.WriteLine(deniedMessage);
|
||||||
|
entityManager.System<PopupSystem>()
|
||||||
|
.PopupEntity(deniedMessage, victim, victim);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var gameTicker = entityManager.System<GameTicker>();
|
||||||
|
var suicideSystem = entityManager.System<SuicideSystem>();
|
||||||
if (suicideSystem.Suicide(victim))
|
if (suicideSystem.Suicide(victim))
|
||||||
{
|
{
|
||||||
// Prevent the player from returning to the body.
|
// Prevent the player from returning to the body.
|
||||||
@@ -48,7 +59,7 @@ namespace Content.Server.Chat.Commands
|
|||||||
if (gameTicker.OnGhostAttempt(mindId, true, mind: mind))
|
if (gameTicker.OnGhostAttempt(mindId, true, mind: mind))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
shell.WriteLine("You can't ghost right now.");
|
shell.WriteLine(Loc.GetString("ghost-command-denied"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using Content.Server.GameTicking;
|
using Content.Server.GameTicking;
|
||||||
|
using Content.Server.Popups;
|
||||||
using Content.Shared.Administration;
|
using Content.Shared.Administration;
|
||||||
using Content.Shared.Mind;
|
using Content.Shared.Mind;
|
||||||
using Robust.Shared.Console;
|
using Robust.Shared.Console;
|
||||||
@@ -11,15 +12,25 @@ namespace Content.Server.Ghost
|
|||||||
[Dependency] private readonly IEntityManager _entities = default!;
|
[Dependency] private readonly IEntityManager _entities = default!;
|
||||||
|
|
||||||
public string Command => "ghost";
|
public string Command => "ghost";
|
||||||
public string Description => "Give up on life and become a ghost.";
|
public string Description => Loc.GetString("ghost-command-description");
|
||||||
public string Help => "ghost";
|
public string Help => Loc.GetString("ghost-command-help-text");
|
||||||
|
|
||||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||||
{
|
{
|
||||||
var player = shell.Player;
|
var player = shell.Player;
|
||||||
if (player == null)
|
if (player == null)
|
||||||
{
|
{
|
||||||
shell.WriteLine("You have no session, you can't ghost.");
|
shell.WriteLine(Loc.GetString("ghost-command-no-session"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (player.AttachedEntity is { Valid: true } frozen &&
|
||||||
|
_entities.HasComponent<AdminFrozenComponent>(frozen))
|
||||||
|
{
|
||||||
|
var deniedMessage = Loc.GetString("ghost-command-denied");
|
||||||
|
shell.WriteLine(deniedMessage);
|
||||||
|
_entities.System<PopupSystem>()
|
||||||
|
.PopupEntity(deniedMessage, frozen, frozen);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -30,9 +41,9 @@ namespace Content.Server.Ghost
|
|||||||
mind = _entities.GetComponent<MindComponent>(mindId);
|
mind = _entities.GetComponent<MindComponent>(mindId);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!EntitySystem.Get<GameTicker>().OnGhostAttempt(mindId, true, true, mind))
|
if (!_entities.System<GameTicker>().OnGhostAttempt(mindId, true, true, mind))
|
||||||
{
|
{
|
||||||
shell.WriteLine("You can't ghost right now.");
|
shell.WriteLine(Loc.GetString("ghost-command-denied"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,8 +2,13 @@
|
|||||||
|
|
||||||
namespace Content.Shared.Administration;
|
namespace Content.Shared.Administration;
|
||||||
|
|
||||||
[RegisterComponent, Access(typeof(AdminFrozenSystem))]
|
[RegisterComponent, Access(typeof(SharedAdminFrozenSystem))]
|
||||||
[NetworkedComponent]
|
[NetworkedComponent, AutoGenerateComponentState]
|
||||||
public sealed partial class AdminFrozenComponent : Component
|
public sealed partial class AdminFrozenComponent : Component
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Whether the player is also muted.
|
||||||
|
/// </summary>
|
||||||
|
[DataField, AutoNetworkedField]
|
||||||
|
public bool Muted;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +1,17 @@
|
|||||||
using Content.Shared.ActionBlocker;
|
using Content.Shared.ActionBlocker;
|
||||||
|
using Content.Shared.Emoting;
|
||||||
using Content.Shared.Interaction.Events;
|
using Content.Shared.Interaction.Events;
|
||||||
using Content.Shared.Item;
|
using Content.Shared.Item;
|
||||||
using Content.Shared.Movement.Events;
|
using Content.Shared.Movement.Events;
|
||||||
using Content.Shared.Movement.Pulling.Components;
|
using Content.Shared.Movement.Pulling.Components;
|
||||||
using Content.Shared.Movement.Pulling.Events;
|
using Content.Shared.Movement.Pulling.Events;
|
||||||
using Content.Shared.Movement.Pulling.Systems;
|
using Content.Shared.Movement.Pulling.Systems;
|
||||||
|
using Content.Shared.Speech;
|
||||||
using Content.Shared.Throwing;
|
using Content.Shared.Throwing;
|
||||||
|
|
||||||
namespace Content.Shared.Administration;
|
namespace Content.Shared.Administration;
|
||||||
|
|
||||||
public sealed class AdminFrozenSystem : EntitySystem
|
public abstract class SharedAdminFrozenSystem : EntitySystem
|
||||||
{
|
{
|
||||||
[Dependency] private readonly ActionBlockerSystem _blocker = default!;
|
[Dependency] private readonly ActionBlockerSystem _blocker = default!;
|
||||||
[Dependency] private readonly PullingSystem _pulling = default!;
|
[Dependency] private readonly PullingSystem _pulling = default!;
|
||||||
@@ -28,6 +30,16 @@ public sealed class AdminFrozenSystem : EntitySystem
|
|||||||
SubscribeLocalEvent<AdminFrozenComponent, PullAttemptEvent>(OnPullAttempt);
|
SubscribeLocalEvent<AdminFrozenComponent, PullAttemptEvent>(OnPullAttempt);
|
||||||
SubscribeLocalEvent<AdminFrozenComponent, AttackAttemptEvent>(OnAttempt);
|
SubscribeLocalEvent<AdminFrozenComponent, AttackAttemptEvent>(OnAttempt);
|
||||||
SubscribeLocalEvent<AdminFrozenComponent, ChangeDirectionAttemptEvent>(OnAttempt);
|
SubscribeLocalEvent<AdminFrozenComponent, ChangeDirectionAttemptEvent>(OnAttempt);
|
||||||
|
SubscribeLocalEvent<AdminFrozenComponent, EmoteAttemptEvent>(OnEmoteAttempt);
|
||||||
|
SubscribeLocalEvent<AdminFrozenComponent, SpeakAttemptEvent>(OnSpeakAttempt);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnSpeakAttempt(EntityUid uid, AdminFrozenComponent component, SpeakAttemptEvent args)
|
||||||
|
{
|
||||||
|
if (!component.Muted)
|
||||||
|
return;
|
||||||
|
|
||||||
|
args.Cancel();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnAttempt(EntityUid uid, AdminFrozenComponent component, CancellableEntityEventArgs args)
|
private void OnAttempt(EntityUid uid, AdminFrozenComponent component, CancellableEntityEventArgs args)
|
||||||
@@ -62,4 +74,10 @@ public sealed class AdminFrozenSystem : EntitySystem
|
|||||||
{
|
{
|
||||||
_blocker.UpdateCanMove(uid);
|
_blocker.UpdateCanMove(uid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void OnEmoteAttempt(EntityUid uid, AdminFrozenComponent component, EmoteAttemptEvent args)
|
||||||
|
{
|
||||||
|
if (component.Muted)
|
||||||
|
args.Cancel();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -6,6 +6,7 @@ admin-verbs-admin-logs-entity = Entity Logs
|
|||||||
admin-verbs-teleport-to = Teleport To
|
admin-verbs-teleport-to = Teleport To
|
||||||
admin-verbs-teleport-here = Teleport Here
|
admin-verbs-teleport-here = Teleport Here
|
||||||
admin-verbs-freeze = Freeze
|
admin-verbs-freeze = Freeze
|
||||||
|
admin-verbs-freeze-and-mute = Freeze And Mute
|
||||||
admin-verbs-unfreeze = Unfreeze
|
admin-verbs-unfreeze = Unfreeze
|
||||||
admin-verbs-erase = Erase
|
admin-verbs-erase = Erase
|
||||||
admin-verbs-erase-description = Removes the player from the round and crew manifest and deletes their chat messages.
|
admin-verbs-erase-description = Removes the player from the round and crew manifest and deletes their chat messages.
|
||||||
|
|||||||
5
Resources/Locale/en-US/chat/commands/ghost-command.ftl
Normal file
5
Resources/Locale/en-US/chat/commands/ghost-command.ftl
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
ghost-command-description = Give up on life and become a ghost.
|
||||||
|
ghost-command-help-text = The ghost command turns you into a ghost and makes the character you played permanently catatonic.
|
||||||
|
Please note that you cannot return to your character's body after ghosting.
|
||||||
|
ghost-command-no-session = You have no session, you can't ghost.
|
||||||
|
ghost-command-denied = You cannot ghost right now.
|
||||||
@@ -6,3 +6,5 @@ suicide-command-help-text = The suicide command gives you a quick way out of a r
|
|||||||
suicide-command-default-text-others = {$name} is attempting to bite their own tongue!
|
suicide-command-default-text-others = {$name} is attempting to bite their own tongue!
|
||||||
suicide-command-default-text-self = You attempt to bite your own tongue!
|
suicide-command-default-text-self = You attempt to bite your own tongue!
|
||||||
suicide-command-already-dead = You can't suicide. You're dead.
|
suicide-command-already-dead = You can't suicide. You're dead.
|
||||||
|
suicide-command-no-mind = You have no mind!
|
||||||
|
suicide-command-denied = You cannot suicide right now.
|
||||||
|
|||||||
Reference in New Issue
Block a user