+using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Runtime.InteropServices;
using Content.Server.Administration.Logs;
private bool _oocEnabled = true;
private bool _adminOocEnabled = true;
- public Dictionary<ICommonSession, int> SenderKeys { get; } = new();
- public Dictionary<ICommonSession, HashSet<NetEntity>> SenderEntities { get; } = new();
+ private readonly Dictionary<NetUserId, ChatUser> _players = new();
public void Initialize()
{
public void DeleteMessagesBy(ICommonSession player)
{
- var key = SenderKeys.GetValueOrDefault(player);
- var entities = SenderEntities.GetValueOrDefault(player) ?? new HashSet<NetEntity>();
- var msg = new MsgDeleteChatMessagesBy { Key = key, Entities = entities };
+ if (!_players.TryGetValue(player.UserId, out var user))
+ return;
+ var msg = new MsgDeleteChatMessagesBy { Key = user.Key, Entities = user.Entities };
_netManager.ServerSendToAll(msg);
}
+ [return: NotNullIfNotNull(nameof(author))]
+ public ChatUser? EnsurePlayer(NetUserId? author)
+ {
+ if (author == null)
+ return null;
+
+ ref var user = ref CollectionsMarshal.GetValueRefOrAddDefault(_players, author.Value, out var exists);
+ if (!exists || user == null)
+ user = new ChatUser(_players.Count);
+
+ return user;
+ }
+
#region Server Announcements
public void DispatchServerAnnouncement(string message, Color? colorOverride = null)
wrappedMessage = Loc.GetString("chat-manager-send-ooc-patron-wrap-message", ("patronColor", patronColor),("playerName", player.Name), ("message", FormattedMessage.EscapeText(message)));
}
- ref var key = ref CollectionsMarshal.GetValueRefOrAddDefault(SenderKeys, player, out var exists);
- if (!exists)
- key = SenderKeys.Count;
-
//TODO: player.Name color, this will need to change the structure of the MsgChatMessage
- ChatMessageToAll(ChatChannel.OOC, message, wrappedMessage, EntityUid.Invalid, hideChat: false, recordReplay: true, colorOverride: colorOverride, senderKey: key);
+ ChatMessageToAll(ChatChannel.OOC, message, wrappedMessage, EntityUid.Invalid, hideChat: false, recordReplay: true, colorOverride: colorOverride, author: player.UserId);
_mommiLink.SendOOCMessage(player.Name, message);
_adminLogger.Add(LogType.Chat, LogImpact.Low, $"OOC from {player:Player}: {message}");
}
("adminChannelName", Loc.GetString("chat-manager-admin-channel-name")),
("playerName", player.Name), ("message", FormattedMessage.EscapeText(message)));
- ref var key = ref CollectionsMarshal.GetValueRefOrAddDefault(SenderKeys, player, out var exists);
- if (!exists)
- key = SenderKeys.Count;
-
foreach (var client in clients)
{
var isSource = client != player.ConnectedClient;
false,
client,
audioPath: isSource ? _netConfigManager.GetClientCVar(client, CCVars.AdminChatSoundPath) : default,
- audioVolume: isSource ? _netConfigManager.GetClientCVar(client, CCVars.AdminChatSoundVolume) : default, senderKey: key);
+ audioVolume: isSource ? _netConfigManager.GetClientCVar(client, CCVars.AdminChatSoundVolume) : default,
+ author: player.UserId);
}
_adminLogger.Add(LogType.Chat, $"Admin chat from {player:Player}: {message}");
#region Utility
- public void ChatMessageToOne(ChatChannel channel, string message, string wrappedMessage, EntityUid source, bool hideChat, INetChannel client, Color? colorOverride = null, bool recordReplay = false, string? audioPath = null, float audioVolume = 0, int? senderKey = null)
+ public void ChatMessageToOne(ChatChannel channel, string message, string wrappedMessage, EntityUid source, bool hideChat, INetChannel client, Color? colorOverride = null, bool recordReplay = false, string? audioPath = null, float audioVolume = 0, NetUserId? author = null)
{
- var msg = new ChatMessage(channel, message, wrappedMessage, _entityManager.GetNetEntity(source), senderKey, hideChat, colorOverride, audioPath, audioVolume);
+ var user = author == null ? null : EnsurePlayer(author);
+ var netSource = _entityManager.GetNetEntity(source);
+ user?.AddEntity(netSource);
+
+ var msg = new ChatMessage(channel, message, wrappedMessage, netSource, user?.Key, hideChat, colorOverride, audioPath, audioVolume);
_netManager.ServerSendMessage(new MsgChatMessage() { Message = msg }, client);
if (!recordReplay)
}
}
- public void ChatMessageToMany(ChatChannel channel, string message, string wrappedMessage, EntityUid source, bool hideChat, bool recordReplay, IEnumerable<INetChannel> clients, Color? colorOverride = null, string? audioPath = null, float audioVolume = 0)
- => ChatMessageToMany(channel, message, wrappedMessage, source, hideChat, recordReplay, clients.ToList(), colorOverride, audioPath, audioVolume);
+ public void ChatMessageToMany(ChatChannel channel, string message, string wrappedMessage, EntityUid source, bool hideChat, bool recordReplay, IEnumerable<INetChannel> clients, Color? colorOverride = null, string? audioPath = null, float audioVolume = 0, NetUserId? author = null)
+ => ChatMessageToMany(channel, message, wrappedMessage, source, hideChat, recordReplay, clients.ToList(), colorOverride, audioPath, audioVolume, author);
- public void ChatMessageToMany(ChatChannel channel, string message, string wrappedMessage, EntityUid source, bool hideChat, bool recordReplay, List<INetChannel> clients, Color? colorOverride = null, string? audioPath = null, float audioVolume = 0)
+ public void ChatMessageToMany(ChatChannel channel, string message, string wrappedMessage, EntityUid source, bool hideChat, bool recordReplay, List<INetChannel> clients, Color? colorOverride = null, string? audioPath = null, float audioVolume = 0, NetUserId? author = null)
{
- var msg = new ChatMessage(channel, message, wrappedMessage, _entityManager.GetNetEntity(source), null, hideChat, colorOverride, audioPath, audioVolume);
+ var user = author == null ? null : EnsurePlayer(author);
+ var netSource = _entityManager.GetNetEntity(source);
+ user?.AddEntity(netSource);
+
+ var msg = new ChatMessage(channel, message, wrappedMessage, netSource, user?.Key, hideChat, colorOverride, audioPath, audioVolume);
_netManager.ServerSendToMany(new MsgChatMessage() { Message = msg }, clients);
if (!recordReplay)
ChatMessageToMany(channel, message, wrappedMessage, source, hideChat, recordReplay, clients, colorOverride, audioPath, audioVolume);
}
- public void ChatMessageToAll(ChatChannel channel, string message, string wrappedMessage, EntityUid source, bool hideChat, bool recordReplay, Color? colorOverride = null, string? audioPath = null, float audioVolume = 0, int? senderKey = null)
+ public 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)
{
- var msg = new ChatMessage(channel, message, wrappedMessage, _entityManager.GetNetEntity(source), senderKey, hideChat, colorOverride, audioPath, audioVolume);
+ var user = author == null ? null : EnsurePlayer(author);
+ var netSource = _entityManager.GetNetEntity(source);
+ user?.AddEntity(netSource);
+
+ var msg = new ChatMessage(channel, message, wrappedMessage, netSource, user?.Key, hideChat, colorOverride, audioPath, audioVolume);
_netManager.ServerSendToAll(new MsgChatMessage() { Message = msg });
if (!recordReplay)
+using System.Diagnostics.CodeAnalysis;
using Content.Shared.Chat;
using Robust.Shared.Network;
using Robust.Shared.Player;
{
public interface IChatManager
{
- /// <summary>
- /// Keys identifying messages sent by a specific player, used when sending
- /// <see cref="MsgChatMessage"/>
- /// </summary>
- Dictionary<ICommonSession, int> SenderKeys { get; }
-
- /// <summary>
- /// Tracks which entities a player was attached to while sending messages.
- /// </summary>
- Dictionary<ICommonSession, HashSet<NetEntity>> SenderEntities { get; }
-
void Initialize();
/// <summary>
void SendAdminAlert(EntityUid player, string message);
void ChatMessageToOne(ChatChannel channel, string message, string wrappedMessage, EntityUid source, bool hideChat,
- INetChannel client, Color? colorOverride = null, bool recordReplay = false, string? audioPath = null, float audioVolume = 0, int? senderKey = null);
+ INetChannel client, Color? colorOverride = null, bool recordReplay = false, string? audioPath = null, float audioVolume = 0, NetUserId? author = null);
void ChatMessageToMany(ChatChannel channel, string message, string wrappedMessage, EntityUid source, bool hideChat, bool recordReplay,
- IEnumerable<INetChannel> clients, Color? colorOverride = null, string? audioPath = null, float audioVolume = 0);
+ IEnumerable<INetChannel> clients, Color? colorOverride = null, string? audioPath = null, float audioVolume = 0, NetUserId? author = null);
void ChatMessageToManyFiltered(Filter filter, ChatChannel channel, string message, string wrappedMessage, EntityUid source, bool hideChat, bool recordReplay, Color? colorOverride, string? audioPath = null, float audioVolume = 0);
- void ChatMessageToAll(ChatChannel channel, string message, string wrappedMessage, EntityUid source, bool hideChat, bool recordReplay, Color? colorOverride = null, string? audioPath = null, float audioVolume = 0, int? senderKey = null);
+ 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);
void DeleteMessagesBy(ICommonSession player);
+
+ [return: NotNullIfNotNull(nameof(author))]
+ ChatUser? EnsurePlayer(NetUserId? author);
}
}
using System.Globalization;
using System.Linq;
using System.Text;
-using Content.Server.Speech.EntitySystems;
-using Content.Server.Speech.Components;
using Content.Server.Administration.Logs;
using Content.Server.Administration.Managers;
using Content.Server.Chat.Managers;
using Content.Server.GameTicking;
+using Content.Server.Speech.Components;
+using Content.Server.Speech.EntitySystems;
using Content.Server.Station.Components;
using Content.Server.Station.Systems;
using Content.Shared.ActionBlocker;
using Content.Shared.Mobs.Systems;
using Content.Shared.Players;
using Content.Shared.Radio;
-using Robust.Server.GameObjects;
using Robust.Server.Player;
using Robust.Shared.Audio;
using Robust.Shared.Configuration;
if (!CanSendInGame(message, shell, player))
return;
+ // this method is a disaster
+ // every second i have to spend working with this code is fucking agony
+ // scientists have to wonder how any of this was merged
+ // coding any game admin feature that involves chat code is pure torture
+ // changing even 10 lines of code feels like waterboarding myself
+ // and i dont feel like vibe checking 50 code paths
+ // so we set this here
+ // todo free me from chat code
if (player != null)
- _chatManager.SenderEntities.GetOrNew(player).Add(GetNetEntity(source));
+ {
+ _chatManager.EnsurePlayer(player.UserId).AddEntity(GetNetEntity(source));
+ }
if (desiredType == InGameICChatType.Speak && message.StartsWith(LocalPrefix))
{
string? nameOverride,
bool hideLog = false,
bool checkEmote = true,
- bool ignoreActionBlocker = false
+ bool ignoreActionBlocker = false,
+ NetUserId? author = null
)
{
if (!_actionBlocker.CanEmote(source) && !ignoreActionBlocker)
if (checkEmote)
TryEmoteChatInput(source, action);
- SendInVoiceRange(ChatChannel.Emotes, action, wrappedMessage, source, range);
+ SendInVoiceRange(ChatChannel.Emotes, action, wrappedMessage, source, range, author);
if (!hideLog)
if (name != Name(source))
_adminLogger.Add(LogType.Chat, LogImpact.Low, $"Emote from {ToPrettyString(source):user} as {name}: {action}");
("entityName", name),
("message", FormattedMessage.EscapeText(message)));
- _chatManager.SenderEntities.GetOrNew(player).Add(GetNetEntity(source));
-
- SendInVoiceRange(ChatChannel.LOOC, message, wrappedMessage, source, hideChat ? ChatTransmitRange.HideChat : ChatTransmitRange.Normal);
+ SendInVoiceRange(ChatChannel.LOOC, message, wrappedMessage, source, hideChat ? ChatTransmitRange.HideChat : ChatTransmitRange.Normal, player.UserId);
_adminLogger.Add(LogType.Chat, LogImpact.Low, $"LOOC from {player:Player}: {message}");
}
_adminLogger.Add(LogType.Chat, LogImpact.Low, $"Dead chat from {player:Player}: {message}");
}
- _chatManager.SenderEntities.GetOrNew(player).Add(GetNetEntity(source));
-
- _chatManager.ChatMessageToMany(ChatChannel.Dead, message, wrappedMessage, source, hideChat, true, clients.ToList());
+ _chatManager.ChatMessageToMany(ChatChannel.Dead, message, wrappedMessage, source, hideChat, true, clients.ToList(), author: player.UserId);
}
#endregion
/// <summary>
/// Sends a chat message to the given players in range of the source entity.
/// </summary>
- private void SendInVoiceRange(ChatChannel channel, string message, string wrappedMessage, EntityUid source, ChatTransmitRange range)
+ private void SendInVoiceRange(ChatChannel channel, string message, string wrappedMessage, EntityUid source, ChatTransmitRange range, NetUserId? author = null)
{
foreach (var (session, data) in GetRecipients(source, VoiceRange))
{
if (entRange == MessageRangeCheckResult.Disallowed)
continue;
var entHideChat = entRange == MessageRangeCheckResult.HideChat;
- _chatManager.ChatMessageToOne(channel, message, wrappedMessage, source, entHideChat, session.ConnectedClient);
+ _chatManager.ChatMessageToOne(channel, message, wrappedMessage, source, entHideChat, session.ConnectedClient, author: author);
}
_replay.RecordServerMessage(new ChatMessage(channel, message, wrappedMessage, GetNetEntity(source), null, MessageRangeHideChatForReplay(range)));