--- /dev/null
+using Content.Server.GameTicking;
+using Content.Server.Ghost;
+using Content.Shared.Administration;
+using Content.Shared.GameTicking;
+using Content.Shared.Mind;
+using Robust.Server.Player;
+using Robust.Shared.Console;
+
+namespace Content.Server.Administration.Commands;
+
+[AdminCommand(AdminFlags.Admin)]
+public sealed class ForceGhostCommand : LocalizedEntityCommands
+{
+ [Dependency] private readonly IEntityManager _entityManager = default!;
+ [Dependency] private readonly IPlayerManager _playerManager = default!;
+ [Dependency] private readonly GameTicker _gameTicker = default!;
+ [Dependency] private readonly SharedMindSystem _mind = default!;
+ [Dependency] private readonly GhostSystem _ghost = default!;
+
+ public override string Command => "forceghost";
+
+ public override void Execute(IConsoleShell shell, string argStr, string[] args)
+ {
+ if (args.Length == 0 || args.Length > 1)
+ {
+ shell.WriteError(LocalizationManager.GetString("shell-wrong-arguments-number"));
+ return;
+ }
+
+ if (!_playerManager.TryGetSessionByUsername(args[0], out var player))
+ {
+ shell.WriteError(LocalizationManager.GetString("shell-target-player-does-not-exist"));
+ return;
+ }
+
+ if (!_gameTicker.PlayerGameStatuses.TryGetValue(player.UserId, out var playerStatus) ||
+ playerStatus is not PlayerGameStatus.JoinedGame)
+ {
+ shell.WriteLine(Loc.GetString("cmd-forceghost-error-lobby"));
+ return;
+ }
+
+ if (!_mind.TryGetMind(player, out var mindId, out var mind))
+ (mindId, mind) = _mind.CreateMind(player.UserId);
+
+ if (!_ghost.OnGhostAttempt(mindId, false, true, true, mind))
+ shell.WriteLine(Loc.GetString("cmd-forceghost-denied"));
+ }
+
+ public override CompletionResult GetCompletion(IConsoleShell shell, string[] args)
+ {
+ if (args.Length == 1)
+ {
+ return CompletionResult.FromHintOptions(
+ CompletionHelper.SessionNames(players: _playerManager),
+ Loc.GetString("cmd-forceghost-hint"));
+ }
+
+ return CompletionResult.Empty;
+ }
+}
return ghost;
}
- public bool OnGhostAttempt(EntityUid mindId, bool canReturnGlobal, bool viaCommand = false, MindComponent? mind = null)
+ public bool OnGhostAttempt(EntityUid mindId, bool canReturnGlobal, bool viaCommand = false, bool forced = false, MindComponent? mind = null)
{
if (!Resolve(mindId, ref mind))
return false;
var playerEntity = mind.CurrentEntity;
if (playerEntity != null && viaCommand)
- _adminLog.Add(LogType.Mind, $"{EntityManager.ToPrettyString(playerEntity.Value):player} is attempting to ghost via command");
+ {
+ if (forced)
+ _adminLog.Add(LogType.Mind, $"{EntityManager.ToPrettyString(playerEntity.Value):player} was forced to ghost via command");
+ else
+ _adminLog.Add(LogType.Mind, $"{EntityManager.ToPrettyString(playerEntity.Value):player} is attempting to ghost via command");
+ }
var handleEv = new GhostAttemptHandleEvent(mind, canReturnGlobal);
RaiseLocalEvent(handleEv);
if (handleEv.Handled)
return handleEv.Result;
- if (mind.PreventGhosting)
+ if (mind.PreventGhosting && !forced)
{
if (mind.Session != null) // Logging is suppressed to prevent spam from ghost attempts caused by movement attempts
{