Process chat messages correctly when using /ss commands
This commit is contained in:
		| @@ -10,6 +10,7 @@ using FFXIVClientStructs.FFXIV.Client.UI.Agent; | |||||||
| using FFXIVClientStructs.FFXIV.Client.UI.Misc; | using FFXIVClientStructs.FFXIV.Client.UI.Misc; | ||||||
| using FFXIVClientStructs.FFXIV.Client.UI.Shell; | using FFXIVClientStructs.FFXIV.Client.UI.Shell; | ||||||
| using FFXIVClientStructs.FFXIV.Component.Shell; | using FFXIVClientStructs.FFXIV.Component.Shell; | ||||||
|  | using MareSynchronos.Services; | ||||||
| using Microsoft.Extensions.Logging; | using Microsoft.Extensions.Logging; | ||||||
|  |  | ||||||
| namespace MareSynchronos.Interop; | namespace MareSynchronos.Interop; | ||||||
| @@ -25,6 +26,7 @@ public unsafe sealed class GameChatHooks : IDisposable | |||||||
|     // Based on https://git.anna.lgbt/anna/ExtraChat/src/branch/main/client/ExtraChat/GameFunctions.cs |     // Based on https://git.anna.lgbt/anna/ExtraChat/src/branch/main/client/ExtraChat/GameFunctions.cs | ||||||
|  |  | ||||||
|     private readonly ILogger<GameChatHooks> _logger; |     private readonly ILogger<GameChatHooks> _logger; | ||||||
|  |     private readonly Action<int, byte[]> _ssCommandHandler; | ||||||
|  |  | ||||||
|     #region signatures |     #region signatures | ||||||
|     #pragma warning disable CS0649 |     #pragma warning disable CS0649 | ||||||
| @@ -126,9 +128,10 @@ public unsafe sealed class GameChatHooks : IDisposable | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public GameChatHooks(ILogger<GameChatHooks> logger, IGameInteropProvider gameInteropProvider) |     public GameChatHooks(ILogger<GameChatHooks> logger, IGameInteropProvider gameInteropProvider, Action<int, byte[]> ssCommandHandler) | ||||||
|     { |     { | ||||||
|         _logger = logger; |         _logger = logger; | ||||||
|  |         _ssCommandHandler = ssCommandHandler; | ||||||
|  |  | ||||||
|         logger.LogInformation("Initializing GameChatHooks"); |         logger.LogInformation("Initializing GameChatHooks"); | ||||||
|         gameInteropProvider.InitializeFromAttributes(this); |         gameInteropProvider.InitializeFromAttributes(this); | ||||||
| @@ -153,6 +156,14 @@ public unsafe sealed class GameChatHooks : IDisposable | |||||||
|         UnfocusTickHook?.Dispose(); |         UnfocusTickHook?.Dispose(); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     private byte[] ProcessChatMessage(Utf8String* message) | ||||||
|  |     { | ||||||
|  |         var pronounModule = UIModule.Instance()->GetPronounModule(); | ||||||
|  |         var chatString1 = pronounModule->ProcessString(message, true); | ||||||
|  |         var chatString2 = _processStringStep2(pronounModule, chatString1, 1); | ||||||
|  |         return MemoryHelper.ReadRaw((nint)chatString2->StringPtr.Value, chatString2->Length); | ||||||
|  |     } | ||||||
|  |  | ||||||
|     private void SendMessageDetour(ShellCommandModule* thisPtr, Utf8String* message, UIModule* uiModule) |     private void SendMessageDetour(ShellCommandModule* thisPtr, Utf8String* message, UIModule* uiModule) | ||||||
|     { |     { | ||||||
|         try |         try | ||||||
| @@ -196,6 +207,23 @@ public unsafe sealed class GameChatHooks : IDisposable | |||||||
|             if (isReply) |             if (isReply) | ||||||
|                 _nextMessageIsReply = utcNow + TimeSpan.FromMilliseconds(100); |                 _nextMessageIsReply = utcNow + TimeSpan.FromMilliseconds(100); | ||||||
|  |  | ||||||
|  |             // If it is a command, check if it begins with /ss first so we can handle the message directly | ||||||
|  |             // Letting Dalamud handle the commands causes all of the special payloads to be dropped | ||||||
|  |             if (isCommand && messageSpan.StartsWith(System.Text.Encoding.ASCII.GetBytes("/ss"))) | ||||||
|  |             { | ||||||
|  |                 for (int i = 1; i <= ChatService.CommandMaxNumber; ++i) | ||||||
|  |                 { | ||||||
|  |                     var cmdString = $"/ss{i} "; | ||||||
|  |                     if (messageSpan.StartsWith(System.Text.Encoding.ASCII.GetBytes(cmdString))) | ||||||
|  |                     { | ||||||
|  |                         var ssChatBytes = ProcessChatMessage(message); | ||||||
|  |                         ssChatBytes = ssChatBytes.Skip(cmdString.Length).ToArray(); | ||||||
|  |                         _ssCommandHandler?.Invoke(i, ssChatBytes); | ||||||
|  |                         return; | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |  | ||||||
|             // If not a command, or no override is set, then call the original chat handler |             // If not a command, or no override is set, then call the original chat handler | ||||||
|             if (isCommand || _chatChannelOverride == null) |             if (isCommand || _chatChannelOverride == null) | ||||||
|             { |             { | ||||||
| @@ -205,10 +233,7 @@ public unsafe sealed class GameChatHooks : IDisposable | |||||||
|  |  | ||||||
|             // Otherwise, the text is to be sent to the emulated chat channel handler |             // Otherwise, the text is to be sent to the emulated chat channel handler | ||||||
|             // The chat input string is rendered in to a payload for display first |             // The chat input string is rendered in to a payload for display first | ||||||
|             var pronounModule = UIModule.Instance()->GetPronounModule(); |             var chatBytes = ProcessChatMessage(message); | ||||||
|             var chatString1 = pronounModule->ProcessString(message, true); |  | ||||||
|             var chatString2 = _processStringStep2(pronounModule, chatString1, 1); |  | ||||||
|             var chatBytes = MemoryHelper.ReadRaw((nint)chatString2->StringPtr.Value, chatString2->Length); |  | ||||||
|  |  | ||||||
|             if (chatBytes.Length > 0) |             if (chatBytes.Length > 0) | ||||||
|                 _chatChannelOverride.ChatMessageHandler?.Invoke(chatBytes); |                 _chatChannelOverride.ChatMessageHandler?.Invoke(chatBytes); | ||||||
|   | |||||||
| @@ -45,7 +45,20 @@ public class ChatService : DisposableMediatorSubscriberBase | |||||||
|         Mediator.Subscribe<UserChatMsgMessage>(this, HandleUserChat); |         Mediator.Subscribe<UserChatMsgMessage>(this, HandleUserChat); | ||||||
|         Mediator.Subscribe<GroupChatMsgMessage>(this, HandleGroupChat); |         Mediator.Subscribe<GroupChatMsgMessage>(this, HandleGroupChat); | ||||||
|  |  | ||||||
|         _gameChatHooks = new(() => new GameChatHooks(loggerFactory.CreateLogger<GameChatHooks>(), gameInteropProvider)); |         _gameChatHooks = new(() => new GameChatHooks(loggerFactory.CreateLogger<GameChatHooks>(), gameInteropProvider, SendChatShell)); | ||||||
|  |  | ||||||
|  |         // Initialize chat hooks in advance | ||||||
|  |         _ = Task.Run(() => | ||||||
|  |         { | ||||||
|  |             try | ||||||
|  |             { | ||||||
|  |                 _ = _gameChatHooks.Value; | ||||||
|  |             } | ||||||
|  |             catch (Exception ex) | ||||||
|  |             { | ||||||
|  |                 _logger.LogError(ex, "Failed to initialize chat hooks"); | ||||||
|  |             } | ||||||
|  |         }); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     protected override void Dispose(bool disposing) |     protected override void Dispose(bool disposing) | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Loporrit
					Loporrit