Files
crystall-punk-14/Content.Server/Chat/Managers/ChatManager.cs

680 lines
26 KiB
C#
Raw Normal View History

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
2022-01-19 13:35:31 +11:00
using Content.Server.Administration.Logs;
2021-06-09 22:19:39 +02:00
using Content.Server.Administration.Managers;
using Content.Server.Ghost.Components;
using Content.Server.Headset;
using Content.Server.MoMMI;
using Content.Server.Players;
2021-06-09 22:19:39 +02:00
using Content.Server.Preferences.Managers;
using Content.Server.Radio.EntitySystems;
using Content.Shared.ActionBlocker;
using Content.Shared.Administration;
using Content.Shared.CCVar;
using Content.Shared.Chat;
2022-01-19 13:35:31 +11:00
using Content.Shared.Database;
2021-06-09 22:19:39 +02:00
using Content.Shared.Inventory;
2021-09-26 15:18:45 +02:00
using Content.Shared.Popups;
using Robust.Server.GameObjects;
using Robust.Server.Player;
using Robust.Shared.Audio;
2021-02-16 20:14:32 +01:00
using Robust.Shared.Configuration;
using Robust.Shared.Console;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
2020-04-09 03:08:06 +02:00
using Robust.Shared.Localization;
2021-02-12 10:45:22 +01:00
using Robust.Shared.Log;
using Robust.Shared.Network;
using Robust.Shared.Player;
using Robust.Shared.Players;
using Robust.Shared.Random;
using Robust.Shared.Utility;
2021-06-09 22:19:39 +02:00
using static Content.Server.Chat.Managers.IChatManager;
2021-06-09 22:19:39 +02:00
namespace Content.Server.Chat.Managers
{
/// <summary>
/// Dispatches chat messages to clients.
/// </summary>
internal sealed class ChatManager : IChatManager
{
2021-02-28 18:51:42 +01:00
private static readonly Dictionary<string, string> PatronOocColors = new()
{
// I had plans for multiple colors and those went nowhere so...
{ "nuclear_operative", "#aa00ff" },
{ "syndicate_agent", "#aa00ff" },
{ "revolutionary", "#aa00ff" }
};
[Dependency] private readonly IChatSanitizationManager _sanitizer = default!;
[Dependency] private readonly IEntityManager _entManager = default!;
2021-02-16 20:14:32 +01:00
[Dependency] private readonly IServerNetManager _netManager = default!;
[Dependency] private readonly IPlayerManager _playerManager = default!;
[Dependency] private readonly IMoMMILink _mommiLink = default!;
[Dependency] private readonly IAdminManager _adminManager = default!;
[Dependency] private readonly IServerPreferencesManager _preferencesManager = default!;
[Dependency] private readonly IConfigurationManager _configurationManager = default!;
[Dependency] private readonly IRobustRandom _random = default!;
2021-02-16 20:14:32 +01:00
2022-01-19 13:35:31 +11:00
private AdminLogSystem _logs = default!;
/// <summary>
/// The maximum length a player-sent message can be sent
/// </summary>
public int MaxMessageLength => _configurationManager.GetCVar(CCVars.ChatMaxMessageLength);
private const int VoiceRange = 7; // how far voice goes in world units
private const int WhisperRange = 2; // how far whisper goes in world units
//TODO: make prio based?
private readonly List<TransformChat> _chatTransformHandlers = new();
2021-02-16 20:14:32 +01:00
private bool _oocEnabled = true;
private bool _adminOocEnabled = true;
2022-01-11 16:29:55 +03:00
private bool _loocEnabled = true;
private bool _adminLoocEnabled = true;
public void Initialize()
{
2022-01-19 13:35:31 +11:00
_logs = EntitySystem.Get<AdminLogSystem>();
_netManager.RegisterNetMessage<MsgChatMessage>();
2021-02-16 20:14:32 +01:00
_configurationManager.OnValueChanged(CCVars.OocEnabled, OnOocEnabledChanged, true);
2022-01-11 16:29:55 +03:00
_configurationManager.OnValueChanged(CCVars.LoocEnabled, OnLoocEnabledChanged, true);
2021-02-16 20:14:32 +01:00
_configurationManager.OnValueChanged(CCVars.AdminOocEnabled, OnAdminOocEnabledChanged, true);
}
private void OnOocEnabledChanged(bool val)
{
2022-01-19 13:35:31 +11:00
if (_oocEnabled == val) return;
2021-02-16 20:14:32 +01:00
_oocEnabled = val;
DispatchServerAnnouncement(Loc.GetString(val ? "chat-manager-ooc-chat-enabled-message" : "chat-manager-ooc-chat-disabled-message"));
2021-02-16 20:14:32 +01:00
}
2022-01-11 16:29:55 +03:00
private void OnLoocEnabledChanged(bool val)
{
2022-01-19 13:35:31 +11:00
if (_loocEnabled == val) return;
2022-01-11 16:29:55 +03:00
_loocEnabled = val;
DispatchServerAnnouncement(Loc.GetString(val ? "chat-manager-looc-chat-enabled-message" : "chat-manager-looc-chat-disabled-message"));
}
2021-02-16 20:14:32 +01:00
private void OnAdminOocEnabledChanged(bool val)
{
2022-01-19 13:35:31 +11:00
if (_adminOocEnabled == val) return;
2021-02-16 20:14:32 +01:00
_adminOocEnabled = val;
DispatchServerAnnouncement(Loc.GetString(val ? "chat-manager-admin-ooc-chat-enabled-message" : "chat-manager-admin-ooc-chat-disabled-message"));
}
public void DispatchServerAnnouncement(string message, Color? colorOverride = null)
{
var messageWrap = Loc.GetString("chat-manager-server-wrap-message");
NetMessageToAll(ChatChannel.Server, message, messageWrap, colorOverride);
2021-02-12 10:45:22 +01:00
Logger.InfoS("SERVER", message);
2022-01-19 13:35:31 +11:00
_logs.Add(LogType.Chat, LogImpact.Low, $"Server announcement: {message}");
}
public void DispatchStationAnnouncement(string message, string sender = "Central Command", bool playDefaultSound = true, Color? colorOverride = null)
{
var messageWrap = Loc.GetString("chat-manager-sender-announcement-wrap-message", ("sender", sender));
NetMessageToAll(ChatChannel.Radio, message, messageWrap, colorOverride);
if (playDefaultSound)
{
SoundSystem.Play(Filter.Broadcast(), "/Audio/Announcements/announce.ogg", AudioParams.Default.WithVolume(-2f));
}
2022-01-19 13:35:31 +11:00
_logs.Add(LogType.Chat, LogImpact.Low, $"Station Announcement from {sender}: {message}");
}
public void DispatchServerMessage(IPlayerSession player, string message)
{
var messageWrap = Loc.GetString("chat-manager-server-wrap-message");
var msg = _netManager.CreateNetMessage<MsgChatMessage>();
msg.Channel = ChatChannel.Server;
msg.Message = message;
msg.MessageWrap = messageWrap;
_netManager.ServerSendMessage(msg, player.ConnectedClient);
2022-01-19 13:35:31 +11:00
_logs.Add(LogType.Chat, LogImpact.Low, $"Server message from {player:Player}: {message}");
}
public void TrySpeak(EntityUid source, string message, bool isWhisper = false, IConsoleShell? shell = null, IPlayerSession? player = null)
{
// Listen it avoids the 30 lines being copy-paste and means only 1 source needs updating if something changes.
if (_entManager.HasComponent<GhostComponent>(source))
{
if (player == null) return;
SendDeadChat(player, message);
}
else
{
var mindComponent = player?.ContentData()?.Mind;
if (mindComponent == null)
{
shell?.WriteError("You don't have a mind!");
return;
}
if (mindComponent.OwnedEntity is not {Valid: true} owned)
{
shell?.WriteError("You don't have an entity!");
return;
}
var isEmote = _sanitizer.TrySanitizeOutSmilies(message, owned, out var sanitized, out var emoteStr);
if (sanitized.Length != 0)
SendEntityChatType(owned, sanitized, isWhisper);
if (isEmote)
EntityMe(owned, emoteStr!);
}
}
public void TryEmote(EntityUid source, string message, IConsoleShell? shell = null, IPlayerSession? player = null)
{
var mindComponent = player?.ContentData()?.Mind;
if (mindComponent == null)
{
shell?.WriteError("You don't have a mind!");
return;
}
if (mindComponent.OwnedEntity is not {Valid: true} owned)
{
shell?.WriteError("You don't have an entity!");
return;
}
var isEmote = _sanitizer.TrySanitizeOutSmilies(message, mindComponent.OwnedEntity.Value, out var sanitized, out var emoteStr);
if (sanitized.Length != 0)
EntityMe(mindComponent.OwnedEntity.Value, sanitized);
if (isEmote)
EntityMe(mindComponent.OwnedEntity.Value, emoteStr!);
}
2021-12-05 18:09:01 +01:00
public void EntitySay(EntityUid source, string message, bool hideChat=false)
{
2021-12-03 15:53:09 +01:00
if (!EntitySystem.Get<ActionBlockerSystem>().CanSpeak(source))
{
return;
}
if (MessageCharacterLimit(source, message))
Headsets (#2023) * add headset component * add basic headset logic * fix formatting in listening component, add dependency to headset * test function for headset * implement headset as listener * ANNIHILATES ListeningComponent, refactor of radio/listener sys * basic headset functionality * rename RadioComponent to HandheldRadioComponent * change channel to list of channels * basic headset implementation complete * message now always excludes ';' * add radio color; state channel freq. and source name * undocumented game breaking bug commit (DO NOT RESEARCH) actually just changes frequency from 1457 (what signalers are set to by default) to 1459, the actual frequency for common * Add more sprites * Reorganizes * Added job headsets * Adds headset as an ignored component * Jobs now spawn with headsets * remove system.tracing * Catchup commits * Add headset property serialization * Turn GetChannels into a property * ListenRange property and serializatioon * Adjust interfaces * Address reviews * Cleanup * Address reviews * Update rsi * Fix licenses and copyright * Fix missing textures * Merge fixes * Move headset textures from objects/devices to clothing/ears * Fix rsi state names and add equipped states * Fix headsets not working * Add missing brackets to channel number in chat * heck * Fix broken rsi * Fix radio id and names * Put quotes around headset messages * Fix method names * Fix handheld radios * Fix capitalization when using radio channels and trim * Remove unnecessary dependency * Indent that * Separate this part * Goodbye icons * Implement IActivate in HandheldRadioComponent * Add examine tooltip to radios and headsets * Rename IListen methods Co-authored-by: Bright <nsmoak10@yahoo.com> Co-authored-by: Swept <jamesurquhartwebb@gmail.com> Co-authored-by: Bright0 <55061890+Bright0@users.noreply.github.com>
2020-10-07 14:02:12 +02:00
{
return;
}
message = message.Trim();
message = SanitizeMessageCapital(source, message);
foreach (var handler in _chatTransformHandlers)
{
//TODO: rather return a bool and use a out var?
2021-12-03 15:53:09 +01:00
message = handler(source, message);
}
var listeners = EntitySystem.Get<ListeningSystem>();
listeners.PingListeners(source, message);
message = FormattedMessage.EscapeText(message);
2021-12-05 18:09:01 +01:00
var sessions = new List<ICommonSession>();
ClientDistanceToList(source, VoiceRange, sessions);
var messageWrap = Loc.GetString("chat-manager-entity-say-wrap-message",("entityName", _entManager.GetComponent<MetaDataComponent>(source).EntityName));
foreach (var session in sessions)
{
NetMessageToOne(ChatChannel.Local, message, messageWrap, source, hideChat, session.ConnectedClient);
}
2022-01-19 13:35:31 +11:00
_logs.Add(LogType.Chat, LogImpact.Low, $"Say from {_entManager.ToPrettyString(source):user}: {message}");
}
public void EntityWhisper(EntityUid source, string message, bool hideChat=false)
{
if (!EntitySystem.Get<ActionBlockerSystem>().CanSpeak(source))
Headsets (#2023) * add headset component * add basic headset logic * fix formatting in listening component, add dependency to headset * test function for headset * implement headset as listener * ANNIHILATES ListeningComponent, refactor of radio/listener sys * basic headset functionality * rename RadioComponent to HandheldRadioComponent * change channel to list of channels * basic headset implementation complete * message now always excludes ';' * add radio color; state channel freq. and source name * undocumented game breaking bug commit (DO NOT RESEARCH) actually just changes frequency from 1457 (what signalers are set to by default) to 1459, the actual frequency for common * Add more sprites * Reorganizes * Added job headsets * Adds headset as an ignored component * Jobs now spawn with headsets * remove system.tracing * Catchup commits * Add headset property serialization * Turn GetChannels into a property * ListenRange property and serializatioon * Adjust interfaces * Address reviews * Cleanup * Address reviews * Update rsi * Fix licenses and copyright * Fix missing textures * Merge fixes * Move headset textures from objects/devices to clothing/ears * Fix rsi state names and add equipped states * Fix headsets not working * Add missing brackets to channel number in chat * heck * Fix broken rsi * Fix radio id and names * Put quotes around headset messages * Fix method names * Fix handheld radios * Fix capitalization when using radio channels and trim * Remove unnecessary dependency * Indent that * Separate this part * Goodbye icons * Implement IActivate in HandheldRadioComponent * Add examine tooltip to radios and headsets * Rename IListen methods Co-authored-by: Bright <nsmoak10@yahoo.com> Co-authored-by: Swept <jamesurquhartwebb@gmail.com> Co-authored-by: Bright0 <55061890+Bright0@users.noreply.github.com>
2020-10-07 14:02:12 +02:00
{
return;
}
get that crap outta here (completely rewrites inventorysystem) (#5807) * some work * equip: done unequip: todo * unequipping done & refactored events * workin * movin * reee namespaces * stun * mobstate * fixes * some work on events * removes serverside itemcomp & misc fixes * work * smol merge fix * ports template to prototype & finishes ui * moves relay & adds containerenumerator * actions & cuffs * my god what is actioncode * more fixes * im loosing my grasp on reality * more fixes * more work * explosions * yes * more work * more fixes * merge master & misc fixed because i forgot to commit before merging master * more fixes * fixes * moar * more work * moar fixes * suffixmap * more work on client * motivation low * no. no containers * mirroring client to server * fixes * move serverinvcomp * serverinventorycomponent is dead * gaming * only strippable & ai left... * only ai and richtext left * fixes ai * fixes * fixes sprite layers * more fixes * resolves optional * yes * stable:tm: * fixes * moar fixes * moar * fix some tests * lmao * no comment * good to merge:tm: * fixes build but for real * adresses some reviews * adresses some more reviews * nullables, yo * fixes lobbyscreen * timid refactor to differentiate actor & target * adresses more reviews * more * my god what a mess * removed the rest of duplicates * removed duplicate slotflags and renamed shoes to feet * removes another unused one * yes * fixes lobby & makes tryunequip return unequipped item * fixes * some funny renames * fixes * misc improvements to attemptevents * fixes * merge fixes Co-authored-by: Paul Ritter <ritter.paul1@gmail.com>
2021-12-30 22:56:10 +01:00
if (MessageCharacterLimit(source, message))
{
return;
Headsets (#2023) * add headset component * add basic headset logic * fix formatting in listening component, add dependency to headset * test function for headset * implement headset as listener * ANNIHILATES ListeningComponent, refactor of radio/listener sys * basic headset functionality * rename RadioComponent to HandheldRadioComponent * change channel to list of channels * basic headset implementation complete * message now always excludes ';' * add radio color; state channel freq. and source name * undocumented game breaking bug commit (DO NOT RESEARCH) actually just changes frequency from 1457 (what signalers are set to by default) to 1459, the actual frequency for common * Add more sprites * Reorganizes * Added job headsets * Adds headset as an ignored component * Jobs now spawn with headsets * remove system.tracing * Catchup commits * Add headset property serialization * Turn GetChannels into a property * ListenRange property and serializatioon * Adjust interfaces * Address reviews * Cleanup * Address reviews * Update rsi * Fix licenses and copyright * Fix missing textures * Merge fixes * Move headset textures from objects/devices to clothing/ears * Fix rsi state names and add equipped states * Fix headsets not working * Add missing brackets to channel number in chat * heck * Fix broken rsi * Fix radio id and names * Put quotes around headset messages * Fix method names * Fix handheld radios * Fix capitalization when using radio channels and trim * Remove unnecessary dependency * Indent that * Separate this part * Goodbye icons * Implement IActivate in HandheldRadioComponent * Add examine tooltip to radios and headsets * Rename IListen methods Co-authored-by: Bright <nsmoak10@yahoo.com> Co-authored-by: Swept <jamesurquhartwebb@gmail.com> Co-authored-by: Bright0 <55061890+Bright0@users.noreply.github.com>
2020-10-07 14:02:12 +02:00
}
message = message.Trim();
message = SanitizeMessageCapital(source, message);
foreach (var handler in _chatTransformHandlers)
Headsets (#2023) * add headset component * add basic headset logic * fix formatting in listening component, add dependency to headset * test function for headset * implement headset as listener * ANNIHILATES ListeningComponent, refactor of radio/listener sys * basic headset functionality * rename RadioComponent to HandheldRadioComponent * change channel to list of channels * basic headset implementation complete * message now always excludes ';' * add radio color; state channel freq. and source name * undocumented game breaking bug commit (DO NOT RESEARCH) actually just changes frequency from 1457 (what signalers are set to by default) to 1459, the actual frequency for common * Add more sprites * Reorganizes * Added job headsets * Adds headset as an ignored component * Jobs now spawn with headsets * remove system.tracing * Catchup commits * Add headset property serialization * Turn GetChannels into a property * ListenRange property and serializatioon * Adjust interfaces * Address reviews * Cleanup * Address reviews * Update rsi * Fix licenses and copyright * Fix missing textures * Merge fixes * Move headset textures from objects/devices to clothing/ears * Fix rsi state names and add equipped states * Fix headsets not working * Add missing brackets to channel number in chat * heck * Fix broken rsi * Fix radio id and names * Put quotes around headset messages * Fix method names * Fix handheld radios * Fix capitalization when using radio channels and trim * Remove unnecessary dependency * Indent that * Separate this part * Goodbye icons * Implement IActivate in HandheldRadioComponent * Add examine tooltip to radios and headsets * Rename IListen methods Co-authored-by: Bright <nsmoak10@yahoo.com> Co-authored-by: Swept <jamesurquhartwebb@gmail.com> Co-authored-by: Bright0 <55061890+Bright0@users.noreply.github.com>
2020-10-07 14:02:12 +02:00
{
//TODO: rather return a bool and use a out var?
message = handler(source, message);
Headsets (#2023) * add headset component * add basic headset logic * fix formatting in listening component, add dependency to headset * test function for headset * implement headset as listener * ANNIHILATES ListeningComponent, refactor of radio/listener sys * basic headset functionality * rename RadioComponent to HandheldRadioComponent * change channel to list of channels * basic headset implementation complete * message now always excludes ';' * add radio color; state channel freq. and source name * undocumented game breaking bug commit (DO NOT RESEARCH) actually just changes frequency from 1457 (what signalers are set to by default) to 1459, the actual frequency for common * Add more sprites * Reorganizes * Added job headsets * Adds headset as an ignored component * Jobs now spawn with headsets * remove system.tracing * Catchup commits * Add headset property serialization * Turn GetChannels into a property * ListenRange property and serializatioon * Adjust interfaces * Address reviews * Cleanup * Address reviews * Update rsi * Fix licenses and copyright * Fix missing textures * Merge fixes * Move headset textures from objects/devices to clothing/ears * Fix rsi state names and add equipped states * Fix headsets not working * Add missing brackets to channel number in chat * heck * Fix broken rsi * Fix radio id and names * Put quotes around headset messages * Fix method names * Fix handheld radios * Fix capitalization when using radio channels and trim * Remove unnecessary dependency * Indent that * Separate this part * Goodbye icons * Implement IActivate in HandheldRadioComponent * Add examine tooltip to radios and headsets * Rename IListen methods Co-authored-by: Bright <nsmoak10@yahoo.com> Co-authored-by: Swept <jamesurquhartwebb@gmail.com> Co-authored-by: Bright0 <55061890+Bright0@users.noreply.github.com>
2020-10-07 14:02:12 +02:00
}
var listeners = EntitySystem.Get<ListeningSystem>();
listeners.PingListeners(source, message);
2021-12-20 12:42:42 +01:00
message = FormattedMessage.EscapeText(message);
var obfuscatedMessage = ObfuscateMessageReadability(message, 0.2f);
var sessions = new List<ICommonSession>();
ClientDistanceToList(source, VoiceRange, sessions);
var transformSource = _entManager.GetComponent<TransformComponent>(source);
var sourceCoords = transformSource.Coordinates;
var messageWrap = Loc.GetString("chat-manager-entity-whisper-wrap-message",("entityName", _entManager.GetComponent<MetaDataComponent>(source).EntityName));
var xforms = _entManager.GetEntityQuery<TransformComponent>();
var ghosts = _entManager.GetEntityQuery<GhostComponent>();
foreach (var session in sessions)
{
if (session.AttachedEntity is not {Valid: true} playerEntity)
continue;
var transformEntity = xforms.GetComponent(playerEntity);
if (sourceCoords.InRange(_entManager, transformEntity.Coordinates, WhisperRange) ||
ghosts.HasComponent(playerEntity))
{
NetMessageToOne(ChatChannel.Whisper, message, messageWrap, source, hideChat, session.ConnectedClient);
}
else
{
NetMessageToOne(ChatChannel.Whisper, obfuscatedMessage, messageWrap, source, hideChat, session.ConnectedClient);
}
}
2022-01-19 13:35:31 +11:00
_logs.Add(LogType.Chat, LogImpact.Low, $"Whisper from {_entManager.ToPrettyString(source):user}: {message}");
}
2021-12-05 18:09:01 +01:00
public void EntityMe(EntityUid source, string action)
{
2021-12-03 15:53:09 +01:00
if (!EntitySystem.Get<ActionBlockerSystem>().CanEmote(source))
{
return;
}
if (MessageCharacterLimit(source, action))
2021-02-16 20:14:32 +01:00
{
return;
}
2021-12-20 12:42:42 +01:00
action = FormattedMessage.EscapeText(action);
var sessions = new List<ICommonSession>();
ClientDistanceToList(source, VoiceRange, sessions);
var messageWrap = Loc.GetString("chat-manager-entity-me-wrap-message", ("entityName", _entManager.GetComponent<MetaDataComponent>(source).EntityName));
foreach (var session in sessions)
2022-01-11 16:29:55 +03:00
{
NetMessageToOne(ChatChannel.Emotes, action, messageWrap, source, true, session.ConnectedClient);
2022-01-11 16:29:55 +03:00
}
2022-01-19 13:35:31 +11:00
_logs.Add(LogType.Chat, LogImpact.Low, $"Emote from {_entManager.ToPrettyString(source):user}: {action}");
}
2022-01-11 16:29:55 +03:00
public void SendLOOC(IPlayerSession player, string message)
{
if (_adminManager.IsAdmin(player))
2022-01-11 16:29:55 +03:00
{
if (!_adminLoocEnabled)
{
return;
}
}
else if (!_loocEnabled)
{
return;
}
// Check they're even attached to an entity before we potentially send a message length error.
if (player.AttachedEntity is not { } entity)
{
return;
}
2022-01-11 16:29:55 +03:00
// Check if message exceeds the character limit
if (message.Length > MaxMessageLength)
{
DispatchServerMessage(player, Loc.GetString("chat-manager-max-message-length-exceeded-message", ("limit", MaxMessageLength)));
2022-01-11 16:29:55 +03:00
return;
}
message = FormattedMessage.EscapeText(message);
var sessions = new List<ICommonSession>();
2022-01-11 16:29:55 +03:00
ClientDistanceToList(entity, VoiceRange, sessions);
2022-01-11 16:29:55 +03:00
var msg = _netManager.CreateNetMessage<MsgChatMessage>();
msg.Channel = ChatChannel.LOOC;
msg.Message = message;
msg.MessageWrap = Loc.GetString("chat-manager-entity-looc-wrap-message", ("entityName", _entManager.GetComponent<MetaDataComponent>(entity).EntityName));
_netManager.ServerSendToMany(msg, sessions.Select(o => o.ConnectedClient).ToList());
2022-01-19 13:35:31 +11:00
_logs.Add(LogType.Chat, LogImpact.Low, $"LOOC from {player:Player}: {message}");
2022-01-11 16:29:55 +03:00
}
public void SendOOC(IPlayerSession player, string message)
{
2021-02-16 20:14:32 +01:00
if (_adminManager.IsAdmin(player))
{
if (!_adminOocEnabled)
{
return;
}
}
else if (!_oocEnabled)
{
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)));
return;
}
2021-12-20 12:42:42 +01:00
message = FormattedMessage.EscapeText(message);
var msg = _netManager.CreateNetMessage<MsgChatMessage>();
msg.Channel = ChatChannel.OOC;
msg.Message = message;
msg.MessageWrap = Loc.GetString("chat-manager-send-ooc-wrap-message", ("playerName",player.Name));
if (_adminManager.HasAdminFlag(player, AdminFlags.Admin))
{
2021-02-16 20:14:32 +01:00
var prefs = _preferencesManager.GetPreferences(player.UserId);
msg.MessageColorOverride = prefs.AdminOOCColor;
}
2021-02-28 18:51:42 +01:00
if (player.ConnectedClient.UserData.PatronTier is { } patron &&
PatronOocColors.TryGetValue(patron, out var patronColor))
{
msg.MessageWrap = Loc.GetString("chat-manager-send-ooc-patron-wrap-message", ("patronColor", patronColor),("playerName", player.Name));
2021-02-28 18:51:42 +01:00
}
//TODO: player.Name color, this will need to change the structure of the MsgChatMessage
_netManager.ServerSendToAll(msg);
2019-04-17 23:31:43 +02:00
_mommiLink.SendOOCMessage(player.Name, message);
2022-01-19 13:35:31 +11:00
_logs.Add(LogType.Chat, LogImpact.Low, $"OOC from {player:Player}: {message}");
2019-04-17 23:31:43 +02:00
}
2020-03-30 01:15:43 +02:00
public void SendDeadChat(IPlayerSession player, string message)
{
// Check if message exceeds the character limit
if (message.Length > MaxMessageLength)
{
DispatchServerMessage(player, Loc.GetString("chat-manager-max-message-length-exceeded-message",("limit", MaxMessageLength)));
return;
}
2021-12-20 12:42:42 +01:00
message = FormattedMessage.EscapeText(message);
var clients = GetDeadChatClients();
2020-03-30 01:15:43 +02:00
var msg = _netManager.CreateNetMessage<MsgChatMessage>();
msg.Channel = ChatChannel.Dead;
msg.Message = message;
2021-12-05 18:09:01 +01:00
var playerName = player.AttachedEntity is {Valid: true} playerEntity
? _entManager.GetComponent<MetaDataComponent>(playerEntity).EntityName
: "???";
msg.MessageWrap = Loc.GetString("chat-manager-send-dead-chat-wrap-message",
("deadChannelName", Loc.GetString("chat-manager-dead-channel-name")),
2021-12-05 18:09:01 +01:00
("playerName", (playerName)));
2021-12-06 15:34:46 +01:00
msg.SenderEntity = player.AttachedEntity.GetValueOrDefault();
2020-03-30 01:15:43 +02:00
_netManager.ServerSendToMany(msg, clients.ToList());
2022-01-19 13:35:31 +11:00
_logs.Add(LogType.Chat, LogImpact.Low, $"Dead chat from {player:Player}: {message}");
2020-03-30 01:15:43 +02:00
}
public void SendAdminDeadChat(IPlayerSession player, string message)
{
// Check if message exceeds the character limit
if (message.Length > MaxMessageLength)
{
DispatchServerMessage(player, Loc.GetString("chat-manager-max-message-length-exceeded-message", ("limit", MaxMessageLength)));
return;
}
2021-12-20 12:42:42 +01:00
message = FormattedMessage.EscapeText(message);
var clients = GetDeadChatClients();
var msg = _netManager.CreateNetMessage<MsgChatMessage>();
msg.Channel = ChatChannel.Dead;
msg.Message = message;
msg.MessageWrap = Loc.GetString("chat-manager-send-admin-dead-chat-wrap-message",
("adminChannelName", Loc.GetString("chat-manager-admin-channel-name")),
("userName", player.ConnectedClient.UserName));
_netManager.ServerSendToMany(msg, clients.ToList());
2022-01-19 13:35:31 +11:00
_logs.Add(LogType.Chat, LogImpact.Low, $"Admin dead chat from {player:Player}: {message}");
}
private IEnumerable<INetChannel> GetDeadChatClients()
{
return Filter.Empty()
.AddWhereAttachedEntity(uid => _entManager.HasComponent<GhostComponent>(uid))
.Recipients
.Union(_adminManager.ActiveAdmins)
.Select(p => p.ConnectedClient);
}
public void SendAdminChat(IPlayerSession player, string message)
{
// Check if message exceeds the character limit
if (message.Length > MaxMessageLength)
{
DispatchServerMessage(player, Loc.GetString("chat-manager-max-message-length-exceeded-message", ("limit", MaxMessageLength)));
return;
}
2021-12-20 12:42:42 +01:00
message = FormattedMessage.EscapeText(message);
var clients = _adminManager.ActiveAdmins.Select(p => p.ConnectedClient);
var msg = _netManager.CreateNetMessage<MsgChatMessage>();
Chat improvements. (#4283) * UI is an abbreviation, in XAML. * Chat improvements. Changing the "selected" channel on the chat box is now only done via the tab cycle or clicking the button. Prefix chars like [ will temporarily replace the active chat channel. This is based 100% on message box contents so there's no input eating garbage or anything. Pressing specific channel focusing keys inserts the correct prefix character, potentially replacing an existing one. Existing chat contents are left in place just fine and selected so you can easily delete them (but are not forced to). Channel focusing keys now match the QWERTY key codes. Deadchat works now. Console can no longer be selected as a chat channel, but you can still use it with the / prefix. Refactored the connection between chat manager and chat box so that it's event based, reducing tons of spaghetti everywhere. Main chat box control uses XAML now. General cleanup. Added focus hotkeys for deadchat/console. Also added prefix for deadchat. Local chat is mapped to deadchat when a ghost. Probably more stuff I can't think of right now. * Add preferred channel system to chat box to automatically select local. I can't actually test this works because the non-lobby chat box code is complete disastrous spaghetti and i need to refactor it. * Move chatbox resizing and all that to a subclass. Refine preferred channel & deadchat mapping code further. * Don't do prefixes for channels you don't have access to. * Change format on channel select popup. * Clean up code with console handling somewhat.
2021-07-20 10:29:09 +02:00
msg.Channel = ChatChannel.Admin;
msg.Message = message;
msg.MessageWrap = Loc.GetString("chat-manager-send-admin-chat-wrap-message",
("adminChannelName", Loc.GetString("chat-manager-admin-channel-name")),
("playerName", player.Name));
_netManager.ServerSendToMany(msg, clients.ToList());
2022-01-19 13:35:31 +11:00
_logs.Add(LogType.Chat, $"Admin chat from {player:Player}: {message}");
}
2020-11-01 23:56:35 +01:00
public void SendAdminAnnouncement(string message)
{
var clients = _adminManager.ActiveAdmins.Select(p => p.ConnectedClient);
2021-12-20 12:42:42 +01:00
message = FormattedMessage.EscapeText(message);
2020-11-01 23:56:35 +01:00
var msg = _netManager.CreateNetMessage<MsgChatMessage>();
Chat improvements. (#4283) * UI is an abbreviation, in XAML. * Chat improvements. Changing the "selected" channel on the chat box is now only done via the tab cycle or clicking the button. Prefix chars like [ will temporarily replace the active chat channel. This is based 100% on message box contents so there's no input eating garbage or anything. Pressing specific channel focusing keys inserts the correct prefix character, potentially replacing an existing one. Existing chat contents are left in place just fine and selected so you can easily delete them (but are not forced to). Channel focusing keys now match the QWERTY key codes. Deadchat works now. Console can no longer be selected as a chat channel, but you can still use it with the / prefix. Refactored the connection between chat manager and chat box so that it's event based, reducing tons of spaghetti everywhere. Main chat box control uses XAML now. General cleanup. Added focus hotkeys for deadchat/console. Also added prefix for deadchat. Local chat is mapped to deadchat when a ghost. Probably more stuff I can't think of right now. * Add preferred channel system to chat box to automatically select local. I can't actually test this works because the non-lobby chat box code is complete disastrous spaghetti and i need to refactor it. * Move chatbox resizing and all that to a subclass. Refine preferred channel & deadchat mapping code further. * Don't do prefixes for channels you don't have access to. * Change format on channel select popup. * Clean up code with console handling somewhat.
2021-07-20 10:29:09 +02:00
msg.Channel = ChatChannel.Admin;
2020-11-01 23:56:35 +01:00
msg.Message = message;
msg.MessageWrap = Loc.GetString("chat-manager-send-admin-announcement-wrap-message",
("adminChannelName", Loc.GetString("chat-manager-admin-channel-name")));
2020-11-01 23:56:35 +01:00
_netManager.ServerSendToMany(msg, clients.ToList());
2022-01-19 13:35:31 +11:00
_logs.Add(LogType.Chat, LogImpact.Low, $"Admin announcement from {message}: {message}");
2020-11-01 23:56:35 +01:00
}
2019-04-17 23:31:43 +02:00
public void SendHookOOC(string sender, string message)
{
2021-12-20 12:42:42 +01:00
message = FormattedMessage.EscapeText(message);
var messageWrap = Loc.GetString("chat-manager-send-hook-ooc-wrap-message", ("senderName", sender));
NetMessageToAll(ChatChannel.OOC, message, messageWrap);
2022-01-19 13:35:31 +11:00
_logs.Add(LogType.Chat, LogImpact.Low, $"Hook OOC from {sender}: {message}");
}
public void RegisterChatTransform(TransformChat handler)
{
// TODO: Literally just make this an event...
_chatTransformHandlers.Add(handler);
}
public void SendEntityChatType(EntityUid source, string message, bool isWhisper)
{
// I don't know why you're trying to smile over the radio...
// This filters out the players who just really want to try.
if (message.StartsWith(';') && message.Length <= 1)
{
return;
}
// We check to see if message is a whisper or a standard say message.
if (isWhisper)
{
EntityWhisper(source, message);
}
else
{
EntitySay(source, message);
}
}
public string SanitizeMessageCapital(EntityUid source, string message)
{
if (message.StartsWith(';'))
{
// Remove semicolon
message = message.Substring(1).TrimStart();
// Capitalize first letter
message = message[0].ToString().ToUpper() + message.Remove(0, 1);
var invSystem = EntitySystem.Get<InventorySystem>();
if (invSystem.TryGetSlotEntity(source, "ears", out var entityUid) &&
_entManager.TryGetComponent(entityUid, out HeadsetComponent? headset))
{
headset.RadioRequested = true;
}
else
{
source.PopupMessage(Loc.GetString("chat-manager-no-headset-on-message"));
}
}
else
{
// Capitalize first letter
message = message[0].ToString().ToUpper() + message.Remove(0, 1);
}
return message;
}
public void NetMessageToOne(ChatChannel channel, string message, string messageWrap, EntityUid source, bool hideChat, INetChannel client)
{
2019-04-17 23:31:43 +02:00
var msg = _netManager.CreateNetMessage<MsgChatMessage>();
msg.Channel = channel;
2019-04-17 23:31:43 +02:00
msg.Message = message;
msg.MessageWrap = messageWrap;
msg.SenderEntity = source;
msg.HideChat = hideChat;
_netManager.ServerSendMessage(msg, client);
}
public void NetMessageToAll(ChatChannel channel, string message, string messageWrap, Color? colorOverride = null)
{
var msg = _netManager.CreateNetMessage<MsgChatMessage>();
msg.Channel = channel;
msg.Message = message;
msg.MessageWrap = messageWrap;
if (colorOverride != null)
{
msg.MessageColorOverride = colorOverride.Value;
}
2019-04-17 23:31:43 +02:00
_netManager.ServerSendToAll(msg);
}
public bool MessageCharacterLimit(EntityUid source, string message)
{
var isOverLength = false;
// Check if message exceeds the character limit if the sender is a player
if (_entManager.TryGetComponent(source, out ActorComponent? actor) &&
message.Length > MaxMessageLength)
{
var feedback = Loc.GetString("chat-manager-max-message-length-exceeded-message", ("limit", MaxMessageLength));
DispatchServerMessage(actor.PlayerSession, feedback);
isOverLength = true;
}
return isOverLength;
}
public void ClientDistanceToList(EntityUid source, int voiceRange, List<ICommonSession> playerSessions)
{
var ghosts = _entManager.GetEntityQuery<GhostComponent>();
var xforms = _entManager.GetEntityQuery<TransformComponent>();
var transformSource = xforms.GetComponent(source);
var sourceMapId = transformSource.MapID;
var sourceCoords = transformSource.Coordinates;
foreach (var player in _playerManager.Sessions)
{
if (player.AttachedEntity is not {Valid: true} playerEntity)
continue;
var transformEntity = xforms.GetComponent(playerEntity);
if (transformEntity.MapID != sourceMapId ||
!ghosts.HasComponent(playerEntity) &&
!sourceCoords.InRange(_entManager, transformEntity.Coordinates, voiceRange))
continue;
playerSessions.Add(player);
}
}
public string ObfuscateMessageReadability(string message, float chance)
{
var modifiedMessage = new StringBuilder(message);
for (var i = 0; i < message.Length; i++)
{
if (char.IsWhiteSpace((modifiedMessage[i])))
{
continue;
}
if (_random.Prob(chance))
{
modifiedMessage[i] = modifiedMessage[i];
}
else
{
modifiedMessage[i] = '~';
}
}
return modifiedMessage.ToString();
}
}
}