Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Chatstack #1422

Merged
merged 5 commits into from
Jan 4, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion Content.Client/Options/UI/Tabs/MiscTab.xaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<tabs:MiscTab xmlns="https://spacestation14.io"
<tabs:MiscTab xmlns="https://spacestation14.io"
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
xmlns:tabs="clr-namespace:Content.Client.Options.UI.Tabs"
xmlns:xNamespace="http://schemas.microsoft.com/winfx/2006/xaml"
Expand Down Expand Up @@ -26,6 +26,11 @@
<CheckBox Name="EnableColorNameCheckBox" Text="{Loc 'ui-options-enable-color-name'}" />
<CheckBox Name="ColorblindFriendlyCheckBox" Text="{Loc 'ui-options-colorblind-friendly'}" />
<CheckBox Name="DisableFiltersCheckBox" Text="{Loc 'ui-options-no-filters'}" />
<BoxContainer Orientation="Horizontal">
<Label Text="{Loc 'ui-options-chatstack'}" />
<Control MinSize="4 0" />
<OptionButton Name="ChatStackOption" />
</BoxContainer>
<BoxContainer Orientation="Horizontal">
<Label Text="{Loc 'ui-options-chat-window-opacity'}" Margin="8 0" />
<Slider Name="ChatWindowOpacitySlider"
Expand Down
19 changes: 17 additions & 2 deletions Content.Client/Options/UI/Tabs/MiscTab.xaml.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using System.Linq;
using System.Linq;
using Content.Client.UserInterface.Screens;
using Content.Shared.CCVar;
using Content.Shared.HUD;
Expand Down Expand Up @@ -59,6 +59,18 @@ public MiscTab()
UpdateApplyButton();
};

ChatStackOption.AddItem(Loc.GetString("ui-options-chatstack-off"), 0);
ChatStackOption.AddItem(Loc.GetString("ui-options-chatstack-single"), 1);
ChatStackOption.AddItem(Loc.GetString("ui-options-chatstack-double"), 2);
ChatStackOption.AddItem(Loc.GetString("ui-options-chatstack-triple"), 3);
ChatStackOption.TrySelectId(_cfg.GetCVar(CCVars.ChatStackLastLines));

ChatStackOption.OnItemSelected += args =>
{
ChatStackOption.SelectId(args.Id);
UpdateApplyButton();
};

// Channel can be null in replays so.
// ReSharper disable once ConditionalAccessQualifierIsNonNullableAccordingToAPIContract
ShowOocPatronColor.Visible = _playerManager.LocalSession?.Channel?.UserData.PatronTier is { };
Expand Down Expand Up @@ -157,6 +169,7 @@ private void OnApplyButtonPressed(BaseButton.ButtonEventArgs args)
// _cfg.SetCVar(CCVars.ToggleWalk, ToggleWalk.Pressed);
_cfg.SetCVar(CCVars.StaticStorageUI, StaticStorageUI.Pressed);
_cfg.SetCVar(CCVars.NoVisionFilters, DisableFiltersCheckBox.Pressed);
_cfg.SetCVar(CCVars.ChatStackLastLines, ChatStackOption.SelectedId);

if (HudLayoutOption.SelectedMetadata is string opt)
{
Expand Down Expand Up @@ -188,6 +201,7 @@ private void UpdateApplyButton()
// var isToggleWalkSame = ToggleWalk.Pressed == _cfg.GetCVar(CCVars.ToggleWalk);
var isStaticStorageUISame = StaticStorageUI.Pressed == _cfg.GetCVar(CCVars.StaticStorageUI);
var isNoVisionFiltersSame = DisableFiltersCheckBox.Pressed == _cfg.GetCVar(CCVars.NoVisionFilters);
var isChatStackTheSame = ChatStackOption.SelectedId == _cfg.GetCVar(CCVars.ChatStackLastLines);

ApplyButton.Disabled = isHudThemeSame &&
isLayoutSame &&
Expand All @@ -207,7 +221,8 @@ private void UpdateApplyButton()
isScreenShakeIntensitySame &&
// isToggleWalkSame &&
isStaticStorageUISame &&
isNoVisionFiltersSame;
isNoVisionFiltersSame &&
isChatStackTheSame;
}

}
Expand Down
109 changes: 105 additions & 4 deletions Content.Client/UserInterface/Systems/Chat/Widgets/ChatBox.xaml.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Content.Client.UserInterface.Systems.Chat.Controls;
using Content.Shared.CCVar;
using Content.Shared.Chat;
using Content.Shared.Input;
using Robust.Client.Audio;
Expand All @@ -7,7 +8,9 @@
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML;
using Robust.Shared;
using Robust.Shared.Audio;
using Robust.Shared.Configuration;
using Robust.Shared.Input;
using Robust.Shared.Player;
using Robust.Shared.Utility;
Expand All @@ -22,14 +25,22 @@ public partial class ChatBox : UIWidget
{
private readonly ChatUIController _controller;
private readonly IEntityManager _entManager;
private readonly IConfigurationManager _cfg;
private readonly ILocalizationManager _loc;

public bool Main { get; set; }

public ChatSelectChannel SelectedChannel => ChatInput.ChannelSelector.SelectedChannel;

private int _chatStackAmount = 0;
private bool _chatStackEnabled => _chatStackAmount > 0;
private List<ChatStackData> _chatStackList;


public ChatBox()
{
RobustXamlLoader.Load(this);
_loc = IoCManager.Resolve<ILocalizationManager>();
_entManager = IoCManager.Resolve<IEntityManager>();

ChatInput.Input.OnTextEntered += OnTextEntered;
Expand All @@ -41,6 +52,22 @@ public ChatBox()
_controller = UserInterfaceManager.GetUIController<ChatUIController>();
_controller.MessageAdded += OnMessageAdded;
_controller.RegisterChat(this);


_cfg = IoCManager.Resolve<IConfigurationManager>();
//_chatStackAmount = _cfg.GetCVar(CCVars.ChatStackLastLines);
//if (_chatStackAmount < 0) // anti-idiot protection
// _chatStackAmount = 0;
_chatStackList = new(_chatStackAmount);
_cfg.OnValueChanged(CCVars.ChatStackLastLines, UpdateChatStack, true);

}


private void UpdateChatStack(int value)
{
_chatStackAmount = value >= 0 ? value : 0;
Repopulate();
}

private void OnTextEntered(LineEditEventArgs args)
Expand All @@ -63,7 +90,57 @@ private void OnMessageAdded(ChatMessage msg)

var color = msg.MessageColorOverride ?? msg.Channel.TextColor();

AddLine(msg.WrappedMessage, color);

if (msg.IgnoreChatStack)
{
TrackNewMessage(msg.WrappedMessage, color, true);
AddLine(msg.WrappedMessage, color);
return;
}

int index = _chatStackList.FindIndex(data => data.WrappedMessage == msg.WrappedMessage && !data.IgnoresChatstack);

if (index == -1) // this also handles chatstack being disabled, since FindIndex won't find anything in an empty array
{
TrackNewMessage(msg.WrappedMessage, color);
AddLine(msg.WrappedMessage, color);
return;
}

UpdateRepeatingLine(index);
}

/// <summary>
/// Removing and then adding insantly nudges the chat window up before slowly dragging it back down, which makes the whole chat log shake.
/// With rapid enough updates, the whole chat becomes unreadable.
/// Adding first and then removing does not produce any visual effects.
/// The other option is to dublicate OutputPanel functionality and everything internal to the engine it relies on.
/// But OutputPanel relies on directly setting Control.Position for control embedding. (which is not exposed to Content.)
/// Thanks robustengine, very cool.
/// </summary>
/// <remarks>
/// zero index is the very last line in chat, 1 is the line before the last one, 2 is the line before that, etc.
/// </remarks>
private void UpdateRepeatingLine(int index)
{
_chatStackList[index].RepeatCount++;
for (int i = index; i >= 0; i--)
{
var data = _chatStackList[i];
AddLine(data.WrappedMessage, data.ColorOverride, data.RepeatCount);
Contents.RemoveEntry(Index.FromEnd(index + 2));
}
}

private void TrackNewMessage(string wrappedMessage, Color colorOverride, bool ignoresChatstack = false)
{
if (!_chatStackEnabled)
return;

if(_chatStackList.Count == _chatStackList.Capacity)
_chatStackList.RemoveAt(_chatStackList.Capacity - 1);

_chatStackList.Insert(0, new ChatStackData(wrappedMessage, colorOverride, ignoresChatstack));
}

private void OnChannelSelect(ChatSelectChannel channel)
Expand All @@ -74,7 +151,7 @@ private void OnChannelSelect(ChatSelectChannel channel)
public void Repopulate()
{
Contents.Clear();

_chatStackList = new List<ChatStackData>(_chatStackAmount);
foreach (var message in _controller.History)
{
OnMessageAdded(message.Item2);
Expand All @@ -96,12 +173,21 @@ private void OnChannelFilter(ChatChannel channel, bool active)
}
}

public void AddLine(string message, Color color)
public void AddLine(string message, Color color, int repeat = 0)
{
var formatted = new FormattedMessage(3);
var formatted = new FormattedMessage(4);
formatted.PushColor(color);
formatted.AddMarkup(message);
formatted.Pop();
if (repeat != 0)
{
int displayRepeat = repeat + 1;
int sizeIncrease = Math.Min(displayRepeat / 6, 5);
formatted.AddMarkup(_loc.GetString("chat-system-repeated-message-counter",
("count", displayRepeat),
("size", 8 + sizeIncrease)
));
}
Contents.AddMessage(formatted);
}

Expand Down Expand Up @@ -185,5 +271,20 @@ protected override void Dispose(bool disposing)
ChatInput.Input.OnKeyBindDown -= OnInputKeyBindDown;
ChatInput.Input.OnTextChanged -= OnTextChanged;
ChatInput.ChannelSelector.OnChannelSelect -= OnChannelSelect;
_cfg.UnsubValueChanged(CCVars.ChatStackLastLines, UpdateChatStack);
}

private class ChatStackData
{
public string WrappedMessage;
public Color ColorOverride;
public int RepeatCount = 0;
public bool IgnoresChatstack;
public ChatStackData(string wrappedMessage, Color colorOverride, bool ignoresChatstack = false)
{
WrappedMessage = wrappedMessage;
ColorOverride = colorOverride;
IgnoresChatstack = ignoresChatstack;
}
}
}
20 changes: 10 additions & 10 deletions Content.Server/Chat/Managers/ChatManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -296,13 +296,13 @@ private void SendAdminChat(ICommonSession player, string 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, NetUserId? author = 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, bool ignoreChatStack = false)
{
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);
var msg = new ChatMessage(channel, message, wrappedMessage, netSource, user?.Key, hideChat, colorOverride, audioPath, audioVolume, ignoreChatStack);
_netManager.ServerSendMessage(new MsgChatMessage() { Message = msg }, client);

if (!recordReplay)
Expand All @@ -315,16 +315,16 @@ public void ChatMessageToOne(ChatChannel channel, string message, string wrapped
}
}

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, IEnumerable<INetChannel> clients, Color? colorOverride = null, string? audioPath = null, float audioVolume = 0, NetUserId? author = null, bool ignoreChatStack = false)
=> ChatMessageToMany(channel, message, wrappedMessage, source, hideChat, recordReplay, clients.ToList(), colorOverride, audioPath, audioVolume, author, ignoreChatStack);

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)
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, bool ignoreChatStack = false)
{
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);
var msg = new ChatMessage(channel, message, wrappedMessage, netSource, user?.Key, hideChat, colorOverride, audioPath, audioVolume, ignoreChatStack);
_netManager.ServerSendToMany(new MsgChatMessage() { Message = msg }, clients);

if (!recordReplay)
Expand All @@ -338,7 +338,7 @@ public void ChatMessageToMany(ChatChannel channel, string message, string wrappe
}

public void ChatMessageToManyFiltered(Filter filter, ChatChannel channel, string message, string wrappedMessage, EntityUid source,
bool hideChat, bool recordReplay, Color? colorOverride = null, string? audioPath = null, float audioVolume = 0)
bool hideChat, bool recordReplay, Color? colorOverride = null, string? audioPath = null, float audioVolume = 0, bool ignoreChatStack = false)
{
if (!recordReplay && !filter.Recipients.Any())
return;
Expand All @@ -349,16 +349,16 @@ public void ChatMessageToManyFiltered(Filter filter, ChatChannel channel, string
clients.Add(recipient.Channel);
}

ChatMessageToMany(channel, message, wrappedMessage, source, hideChat, recordReplay, clients, colorOverride, audioPath, audioVolume);
ChatMessageToMany(channel, message, wrappedMessage, source, hideChat, recordReplay, clients, colorOverride, audioPath, audioVolume, null, ignoreChatStack);
}

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)
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, bool ignoreChatStack = false)
{
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);
var msg = new ChatMessage(channel, message, wrappedMessage, netSource, user?.Key, hideChat, colorOverride, audioPath, audioVolume, ignoreChatStack);
_netManager.ServerSendToAll(new MsgChatMessage() { Message = msg });

if (!recordReplay)
Expand Down
8 changes: 4 additions & 4 deletions Content.Server/Chat/Managers/IChatManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,14 @@ public interface IChatManager : ISharedChatManager
void SendAdminAnnouncementMessage(ICommonSession player, string message, bool suppressLog = true);

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);
INetChannel client, Color? colorOverride = null, bool recordReplay = false, string? audioPath = null, float audioVolume = 0, NetUserId? author = null, bool ignoreChatStack = false);

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);
IEnumerable<INetChannel> clients, Color? colorOverride = null, string? audioPath = null, float audioVolume = 0, NetUserId? author = null, bool ignoreChatStack = false);

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 ChatMessageToManyFiltered(Filter filter, ChatChannel channel, string message, string wrappedMessage, EntityUid source, bool hideChat, bool recordReplay, Color? colorOverride, string? audioPath = null, float audioVolume = 0, bool ignoreChatStack = false);

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);
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 ignoreChatStack = false);

bool MessageCharacterLimit(ICommonSession player, string message);

Expand Down
3 changes: 3 additions & 0 deletions Content.Shared/CCVar/CCVars.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2091,6 +2091,9 @@ public static readonly CVarDef<int>
public static readonly CVarDef<bool> ChatFancyNameBackground =
CVarDef.Create("chat.fancy_name_background", false, CVar.CLIENTONLY | CVar.ARCHIVE, "Toggles displaying a background under the speaking character's name.");

public static readonly CVarDef<int> ChatStackLastLines =
CVarDef.Create("chat.chatstack_last_lines", 1, CVar.CLIENTONLY | CVar.ARCHIVE, "How far into the chat history to look when looking for similiar messages to coalesce them.");

/// <summary>
/// A message broadcast to each player that joins the lobby.
/// May be changed by admins ingame through use of the "set-motd" command.
Expand Down
5 changes: 3 additions & 2 deletions Content.Shared/Chat/MsgChatMessage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,11 @@ public sealed class ChatMessage
public Color? MessageColorOverride;
public string? AudioPath;
public float AudioVolume;

public bool IgnoreChatStack;
[NonSerialized]
public bool Read;

public ChatMessage(ChatChannel channel, string message, string wrappedMessage, NetEntity source, int? senderKey, bool hideChat = false, Color? colorOverride = null, string? audioPath = null, float audioVolume = 0)
public ChatMessage(ChatChannel channel, string message, string wrappedMessage, NetEntity source, int? senderKey, bool hideChat = false, Color? colorOverride = null, string? audioPath = null, float audioVolume = 0, bool ignoreChatStack = false)
{
Channel = channel;
Message = message;
Expand All @@ -41,6 +41,7 @@ public ChatMessage(ChatChannel channel, string message, string wrappedMessage, N
MessageColorOverride = colorOverride;
AudioPath = audioPath;
AudioVolume = audioVolume;
IgnoreChatStack = ignoreChatStack;
}
}

Expand Down
Loading
Loading