diff --git a/Content.Server/Chat/Managers/ChatManager.cs b/Content.Server/Chat/Managers/ChatManager.cs
index 77c8464af0..3432321b31 100644
--- a/Content.Server/Chat/Managers/ChatManager.cs
+++ b/Content.Server/Chat/Managers/ChatManager.cs
@@ -83,12 +83,13 @@ namespace Content.Server.Chat.Managers
_adminLogger.Add(LogType.Chat, LogImpact.Low, $"Server announcement: {message}");
}
- public void DispatchServerMessage(IPlayerSession player, string message)
+ public void DispatchServerMessage(IPlayerSession player, string message, bool suppressLog = false)
{
var wrappedMessage = Loc.GetString("chat-manager-server-wrap-message", ("message", FormattedMessage.EscapeText(message)));
ChatMessageToOne(ChatChannel.Server, message, wrappedMessage, default, false, player.ConnectedClient);
- _adminLogger.Add(LogType.Chat, LogImpact.Low, $"Server message to {player:Player}: {message}");
+ if (!suppressLog)
+ _adminLogger.Add(LogType.Chat, LogImpact.Low, $"Server message to {player:Player}: {message}");
}
public void SendAdminAnnouncement(string message)
diff --git a/Content.Server/Chat/Managers/IChatManager.cs b/Content.Server/Chat/Managers/IChatManager.cs
index 3fedeb0d35..b860da9624 100644
--- a/Content.Server/Chat/Managers/IChatManager.cs
+++ b/Content.Server/Chat/Managers/IChatManager.cs
@@ -16,7 +16,7 @@ namespace Content.Server.Chat.Managers
/// Override the color of the message being sent.
void DispatchServerAnnouncement(string message, Color? colorOverride = null);
- void DispatchServerMessage(IPlayerSession player, string message);
+ void DispatchServerMessage(IPlayerSession player, string message, bool suppressLog = false);
void TrySendOOCMessage(IPlayerSession player, string message, OOCChatType type);
diff --git a/Content.Server/GameTicking/GameTicker.GamePreset.cs b/Content.Server/GameTicking/GameTicker.GamePreset.cs
index b5636b6f7d..9c58c96cb9 100644
--- a/Content.Server/GameTicking/GameTicker.GamePreset.cs
+++ b/Content.Server/GameTicking/GameTicker.GamePreset.cs
@@ -156,6 +156,14 @@ namespace Content.Server.GameTicking
if (handleEv.Handled)
return handleEv.Result;
+ if (mind.PreventGhosting)
+ {
+ if (mind.Session != null)
+ // Logging is suppressed to prevent spam from ghost attempts caused by movement attempts
+ _chatManager.DispatchServerMessage(mind.Session, Loc.GetString("comp-mind-ghosting-prevented"), true);
+ return false;
+ }
+
var playerEntity = mind.CurrentEntity;
var entities = IoCManager.Resolve();
diff --git a/Content.Server/Mind/Mind.cs b/Content.Server/Mind/Mind.cs
index 1b87bedfa2..5e3cf282f9 100644
--- a/Content.Server/Mind/Mind.cs
+++ b/Content.Server/Mind/Mind.cs
@@ -109,6 +109,13 @@ namespace Content.Server.Mind
[ViewVariables]
public IEnumerable AllObjectives => _objectives;
+ ///
+ /// Prevents user from ghosting out
+ ///
+ [ViewVariables(VVAccess.ReadWrite)]
+ [DataField("preventGhosting")]
+ public bool PreventGhosting { get; set; }
+
///
/// The session of the player owning this mind.
/// Can be null, in which case the player is currently not logged in.
diff --git a/Resources/Locale/en-US/mind/components/mind-component.ftl b/Resources/Locale/en-US/mind/components/mind-component.ftl
index e5ee78202c..0711958b70 100644
--- a/Resources/Locale/en-US/mind/components/mind-component.ftl
+++ b/Resources/Locale/en-US/mind/components/mind-component.ftl
@@ -1,5 +1,7 @@
# MindComponent localization
+comp-mind-ghosting-prevented = You are not able to ghost right now.
+
## Messages displayed when a body is examined and in a certain state
comp-mind-examined-catatonic = { CAPITALIZE(SUBJECT($ent)) } { CONJUGATE-BE($ent) } totally catatonic. The stresses of life in deep-space must have been too much for { OBJECT($ent) }. Any recovery is unlikely.
comp-mind-examined-dead = { CAPITALIZE(POSS-ADJ($ent)) } soul has departed.