diff --git a/Content.Client/Chat/Managers/ChatManager.cs b/Content.Client/Chat/Managers/ChatManager.cs index e428d30f20c702..2137d02d7a9daa 100644 --- a/Content.Client/Chat/Managers/ChatManager.cs +++ b/Content.Client/Chat/Managers/ChatManager.cs @@ -1,74 +1,27 @@ -using Content.Client.Administration.Managers; -using Content.Client.Ghost; -using Content.Shared.Administration; using Content.Shared.Chat; using Robust.Client.Console; -using Robust.Shared.Utility; +using Robust.Shared.Network; namespace Content.Client.Chat.Managers; internal sealed class ChatManager : IChatManager { [Dependency] private readonly IClientConsoleHost _consoleHost = default!; - [Dependency] private readonly IClientAdminManager _adminMgr = default!; - [Dependency] private readonly IEntitySystemManager _systems = default!; - - private ISawmill _sawmill = default!; + [Dependency] private readonly INetManager _net = default!; public void Initialize() { - _sawmill = Logger.GetSawmill("chat"); - _sawmill.Level = LogLevel.Info; + _net.RegisterNetMessage(); } public void SendMessage(string text, ChatSelectChannel channel) { - var str = text.ToString(); - switch (channel) - { - case ChatSelectChannel.Console: - // run locally - _consoleHost.ExecuteCommand(text); - break; - - case ChatSelectChannel.LOOC: - _consoleHost.ExecuteCommand($"looc \"{CommandParsing.Escape(str)}\""); - break; - - case ChatSelectChannel.OOC: - _consoleHost.ExecuteCommand($"ooc \"{CommandParsing.Escape(str)}\""); - break; - - case ChatSelectChannel.Admin: - _consoleHost.ExecuteCommand($"asay \"{CommandParsing.Escape(str)}\""); - break; - - case ChatSelectChannel.Emotes: - _consoleHost.ExecuteCommand($"me \"{CommandParsing.Escape(str)}\""); - break; - - case ChatSelectChannel.Dead: - if (_systems.GetEntitySystemOrNull() is {IsGhost: true}) - goto case ChatSelectChannel.Local; - - if (_adminMgr.HasFlag(AdminFlags.Admin)) - _consoleHost.ExecuteCommand($"dsay \"{CommandParsing.Escape(str)}\""); - else - _sawmill.Warning("Tried to speak on deadchat without being ghost or admin."); - break; - - // TODO sepearate radio and say into separate commands. - case ChatSelectChannel.Radio: - case ChatSelectChannel.Local: - _consoleHost.ExecuteCommand($"say \"{CommandParsing.Escape(str)}\""); - break; - - case ChatSelectChannel.Whisper: - _consoleHost.ExecuteCommand($"whisper \"{CommandParsing.Escape(str)}\""); - break; + if (string.IsNullOrWhiteSpace(text)) + return; - default: - throw new ArgumentOutOfRangeException(nameof(channel), channel, null); - } + if (channel == ChatSelectChannel.Console) + _consoleHost.ExecuteCommand(text); + else + _net.ClientSendMessage(new RequestChatMessage { Text = text, Channel = channel, }); } } diff --git a/Content.Client/UserInterface/Systems/Chat/ChatUIController.cs b/Content.Client/UserInterface/Systems/Chat/ChatUIController.cs index 8f66340a30fedc..7004b16fb5bdf4 100644 --- a/Content.Client/UserInterface/Systems/Chat/ChatUIController.cs +++ b/Content.Client/UserInterface/Systems/Chat/ChatUIController.cs @@ -747,7 +747,7 @@ public void SendMessage(ChatBox box, ChatSelectChannel channel) // Check if message is longer than the character limit if (text.Length > MaxMessageLength) { - var locWarning = Loc.GetString("chat-manager-max-message-length", + var locWarning = Loc.GetString("chat-manager-max-message-length-exceeded-message", ("maxMessageLength", MaxMessageLength)); box.AddLine(locWarning, Color.Orange); return; diff --git a/Content.Server/Administration/Commands/DSay.cs b/Content.Server/Administration/Commands/DSay.cs index 8e7f0f4bf05abc..13220fa5c381b9 100644 --- a/Content.Server/Administration/Commands/DSay.cs +++ b/Content.Server/Administration/Commands/DSay.cs @@ -1,5 +1,6 @@ -using Content.Server.Chat.Systems; +using Content.Server.Chat.Managers; using Content.Shared.Administration; +using Content.Shared.Chat; using Robust.Shared.Console; namespace Content.Server.Administration.Commands @@ -7,7 +8,7 @@ namespace Content.Server.Administration.Commands [AdminCommand(AdminFlags.Moderator)] sealed class DSay : IConsoleCommand { - [Dependency] private readonly IEntityManager _e = default!; + [Dependency] private readonly IChatManager _chat = default!; public string Command => "dsay"; @@ -23,18 +24,11 @@ public void Execute(IConsoleShell shell, string argStr, string[] args) return; } - if (player.AttachedEntity is not { Valid: true } entity) - return; - if (args.Length < 1) return; var message = string.Join(" ", args).Trim(); - if (string.IsNullOrEmpty(message)) - return; - - var chat = _e.System(); - chat.TrySendInGameOOCMessage(entity, message, InGameOOCChatType.Dead, false, shell, player); + _chat.RequestChat(player, message, ChatSelectChannel.Dead); } } } diff --git a/Content.Server/Chat/Commands/AdminChatCommand.cs b/Content.Server/Chat/Commands/AdminChatCommand.cs index 1a7ae050b66491..f76bcd54afa7b3 100644 --- a/Content.Server/Chat/Commands/AdminChatCommand.cs +++ b/Content.Server/Chat/Commands/AdminChatCommand.cs @@ -1,6 +1,7 @@ using Content.Server.Administration; using Content.Server.Chat.Managers; using Content.Shared.Administration; +using Content.Shared.Chat; using Robust.Shared.Console; namespace Content.Server.Chat.Commands @@ -8,6 +9,8 @@ namespace Content.Server.Chat.Commands [AdminCommand(AdminFlags.Adminchat)] internal sealed class AdminChatCommand : IConsoleCommand { + [Dependency] private readonly IChatManager _chat = default!; + public string Command => "asay"; public string Description => "Send chat messages to the private admin chat channel."; public string Help => "asay "; @@ -26,10 +29,7 @@ public void Execute(IConsoleShell shell, string argStr, string[] args) return; var message = string.Join(" ", args).Trim(); - if (string.IsNullOrEmpty(message)) - return; - - IoCManager.Resolve().TrySendOOCMessage(player, message, OOCChatType.Admin); + _chat.RequestChat(player, message, ChatSelectChannel.Admin); } } } diff --git a/Content.Server/Chat/Commands/LOOCCommand.cs b/Content.Server/Chat/Commands/LOOCCommand.cs index e303b9766d84ed..c95e162655b0ad 100644 --- a/Content.Server/Chat/Commands/LOOCCommand.cs +++ b/Content.Server/Chat/Commands/LOOCCommand.cs @@ -1,14 +1,14 @@ -using Content.Server.Chat.Systems; +using Content.Server.Chat.Managers; using Content.Shared.Administration; +using Content.Shared.Chat; using Robust.Shared.Console; -using Robust.Shared.Enums; namespace Content.Server.Chat.Commands { [AnyCommand] internal sealed class LOOCCommand : IConsoleCommand { - [Dependency] private readonly IEntityManager _e = default!; + [Dependency] private readonly IChatManager _chat = default!; public string Command => "looc"; public string Description => "Send Local Out Of Character chat messages."; @@ -22,20 +22,11 @@ public void Execute(IConsoleShell shell, string argStr, string[] args) return; } - if (player.AttachedEntity is not { Valid: true } entity) - return; - - if (player.Status != SessionStatus.InGame) - return; - if (args.Length < 1) return; var message = string.Join(" ", args).Trim(); - if (string.IsNullOrEmpty(message)) - return; - - _e.System().TrySendInGameOOCMessage(entity, message, InGameOOCChatType.Looc, false, shell, player); + _chat.RequestChat(player, message, ChatSelectChannel.LOOC); } } } diff --git a/Content.Server/Chat/Commands/MeCommand.cs b/Content.Server/Chat/Commands/MeCommand.cs index e763d5656e16da..616d01fbcd40b8 100644 --- a/Content.Server/Chat/Commands/MeCommand.cs +++ b/Content.Server/Chat/Commands/MeCommand.cs @@ -1,13 +1,15 @@ -using Content.Server.Chat.Systems; +using Content.Server.Chat.Managers; using Content.Shared.Administration; +using Content.Shared.Chat; using Robust.Shared.Console; -using Robust.Shared.Enums; namespace Content.Server.Chat.Commands { [AnyCommand] internal sealed class MeCommand : IConsoleCommand { + [Dependency] private readonly IChatManager _chat = default!; + public string Command => "me"; public string Description => "Perform an action."; public string Help => "me "; @@ -20,9 +22,6 @@ public void Execute(IConsoleShell shell, string argStr, string[] args) return; } - if (player.Status != SessionStatus.InGame) - return; - if (player.AttachedEntity is not {} playerEntity) { shell.WriteError("You don't have an entity!"); @@ -33,11 +32,7 @@ public void Execute(IConsoleShell shell, string argStr, string[] args) return; var message = string.Join(" ", args).Trim(); - if (string.IsNullOrEmpty(message)) - return; - - IoCManager.Resolve().GetEntitySystem() - .TrySendInGameICMessage(playerEntity, message, InGameICChatType.Emote, ChatTransmitRange.Normal, false, shell, player); + _chat.RequestChat(player, message, ChatSelectChannel.Emotes); } } } diff --git a/Content.Server/Chat/Commands/OOCCommand.cs b/Content.Server/Chat/Commands/OOCCommand.cs index 36f6303fbd145c..1b23807ee8293f 100644 --- a/Content.Server/Chat/Commands/OOCCommand.cs +++ b/Content.Server/Chat/Commands/OOCCommand.cs @@ -1,5 +1,6 @@ using Content.Server.Chat.Managers; using Content.Shared.Administration; +using Content.Shared.Chat; using Robust.Shared.Console; namespace Content.Server.Chat.Commands @@ -7,6 +8,8 @@ namespace Content.Server.Chat.Commands [AnyCommand] internal sealed class OOCCommand : IConsoleCommand { + [Dependency] private readonly IChatManager _chat = default!; + public string Command => "ooc"; public string Description => "Send Out Of Character chat messages."; public string Help => "ooc "; @@ -23,10 +26,7 @@ public void Execute(IConsoleShell shell, string argStr, string[] args) return; var message = string.Join(" ", args).Trim(); - if (string.IsNullOrEmpty(message)) - return; - - IoCManager.Resolve().TrySendOOCMessage(player, message, OOCChatType.OOC); + _chat.RequestChat(player, message, ChatSelectChannel.OOC); } } } diff --git a/Content.Server/Chat/Commands/SayCommand.cs b/Content.Server/Chat/Commands/SayCommand.cs index df6e548e5d9efc..c3313b293c5140 100644 --- a/Content.Server/Chat/Commands/SayCommand.cs +++ b/Content.Server/Chat/Commands/SayCommand.cs @@ -1,13 +1,15 @@ -using Content.Server.Chat.Systems; +using Content.Server.Chat.Managers; using Content.Shared.Administration; +using Content.Shared.Chat; using Robust.Shared.Console; -using Robust.Shared.Enums; namespace Content.Server.Chat.Commands { [AnyCommand] internal sealed class SayCommand : IConsoleCommand { + [Dependency] private readonly IChatManager _chat = default!; + public string Command => "say"; public string Description => "Send chat messages to the local channel or a specified radio channel."; public string Help => "say "; @@ -20,10 +22,7 @@ public void Execute(IConsoleShell shell, string argStr, string[] args) return; } - if (player.Status != SessionStatus.InGame) - return; - - if (player.AttachedEntity is not {} playerEntity) + if (player.AttachedEntity is not {}) { shell.WriteError("You don't have an entity!"); return; @@ -33,11 +32,7 @@ public void Execute(IConsoleShell shell, string argStr, string[] args) return; var message = string.Join(" ", args).Trim(); - if (string.IsNullOrEmpty(message)) - return; - - IoCManager.Resolve().GetEntitySystem() - .TrySendInGameICMessage(playerEntity, message, InGameICChatType.Speak, ChatTransmitRange.Normal, false, shell, player); + _chat.RequestChat(player, message, ChatSelectChannel.Local); } } } diff --git a/Content.Server/Chat/Commands/WhisperCommand.cs b/Content.Server/Chat/Commands/WhisperCommand.cs index 13effa34464ba8..da18e4c51686ea 100644 --- a/Content.Server/Chat/Commands/WhisperCommand.cs +++ b/Content.Server/Chat/Commands/WhisperCommand.cs @@ -1,13 +1,15 @@ -using Content.Server.Chat.Systems; +using Content.Server.Chat.Managers; using Content.Shared.Administration; +using Content.Shared.Chat; using Robust.Shared.Console; -using Robust.Shared.Enums; namespace Content.Server.Chat.Commands { [AnyCommand] internal sealed class WhisperCommand : IConsoleCommand { + [Dependency] private readonly IChatManager _chat = default!; + public string Command => "whisper"; public string Description => "Send chat messages to the local channel as a whisper"; public string Help => "whisper "; @@ -20,10 +22,7 @@ public void Execute(IConsoleShell shell, string argStr, string[] args) return; } - if (player.Status != SessionStatus.InGame) - return; - - if (player.AttachedEntity is not {} playerEntity) + if (player.AttachedEntity is not {}) { shell.WriteError("You don't have an entity!"); return; @@ -33,11 +32,7 @@ public void Execute(IConsoleShell shell, string argStr, string[] args) return; var message = string.Join(" ", args).Trim(); - if (string.IsNullOrEmpty(message)) - return; - - IoCManager.Resolve().GetEntitySystem() - .TrySendInGameICMessage(playerEntity, message, InGameICChatType.Whisper, ChatTransmitRange.Normal, false, shell, player); + _chat.RequestChat(player, message, ChatSelectChannel.Whisper); } } } diff --git a/Content.Server/Chat/Managers/ChatManager.RateLimit.cs b/Content.Server/Chat/Managers/ChatManager.RateLimit.cs index 45e7d2e20d0f26..f2d743586c59e5 100644 --- a/Content.Server/Chat/Managers/ChatManager.RateLimit.cs +++ b/Content.Server/Chat/Managers/ChatManager.RateLimit.cs @@ -25,13 +25,13 @@ private void RegisterRateLimits() private void RateLimitPlayerLimited(ICommonSession player) { - DispatchServerMessage(player, Loc.GetString("chat-manager-rate-limited"), suppressLog: true); + DispatchServerMessage(player, _loc.GetString("chat-manager-rate-limited"), suppressLog: true); } private void RateLimitAlertAdmins(ICommonSession player) { if (_configurationManager.GetCVar(CCVars.ChatRateLimitAnnounceAdmins)) - SendAdminAlert(Loc.GetString("chat-manager-rate-limit-admin-announcement", ("player", player.Name))); + SendAdminAlert(_loc.GetString("chat-manager-rate-limit-admin-announcement", ("player", player.Name))); } public RateLimitStatus HandleRateLimit(ICommonSession player) diff --git a/Content.Server/Chat/Managers/ChatManager.cs b/Content.Server/Chat/Managers/ChatManager.cs index 02f718daef0e6e..95f975c2ecadbc 100644 --- a/Content.Server/Chat/Managers/ChatManager.cs +++ b/Content.Server/Chat/Managers/ChatManager.cs @@ -4,6 +4,7 @@ using Content.Server.Administration.Logs; using Content.Server.Administration.Managers; using Content.Server.Administration.Systems; +using Content.Server.Chat.Systems; using Content.Server.MoMMI; using Content.Server.Players.RateLimiting; using Content.Server.Preferences.Managers; @@ -11,8 +12,10 @@ using Content.Shared.CCVar; using Content.Shared.Chat; using Content.Shared.Database; +using Content.Shared.Ghost; using Content.Shared.Mind; using Robust.Shared.Configuration; +using Robust.Shared.Enums; using Robust.Shared.Network; using Robust.Shared.Player; using Robust.Shared.Replays; @@ -43,6 +46,9 @@ internal sealed partial class ChatManager : IChatManager [Dependency] private readonly INetConfigurationManager _netConfigManager = default!; [Dependency] private readonly IEntityManager _entityManager = default!; [Dependency] private readonly PlayerRateLimitManager _rateLimitManager = default!; + [Dependency] private readonly ISharedPlayerManager _playerMan = default!; + [Dependency] private readonly ILogManager _logMan = default!; + [Dependency] private readonly ILocalizationManager _loc = default!; /// /// The maximum length a player-sent message can be sent @@ -53,11 +59,14 @@ internal sealed partial class ChatManager : IChatManager private bool _adminOocEnabled = true; private readonly Dictionary _players = new(); + private ISawmill _log = default!; public void Initialize() { _netManager.RegisterNetMessage(); _netManager.RegisterNetMessage(); + _netManager.RegisterNetMessage(OnRequestChat); + _log = _logMan.GetSawmill("chat"); _configurationManager.OnValueChanged(CCVars.OocEnabled, OnOocEnabledChanged, true); _configurationManager.OnValueChanged(CCVars.AdminOocEnabled, OnAdminOocEnabledChanged, true); @@ -65,12 +74,75 @@ public void Initialize() RegisterRateLimits(); } + private void OnRequestChat(RequestChatMessage msg) + { + if (_playerMan.TryGetSessionByChannel(msg.MsgChannel, out var session)) + RequestChat(session, msg.Text, msg.Channel); + } + + /// + public void RequestChat(ICommonSession session, string text, ChatSelectChannel channel) + { + if (string.IsNullOrWhiteSpace(text)) + return; + + _log.Info($"{session.Name} - {channel}: {text}"); + + // entity independent channels + switch (channel) + { + case ChatSelectChannel.OOC: + TrySendOOCMessage(session, text, OOCChatType.OOC); + return; + + case ChatSelectChannel.Admin: + if (_adminManager.HasAdminFlag(session, AdminFlags.Adminchat)) + TrySendOOCMessage(session, text, OOCChatType.Admin); + return; + } + + if (session.Status != SessionStatus.InGame || session.AttachedEntity is not { } ent || + !_entityManager.EntityExists(ent)) + { + var msg = _loc.GetString("chat-manager-no-entity", ("channel", channel)); + DispatchServerMessage(session, msg, true); + return; + } + + var sys = _entityManager.SystemOrNull(); + switch (channel) + { + case ChatSelectChannel.LOOC: + sys?.TrySendInGameOOCMessage(ent, text, InGameOOCChatType.Looc, false, null, session); + break; + case ChatSelectChannel.Dead: + if (_entityManager.HasComponent(ent) || _adminManager.HasAdminFlag(session, AdminFlags.Admin)) + sys?.TrySendInGameOOCMessage(ent, text, InGameOOCChatType.Dead, false, null, session); + else + DispatchServerMessage(session, _loc.GetString("chat-manager-not-dead"), true); + break; + case ChatSelectChannel.Emotes: + sys?.TrySendInGameICMessage(ent, text, InGameICChatType.Emote, ChatTransmitRange.Normal, false, null, session); + break; + case ChatSelectChannel.Whisper: + sys?.TrySendInGameICMessage(ent, text, InGameICChatType.Whisper, ChatTransmitRange.Normal, false, null, session); + break; + // TODO separate radio and say + case ChatSelectChannel.Radio: + case ChatSelectChannel.Local: + sys?.TrySendInGameICMessage(ent, text, InGameICChatType.Speak, ChatTransmitRange.Normal, false, null, session); + break; + default: + throw new ArgumentOutOfRangeException(nameof(channel), channel, null); + } + } + private void OnOocEnabledChanged(bool val) { if (_oocEnabled == val) return; _oocEnabled = val; - DispatchServerAnnouncement(Loc.GetString(val ? "chat-manager-ooc-chat-enabled-message" : "chat-manager-ooc-chat-disabled-message")); + DispatchServerAnnouncement(_loc.GetString(val ? "chat-manager-ooc-chat-enabled-message" : "chat-manager-ooc-chat-disabled-message")); } private void OnAdminOocEnabledChanged(bool val) @@ -78,7 +150,7 @@ private void OnAdminOocEnabledChanged(bool val) if (_adminOocEnabled == val) return; _adminOocEnabled = val; - DispatchServerAnnouncement(Loc.GetString(val ? "chat-manager-admin-ooc-chat-enabled-message" : "chat-manager-admin-ooc-chat-disabled-message")); + DispatchServerAnnouncement(_loc.GetString(val ? "chat-manager-admin-ooc-chat-enabled-message" : "chat-manager-admin-ooc-chat-disabled-message")); } public void DeleteMessagesBy(NetUserId uid) @@ -107,7 +179,7 @@ public void DeleteMessagesBy(NetUserId uid) public void DispatchServerAnnouncement(string message, Color? colorOverride = null) { - var wrappedMessage = Loc.GetString("chat-manager-server-wrap-message", ("message", FormattedMessage.EscapeText(message))); + var wrappedMessage = _loc.GetString("chat-manager-server-wrap-message", ("message", FormattedMessage.EscapeText(message))); ChatMessageToAll(ChatChannel.Server, message, wrappedMessage, EntityUid.Invalid, hideChat: false, recordReplay: true, colorOverride: colorOverride); Logger.InfoS("SERVER", message); @@ -116,7 +188,7 @@ public void DispatchServerAnnouncement(string message, Color? colorOverride = nu public void DispatchServerMessage(ICommonSession player, string message, bool suppressLog = false) { - var wrappedMessage = Loc.GetString("chat-manager-server-wrap-message", ("message", FormattedMessage.EscapeText(message))); + var wrappedMessage = _loc.GetString("chat-manager-server-wrap-message", ("message", FormattedMessage.EscapeText(message))); ChatMessageToOne(ChatChannel.Server, message, wrappedMessage, default, false, player.Channel); if (!suppressLog) @@ -141,8 +213,8 @@ public void SendAdminAnnouncement(string message, AdminFlags? flagBlacklist, Adm }).Select(p => p.Channel); - var wrappedMessage = Loc.GetString("chat-manager-send-admin-announcement-wrap-message", - ("adminChannelName", Loc.GetString("chat-manager-admin-channel-name")), ("message", FormattedMessage.EscapeText(message))); + var wrappedMessage = _loc.GetString("chat-manager-send-admin-announcement-wrap-message", + ("adminChannelName", _loc.GetString("chat-manager-admin-channel-name")), ("message", FormattedMessage.EscapeText(message))); ChatMessageToMany(ChatChannel.Admin, message, wrappedMessage, default, false, true, clients); _adminLogger.Add(LogType.Chat, LogImpact.Low, $"Admin announcement: {message}"); @@ -150,8 +222,8 @@ public void SendAdminAnnouncement(string message, AdminFlags? flagBlacklist, Adm public void SendAdminAnnouncementMessage(ICommonSession player, string message, bool suppressLog = true) { - var wrappedMessage = Loc.GetString("chat-manager-send-admin-announcement-wrap-message", - ("adminChannelName", Loc.GetString("chat-manager-admin-channel-name")), + var wrappedMessage = _loc.GetString("chat-manager-send-admin-announcement-wrap-message", + ("adminChannelName", _loc.GetString("chat-manager-admin-channel-name")), ("message", FormattedMessage.EscapeText(message))); ChatMessageToOne(ChatChannel.Admin, message, wrappedMessage, default, false, player.Channel); } @@ -160,8 +232,8 @@ public void SendAdminAlert(string message) { var clients = _adminManager.ActiveAdmins.Select(p => p.Channel); - var wrappedMessage = Loc.GetString("chat-manager-send-admin-announcement-wrap-message", - ("adminChannelName", Loc.GetString("chat-manager-admin-channel-name")), ("message", FormattedMessage.EscapeText(message))); + var wrappedMessage = _loc.GetString("chat-manager-send-admin-announcement-wrap-message", + ("adminChannelName", _loc.GetString("chat-manager-admin-channel-name")), ("message", FormattedMessage.EscapeText(message))); ChatMessageToMany(ChatChannel.AdminAlert, message, wrappedMessage, default, false, true, clients); } @@ -187,7 +259,7 @@ public void SendHookOOC(string sender, string message) { return; } - var wrappedMessage = Loc.GetString("chat-manager-send-hook-ooc-wrap-message", ("senderName", sender), ("message", FormattedMessage.EscapeText(message))); + var wrappedMessage = _loc.GetString("chat-manager-send-hook-ooc-wrap-message", ("senderName", sender), ("message", FormattedMessage.EscapeText(message))); ChatMessageToAll(ChatChannel.OOC, message, wrappedMessage, source: EntityUid.Invalid, hideChat: false, recordReplay: true); _adminLogger.Add(LogType.Chat, LogImpact.Low, $"Hook OOC from {sender}: {message}"); } @@ -208,11 +280,8 @@ public void TrySendOOCMessage(ICommonSession player, string message, OOCChatType return; // Check if message exceeds the character limit - if (message.Length > MaxMessageLength) - { - DispatchServerMessage(player, Loc.GetString("chat-manager-max-message-length-exceeded-message", ("limit", MaxMessageLength))); + if (ExceedsCharacterLimit(player, message)) return; - } switch (type) { @@ -244,7 +313,7 @@ private void SendOOC(ICommonSession player, string message) } Color? colorOverride = null; - var wrappedMessage = Loc.GetString("chat-manager-send-ooc-wrap-message", ("playerName",player.Name), ("message", FormattedMessage.EscapeText(message))); + var wrappedMessage = _loc.GetString("chat-manager-send-ooc-wrap-message", ("playerName",player.Name), ("message", FormattedMessage.EscapeText(message))); if (_adminManager.HasAdminFlag(player, AdminFlags.Admin)) { var prefs = _preferencesManager.GetPreferences(player.UserId); @@ -252,7 +321,7 @@ private void SendOOC(ICommonSession player, string message) } if ( _netConfigManager.GetClientCVar(player.Channel, CCVars.ShowOocPatronColor) && player.Channel.UserData.PatronTier is { } patron && PatronOocColors.TryGetValue(patron, out var patronColor)) { - wrappedMessage = Loc.GetString("chat-manager-send-ooc-patron-wrap-message", ("patronColor", patronColor),("playerName", player.Name), ("message", FormattedMessage.EscapeText(message))); + wrappedMessage = _loc.GetString("chat-manager-send-ooc-patron-wrap-message", ("patronColor", patronColor),("playerName", player.Name), ("message", FormattedMessage.EscapeText(message))); } //TODO: player.Name color, this will need to change the structure of the MsgChatMessage @@ -270,8 +339,8 @@ private void SendAdminChat(ICommonSession player, string message) } var clients = _adminManager.ActiveAdmins.Select(p => p.Channel); - var wrappedMessage = Loc.GetString("chat-manager-send-admin-chat-wrap-message", - ("adminChannelName", Loc.GetString("chat-manager-admin-channel-name")), + var wrappedMessage = _loc.GetString("chat-manager-send-admin-chat-wrap-message", + ("adminChannelName", _loc.GetString("chat-manager-admin-channel-name")), ("playerName", player.Name), ("message", FormattedMessage.EscapeText(message))); foreach (var client in clients) @@ -370,25 +439,22 @@ public void ChatMessageToAll(ChatChannel channel, string message, string wrapped } } - public bool MessageCharacterLimit(ICommonSession? player, string message) + /// + /// Checks if a message exceeds the character limit. If ti does, it will notify the player. + /// + public bool ExceedsCharacterLimit(ICommonSession? player, string message) { - var isOverLength = false; - // Non-players don't need to be checked. if (player == null) return false; // Check if message exceeds the character limit if the sender is a player - if (message.Length > MaxMessageLength) - { - var feedback = Loc.GetString("chat-manager-max-message-length-exceeded-message", ("limit", MaxMessageLength)); - - DispatchServerMessage(player, feedback); - - isOverLength = true; - } + if (message.Length <= MaxMessageLength) + return false; - return isOverLength; + var feedback = _loc.GetString("chat-manager-max-message-length-exceeded-message", ("limit", MaxMessageLength)); + DispatchServerMessage(player, feedback); + return true; } #endregion diff --git a/Content.Server/Chat/Managers/IChatManager.cs b/Content.Server/Chat/Managers/IChatManager.cs index 76fa91d847411e..27a4027a9a54ba 100644 --- a/Content.Server/Chat/Managers/IChatManager.cs +++ b/Content.Server/Chat/Managers/IChatManager.cs @@ -1,5 +1,4 @@ using System.Diagnostics.CodeAnalysis; -using Content.Server.Players; using Content.Server.Players.RateLimiting; using Content.Shared.Administration; using Content.Shared.Chat; @@ -39,13 +38,18 @@ void ChatMessageToMany(ChatChannel channel, string message, string wrappedMessag void ChatMessageToAll(ChatChannel channel, string message, string wrappedMessage, EntityUid source, bool hideChat, bool recordReplay, Color? colorOverride = null, string? audioPath = null, float audioVolume = 0, NetUserId? author = null); - bool MessageCharacterLimit(ICommonSession player, string message); + bool ExceedsCharacterLimit(ICommonSession player, string message); void DeleteMessagesBy(NetUserId uid); [return: NotNullIfNotNull(nameof(author))] ChatUser? EnsurePlayer(NetUserId? author); + /// + /// This method is used by clients via commands and networked messages to attempt to send a chat message. + /// + void RequestChat(ICommonSession session, string text, ChatSelectChannel channel); + /// /// Called when a player sends a chat message to handle rate limits. /// Will update counts and do necessary actions if breached. diff --git a/Content.Server/Chat/Systems/ChatSystem.cs b/Content.Server/Chat/Systems/ChatSystem.cs index bcbc6a6144c92c..0cc33ed5710c87 100644 --- a/Content.Server/Chat/Systems/ChatSystem.cs +++ b/Content.Server/Chat/Systems/ChatSystem.cs @@ -192,7 +192,7 @@ public void TrySendInGameICMessage( return; } - if (!CanSendInGame(message, shell, player)) + if (!CanSendInGame(message, player, desiredType)) return; ignoreActionBlocker = CheckIgnoreSpeechBlocker(source, ignoreActionBlocker); @@ -269,7 +269,7 @@ public void TrySendInGameOOCMessage( ICommonSession? player = null ) { - if (!CanSendInGame(message, shell, player)) + if (!CanSendInGame(message, player, type)) return; if (player != null && _chatManager.HandleRateLimit(player) != RateLimitStatus.Allowed) @@ -722,7 +722,7 @@ private void SendInVoiceRange(ChatChannel channel, string message, string wrappe /// /// Returns true if the given player is 'allowed' to send the given message, false otherwise. /// - private bool CanSendInGame(string message, IConsoleShell? shell = null, ICommonSession? player = null) + private bool CanSendInGame(string message, ICommonSession? player, Enum channel) { // Non-players don't have to worry about these restrictions. if (player == null) @@ -730,19 +730,12 @@ private bool CanSendInGame(string message, IConsoleShell? shell = null, ICommonS var mindContainerComponent = player.ContentData()?.Mind; - if (mindContainerComponent == null) - { - shell?.WriteError("You don't have a mind!"); - return false; - } - - if (player.AttachedEntity is not { Valid: true } _) - { - shell?.WriteError("You don't have an entity!"); - return false; - } + if (mindContainerComponent != null && Exists(player.AttachedEntity)) + return !_chatManager.ExceedsCharacterLimit(player, message); - return !_chatManager.MessageCharacterLimit(player, message); + var msg = Loc.GetString("chat-manager-cannot-send", ("channel", channel)); + _chatManager.DispatchServerMessage(player, msg, true); + return false; } // ReSharper disable once InconsistentNaming diff --git a/Content.Server/Motd/SetMOTDCommand.cs b/Content.Server/Motd/SetMOTDCommand.cs index 3a7a5e71c6a8c7..d243880463ce80 100644 --- a/Content.Server/Motd/SetMOTDCommand.cs +++ b/Content.Server/Motd/SetMOTDCommand.cs @@ -28,7 +28,7 @@ public override void Execute(IConsoleShell shell, string argStr, string[] args) if (args.Length > 0) { motd = string.Join(" ", args).Trim(); - if (player != null && _chatManager.MessageCharacterLimit(player, motd)) + if (player != null && _chatManager.ExceedsCharacterLimit(player, motd)) return; // check function prints its own error response } diff --git a/Content.Shared/Chat/MsgRequestChat.cs b/Content.Shared/Chat/MsgRequestChat.cs new file mode 100644 index 00000000000000..d7bfd9ccf22a89 --- /dev/null +++ b/Content.Shared/Chat/MsgRequestChat.cs @@ -0,0 +1,28 @@ +using Lidgren.Network; +using Robust.Shared.Network; +using Robust.Shared.Serialization; + +namespace Content.Shared.Chat; + +/// +/// Chat message sent from client->server +/// +public sealed class RequestChatMessage : NetMessage +{ + public override MsgGroups MsgGroup => MsgGroups.String; + + public string Text = default!; + public ChatSelectChannel Channel; + + public override void ReadFromBuffer(NetIncomingMessage buffer, IRobustSerializer serializer) + { + Text = buffer.ReadString(); + Channel = (ChatSelectChannel)buffer.ReadUInt16(); + } + + public override void WriteToBuffer(NetOutgoingMessage buffer, IRobustSerializer serializer) + { + buffer.Write(Text); + buffer.Write((ushort)Channel); + } +} diff --git a/Resources/Locale/en-US/chat/managers/chat-manager.ftl b/Resources/Locale/en-US/chat/managers/chat-manager.ftl index 59b927742bb2b9..7171bf48798a2f 100644 --- a/Resources/Locale/en-US/chat/managers/chat-manager.ftl +++ b/Resources/Locale/en-US/chat/managers/chat-manager.ftl @@ -1,6 +1,5 @@ ### UI -chat-manager-max-message-length = Your message exceeds {$maxMessageLength} character limit chat-manager-ooc-chat-enabled-message = OOC chat has been enabled. chat-manager-ooc-chat-disabled-message = OOC chat has been disabled. chat-manager-looc-chat-enabled-message = LOOC chat has been enabled. @@ -13,6 +12,9 @@ chat-manager-admin-ooc-chat-enabled-message = Admin OOC chat has been enabled. chat-manager-admin-ooc-chat-disabled-message = Admin OOC chat has been disabled. chat-manager-max-message-length-exceeded-message = Your message exceeded {$limit} character limit +chat-manager-no-entity = You need to be in-game to send a {$channel} chat message. +chat-manager-not-dead = You need to be dead or an admin to send dead-chat messages. +chat-manager-cannot-send = You cannot send a {$channel} chat message. chat-manager-no-headset-on-message = You don't have a headset on! chat-manager-no-radio-key = No radio key specified! chat-manager-no-such-channel = There is no channel with key '{$key}'!