diff --git a/Dalamud/Game/ChatHandlers.cs b/Dalamud/Game/ChatHandlers.cs index ed69b7bbe..1d82e5f9c 100644 --- a/Dalamud/Game/ChatHandlers.cs +++ b/Dalamud/Game/ChatHandlers.cs @@ -264,7 +264,7 @@ public class ChatHandlers : IServiceType if (string.IsNullOrEmpty(this.configuration.LastVersion) || !assemblyVersion.StartsWith(this.configuration.LastVersion)) { - chatGui.PrintChat(new XivChatEntry + chatGui.Print(new XivChatEntry { Message = Loc.Localize("DalamudUpdated", "Dalamud has been updated successfully! Please check the discord for a full changelog."), Type = XivChatType.Notice, @@ -321,7 +321,7 @@ public class ChatHandlers : IServiceType } else { - chatGui.PrintChat(new XivChatEntry + chatGui.Print(new XivChatEntry { Message = new SeString(new List() { diff --git a/Dalamud/Game/Gui/ChatGui.cs b/Dalamud/Game/Gui/ChatGui.cs index d8154b41d..2fbeb404e 100644 --- a/Dalamud/Game/Gui/ChatGui.cs +++ b/Dalamud/Game/Gui/ChatGui.cs @@ -20,13 +20,9 @@ namespace Dalamud.Game.Gui; /// /// This class handles interacting with the native chat UI. /// -[PluginInterface] [InterfaceVersion("1.0")] [ServiceManager.BlockingEarlyLoadedService] -#pragma warning disable SA1015 -[ResolveVia] -#pragma warning restore SA1015 -public sealed class ChatGui : IDisposable, IServiceType, IChatGui +internal sealed class ChatGui : IDisposable, IServiceType, IChatGui { private readonly ChatGuiAddressResolver address; @@ -66,16 +62,16 @@ public sealed class ChatGui : IDisposable, IServiceType, IChatGui private delegate void InteractableLinkClickedDelegate(IntPtr managerPtr, IntPtr messagePtr); /// - public event IChatGui.OnMessageDelegate ChatMessage; + public event IChatGui.OnMessageDelegate? ChatMessage; /// - public event IChatGui.OnCheckMessageHandledDelegate CheckMessageHandled; + public event IChatGui.OnCheckMessageHandledDelegate? CheckMessageHandled; /// - public event IChatGui.OnMessageHandledDelegate ChatMessageHandled; + public event IChatGui.OnMessageHandledDelegate? ChatMessageHandled; /// - public event IChatGui.OnMessageUnhandledDelegate ChatMessageUnhandled; + public event IChatGui.OnMessageUnhandledDelegate? ChatMessageUnhandled; /// public int LastLinkedItemId { get; private set; } @@ -94,55 +90,35 @@ public sealed class ChatGui : IDisposable, IServiceType, IChatGui } /// - public void PrintChat(XivChatEntry chat) + public void Print(XivChatEntry chat) { this.chatQueue.Enqueue(chat); } - + /// - public void Print(string message) + public void Print(string message, string? messageTag = null, ushort? tagColor = null) { - // Log.Verbose("[CHATGUI PRINT REGULAR]{0}", message); - this.PrintChat(new XivChatEntry - { - Message = message, - Type = this.configuration.GeneralChatType, - }); + this.PrintTagged(message, this.configuration.GeneralChatType, messageTag, tagColor); } - + /// - public void Print(SeString message) + public void Print(SeString message, string? messageTag = null, ushort? tagColor = null) { - // Log.Verbose("[CHATGUI PRINT SESTRING]{0}", message.TextValue); - this.PrintChat(new XivChatEntry - { - Message = message, - Type = this.configuration.GeneralChatType, - }); + this.PrintTagged(message, this.configuration.GeneralChatType, messageTag, tagColor); } - + /// - public void PrintError(string message) + public void PrintError(string message, string? messageTag = null, ushort? tagColor = null) { - // Log.Verbose("[CHATGUI PRINT REGULAR ERROR]{0}", message); - this.PrintChat(new XivChatEntry - { - Message = message, - Type = XivChatType.Urgent, - }); + this.PrintTagged(message, XivChatType.Urgent, messageTag, tagColor); } - + /// - public void PrintError(SeString message) + public void PrintError(SeString message, string? messageTag = null, ushort? tagColor = null) { - // Log.Verbose("[CHATGUI PRINT SESTRING ERROR]{0}", message.TextValue); - this.PrintChat(new XivChatEntry - { - Message = message, - Type = XivChatType.Urgent, - }); + this.PrintTagged(message, XivChatType.Urgent, messageTag, tagColor); } - + /// /// Process a chat queue. /// @@ -176,7 +152,7 @@ public sealed class ChatGui : IDisposable, IServiceType, IChatGui /// A payload for handling. internal DalamudLinkPayload AddChatLinkHandler(string pluginName, uint commandId, Action commandAction) { - var payload = new DalamudLinkPayload() { Plugin = pluginName, CommandId = commandId }; + var payload = new DalamudLinkPayload { Plugin = pluginName, CommandId = commandId }; this.dalamudLinkHandlers.Add((pluginName, commandId), commandAction); return payload; } @@ -200,20 +176,63 @@ public sealed class ChatGui : IDisposable, IServiceType, IChatGui /// The ID of the command to be removed. internal void RemoveChatLinkHandler(string pluginName, uint commandId) { - if (this.dalamudLinkHandlers.ContainsKey((pluginName, commandId))) - { - this.dalamudLinkHandlers.Remove((pluginName, commandId)); - } + this.dalamudLinkHandlers.Remove((pluginName, commandId)); } [ServiceManager.CallWhenServicesReady] - private void ContinueConstruction(GameGui gameGui, LibcFunction libcFunction) + private void ContinueConstruction() { this.printMessageHook.Enable(); this.populateItemLinkHook.Enable(); this.interactableLinkClickedHook.Enable(); } + private void PrintTagged(string message, XivChatType channel, string? tag, ushort? color) + { + var builder = new SeStringBuilder(); + + if (!tag.IsNullOrEmpty()) + { + if (color is not null) + { + builder.AddUiForeground($"[{tag}] ", color.Value); + } + else + { + builder.AddText($"[{tag}] "); + } + } + + this.Print(new XivChatEntry + { + Message = builder.AddText(message).Build(), + Type = channel, + }); + } + + private void PrintTagged(SeString message, XivChatType channel, string? tag, ushort? color) + { + var builder = new SeStringBuilder(); + + if (!tag.IsNullOrEmpty()) + { + if (color is not null) + { + builder.AddUiForeground($"[{tag}] ", color.Value); + } + else + { + builder.AddText($"[{tag}] "); + } + } + + this.Print(new XivChatEntry + { + Message = builder.Build().Append(message), + Type = channel, + }); + } + private void HandlePopulateItemLinkDetour(IntPtr linkObjectPtr, IntPtr itemInfoPtr) { try @@ -232,7 +251,7 @@ public sealed class ChatGui : IDisposable, IServiceType, IChatGui } } - private IntPtr HandlePrintMessageDetour(IntPtr manager, XivChatType chattype, IntPtr pSenderName, IntPtr pMessage, uint senderid, IntPtr parameter) + private IntPtr HandlePrintMessageDetour(IntPtr manager, XivChatType chatType, IntPtr pSenderName, IntPtr pMessage, uint senderId, IntPtr parameter) { var retVal = IntPtr.Zero; @@ -259,13 +278,13 @@ public sealed class ChatGui : IDisposable, IServiceType, IChatGui // Call events var isHandled = false; - var invocationList = this.CheckMessageHandled.GetInvocationList(); + var invocationList = this.CheckMessageHandled!.GetInvocationList(); foreach (var @delegate in invocationList) { try { var messageHandledDelegate = @delegate as IChatGui.OnCheckMessageHandledDelegate; - messageHandledDelegate!.Invoke(chattype, senderid, ref parsedSender, ref parsedMessage, ref isHandled); + messageHandledDelegate!.Invoke(chatType, senderId, ref parsedSender, ref parsedMessage, ref isHandled); } catch (Exception e) { @@ -275,13 +294,13 @@ public sealed class ChatGui : IDisposable, IServiceType, IChatGui if (!isHandled) { - invocationList = this.ChatMessage.GetInvocationList(); + invocationList = this.ChatMessage!.GetInvocationList(); foreach (var @delegate in invocationList) { try { var messageHandledDelegate = @delegate as IChatGui.OnMessageDelegate; - messageHandledDelegate!.Invoke(chattype, senderid, ref parsedSender, ref parsedMessage, ref isHandled); + messageHandledDelegate!.Invoke(chatType, senderId, ref parsedSender, ref parsedMessage, ref isHandled); } catch (Exception e) { @@ -324,12 +343,12 @@ public sealed class ChatGui : IDisposable, IServiceType, IChatGui // Print the original chat if it's handled. if (isHandled) { - this.ChatMessageHandled?.Invoke(chattype, senderid, parsedSender, parsedMessage); + this.ChatMessageHandled?.Invoke(chatType, senderId, parsedSender, parsedMessage); } else { - retVal = this.printMessageHook.Original(manager, chattype, senderPtr, messagePtr, senderid, parameter); - this.ChatMessageUnhandled?.Invoke(chattype, senderid, parsedSender, parsedMessage); + retVal = this.printMessageHook.Original(manager, chatType, senderPtr, messagePtr, senderId, parameter); + this.ChatMessageUnhandled?.Invoke(chatType, senderId, parsedSender, parsedMessage); } if (this.baseAddress == IntPtr.Zero) @@ -341,7 +360,7 @@ public sealed class ChatGui : IDisposable, IServiceType, IChatGui catch (Exception ex) { Log.Error(ex, "Exception on OnChatMessage hook."); - retVal = this.printMessageHook.Original(manager, chattype, pSenderName, pMessage, senderid, parameter); + retVal = this.printMessageHook.Original(manager, chatType, pSenderName, pMessage, senderId, parameter); } return retVal; @@ -373,10 +392,10 @@ public sealed class ChatGui : IDisposable, IServiceType, IChatGui var linkPayload = payloads[0]; if (linkPayload is DalamudLinkPayload link) { - if (this.dalamudLinkHandlers.ContainsKey((link.Plugin, link.CommandId))) + if (this.dalamudLinkHandlers.TryGetValue((link.Plugin, link.CommandId), out var value)) { Log.Verbose($"Sending DalamudLink to {link.Plugin}: {link.CommandId}"); - this.dalamudLinkHandlers[(link.Plugin, link.CommandId)].Invoke(link.CommandId, new SeString(payloads)); + value.Invoke(link.CommandId, new SeString(payloads)); } else { @@ -390,3 +409,88 @@ public sealed class ChatGui : IDisposable, IServiceType, IChatGui } } } + +/// +/// Plugin scoped version of ChatGui. +/// +[PluginInterface] +[InterfaceVersion("1.0")] +[ServiceManager.ScopedService] +#pragma warning disable SA1015 +[ResolveVia] +#pragma warning restore SA1015 +internal class ChatGuiPluginScoped : IDisposable, IServiceType, IChatGui +{ + [ServiceManager.ServiceDependency] + private readonly ChatGui chatGuiService = Service.Get(); + + /// + /// Initializes a new instance of the class. + /// + internal ChatGuiPluginScoped() + { + this.chatGuiService.ChatMessage += this.OnMessageForward; + this.chatGuiService.CheckMessageHandled += this.OnCheckMessageForward; + this.chatGuiService.ChatMessageHandled += this.OnMessageHandledForward; + this.chatGuiService.ChatMessageUnhandled += this.OnMessageUnhandledForward; + } + + /// + public event IChatGui.OnMessageDelegate? ChatMessage; + + /// + public event IChatGui.OnCheckMessageHandledDelegate? CheckMessageHandled; + + /// + public event IChatGui.OnMessageHandledDelegate? ChatMessageHandled; + + /// + public event IChatGui.OnMessageUnhandledDelegate? ChatMessageUnhandled; + + /// + public int LastLinkedItemId => this.chatGuiService.LastLinkedItemId; + + /// + public byte LastLinkedItemFlags => this.chatGuiService.LastLinkedItemFlags; + + /// + public void Dispose() + { + this.chatGuiService.ChatMessage -= this.OnMessageForward; + this.chatGuiService.CheckMessageHandled -= this.OnCheckMessageForward; + this.chatGuiService.ChatMessageHandled -= this.OnMessageHandledForward; + this.chatGuiService.ChatMessageUnhandled -= this.OnMessageUnhandledForward; + } + + /// + public void Print(XivChatEntry chat) + => this.chatGuiService.Print(chat); + + /// + public void Print(string message, string? messageTag = null, ushort? tagColor = null) + => this.chatGuiService.Print(message, messageTag, tagColor); + + /// + public void Print(SeString message, string? messageTag = null, ushort? tagColor = null) + => this.chatGuiService.Print(message, messageTag, tagColor); + + /// + public void PrintError(string message, string? messageTag = null, ushort? tagColor = null) + => this.chatGuiService.PrintError(message, messageTag, tagColor); + + /// + public void PrintError(SeString message, string? messageTag = null, ushort? tagColor = null) + => this.chatGuiService.PrintError(message, messageTag, tagColor); + + private void OnMessageForward(XivChatType type, uint senderId, ref SeString sender, ref SeString message, ref bool isHandled) + => this.ChatMessage?.Invoke(type, senderId, ref sender, ref message, ref isHandled); + + private void OnCheckMessageForward(XivChatType type, uint senderId, ref SeString sender, ref SeString message, ref bool isHandled) + => this.CheckMessageHandled?.Invoke(type, senderId, ref sender, ref message, ref isHandled); + + private void OnMessageHandledForward(XivChatType type, uint senderId, SeString sender, SeString message) + => this.ChatMessageHandled?.Invoke(type, senderId, sender, message); + + private void OnMessageUnhandledForward(XivChatType type, uint senderId, SeString sender, SeString message) + => this.ChatMessageUnhandled?.Invoke(type, senderId, sender, message); +} diff --git a/Dalamud/Plugin/Internal/PluginManager.cs b/Dalamud/Plugin/Internal/PluginManager.cs index b160038d7..691d5f729 100644 --- a/Dalamud/Plugin/Internal/PluginManager.cs +++ b/Dalamud/Plugin/Internal/PluginManager.cs @@ -285,7 +285,7 @@ internal partial class PluginManager : IDisposable, IServiceType if (updateMetadata is { Count: > 0 }) { - chatGui.PrintChat(new XivChatEntry + chatGui.Print(new XivChatEntry { Message = new SeString(new List() { @@ -308,7 +308,7 @@ internal partial class PluginManager : IDisposable, IServiceType } else { - chatGui.PrintChat(new XivChatEntry + chatGui.Print(new XivChatEntry { Message = Locs.DalamudPluginUpdateFailed(metadata.Name, metadata.Version), Type = XivChatType.Urgent, diff --git a/Dalamud/Plugin/Services/IChatGui.cs b/Dalamud/Plugin/Services/IChatGui.cs index 1d71bb886..bafdabbb5 100644 --- a/Dalamud/Plugin/Services/IChatGui.cs +++ b/Dalamud/Plugin/Services/IChatGui.cs @@ -78,42 +78,40 @@ public interface IChatGui public byte LastLinkedItemFlags { get; } /// - /// Queue a chat message. While method is named as PrintChat, it only add a entry to the queue, - /// later to be processed when UpdateQueue() is called. + /// Queue a chat message. Dalamud will send queued messages on the next framework event. /// /// A message to send. - public void PrintChat(XivChatEntry chat); + public void Print(XivChatEntry chat); /// - /// Queue a chat message. While method is named as PrintChat (it calls it internally), it only add a entry to the queue, - /// later to be processed when UpdateQueue() is called. + /// Queue a chat message. Dalamud will send queued messages on the next framework event. /// /// A message to send. - public void Print(string message); + /// String to prepend message with "[messageTag] ". + /// Color to display the message tag with. + public void Print(string message, string? messageTag = null, ushort? tagColor = null); /// - /// Queue a chat message. While method is named as PrintChat (it calls it internally), it only add a entry to the queue, - /// later to be processed when UpdateQueue() is called. + /// Queue a chat message. Dalamud will send queued messages on the next framework event. /// /// A message to send. - public void Print(SeString message); + /// String to prepend message with "[messageTag] ". + /// Color to display the message tag with. + public void Print(SeString message, string? messageTag = null, ushort? tagColor = null); /// - /// Queue an error chat message. While method is named as PrintChat (it calls it internally), it only add a entry to - /// the queue, later to be processed when UpdateQueue() is called. + /// Queue a chat message. Dalamud will send queued messages on the next framework event. /// /// A message to send. - public void PrintError(string message); + /// String to prepend message with "[messageTag] ". + /// Color to display the message tag with. + public void PrintError(string message, string? messageTag = null, ushort? tagColor = null); /// - /// Queue an error chat message. While method is named as PrintChat (it calls it internally), it only add a entry to - /// the queue, later to be processed when UpdateQueue() is called. + /// Queue a chat message. Dalamud will send queued messages on the next framework event. /// /// A message to send. - public void PrintError(SeString message); - - /// - /// Process a chat queue. - /// - public void UpdateQueue(); + /// String to prepend message with "[messageTag] ". + /// Color to display the message tag with. + public void PrintError(SeString message, string? messageTag = null, ushort? tagColor = null); }