This commit is contained in:
Infi
2024-08-20 23:14:52 +02:00
parent 16a16b1f12
commit 117d9fc45c
7 changed files with 114 additions and 45 deletions
+9 -2
View File
@@ -22,7 +22,14 @@ tags:
- Chat - Chat
- Replacement - Replacement
changelog: |- changelog: |-
**Misc** **Added**
- Implement 24-hour clock timestamp option [default false] - Implement 24-hour clock timestamp option [default false]
- Fix hide activity channels not being saved across sessions
**Fixes**
- Bozja/Eureka tells should work again
- Invites in Bozja/Eureka should work again
- Added a notification that warns about fails duo missing player id
- Hide activity channels are now saved across sessions
**Misc**
- Loc updates - Loc updates
+57 -38
View File
@@ -30,20 +30,23 @@ internal sealed unsafe class Chat : IDisposable
private readonly delegate* unmanaged<RaptureLogModule*, ushort, Utf8String*, Utf8String*, ulong, ulong, ushort, byte, int, byte, void> PrintTellNative = null!; private readonly delegate* unmanaged<RaptureLogModule*, ushort, Utf8String*, Utf8String*, ulong, ulong, ushort, byte, int, byte, void> PrintTellNative = null!;
[Signature("E8 ?? ?? ?? ?? 48 8D 4C 24 ?? E8 ?? ?? ?? ?? 48 8D 8C 24 ?? ?? ?? ?? E8 ?? ?? ?? ?? B0 01")] [Signature("E8 ?? ?? ?? ?? 48 8D 4C 24 ?? E8 ?? ?? ?? ?? 48 8D 8C 24 ?? ?? ?? ?? E8 ?? ?? ?? ?? B0 01")]
private readonly delegate* unmanaged<NetworkModule*, ulong, ushort, Utf8String*, Utf8String*, ushort, ushort, bool> SendTellNative = null!; private readonly delegate* unmanaged<NetworkModule*, ulong, ushort, Utf8String*, Utf8String*, ushort, ushort, byte> SendTellNative = null!;
// Client::UI::AddonChatLog.OnRefresh // Client::UI::AddonChatLog.OnRefresh
[Signature("40 53 56 57 48 81 EC ?? ?? ?? ?? 48 8B 05 ?? ?? ?? ?? 48 33 C4 48 89 84 24 ?? ?? ?? ?? 49 8B F0 8B FA", DetourName = nameof(ChatLogRefreshDetour))] [Signature("40 53 56 57 48 81 EC ?? ?? ?? ?? 48 8B 05 ?? ?? ?? ?? 48 33 C4 48 89 84 24 ?? ?? ?? ?? 49 8B F0 8B FA", DetourName = nameof(ChatLogRefreshDetour))]
private Hook<ChatLogRefreshDelegate>? ChatLogRefreshHook { get; init; } private Hook<ChatLogRefreshDelegate>? ChatLogRefreshHook { get; init; }
private delegate byte ChatLogRefreshDelegate(nint log, ushort eventId, AtkValue* value); private delegate byte ChatLogRefreshDelegate(nint log, ushort eventId, AtkValue* value);
// Replace with CS version later
[Signature("E8 ?? ?? ?? ?? EB 81 48 8B 1D", DetourName = nameof(ContextMenuTellInForayDetour))]
private Hook<ContextMenuTellInForayDelegate>? ContextMenuTellInForayHook { get; set; }
private delegate void ContextMenuTellInForayDelegate(RaptureShellModule* module, Utf8String* playerName, Utf8String* worldName, ushort worldId, ulong accountId, ulong contentId, ushort reason);
private Hook<AgentChatLog.Delegates.ChangeChannelName> ChangeChannelNameHook { get; init; } private Hook<AgentChatLog.Delegates.ChangeChannelName> ChangeChannelNameHook { get; init; }
private Hook<RaptureShellModule.Delegates.ReplyInSelectedChatMode>? ReplyInSelectedChatModeHook { get; init; } private Hook<RaptureShellModule.Delegates.ReplyInSelectedChatMode>? ReplyInSelectedChatModeHook { get; init; }
private Hook<RaptureShellModule.Delegates.SetContextTellTarget>? SetChatLogTellTargetHook { get; init; } private Hook<RaptureShellModule.Delegates.SetContextTellTarget>? SetChatLogTellTargetHook { get; init; }
private Hook<RaptureShellModule.Delegates.SetContextTellTargetInForay>? EurekaContextMenuTellHook { get; init; }
// Pointers // Pointers
[Signature("48 8D 35 ?? ?? ?? ?? 8B 05", ScanType = ScanType.StaticAddress)] [Signature("48 8D 35 ?? ?? ?? ?? 8B 05", ScanType = ScanType.StaticAddress)]
private readonly char* CurrentCharacter = null!; private readonly char* CurrentCharacter = null!;
@@ -75,6 +78,7 @@ internal sealed unsafe class Chat : IDisposable
Plugin.GameInteropProvider.InitializeFromAttributes(this); Plugin.GameInteropProvider.InitializeFromAttributes(this);
ChatLogRefreshHook?.Enable(); ChatLogRefreshHook?.Enable();
ContextMenuTellInForayHook?.Enable();
ChangeChannelNameHook = Plugin.GameInteropProvider.HookFromAddress<AgentChatLog.Delegates.ChangeChannelName>(AgentChatLog.MemberFunctionPointers.ChangeChannelName, ChangeChannelNameDetour); ChangeChannelNameHook = Plugin.GameInteropProvider.HookFromAddress<AgentChatLog.Delegates.ChangeChannelName>(AgentChatLog.MemberFunctionPointers.ChangeChannelName, ChangeChannelNameDetour);
ChangeChannelNameHook.Enable(); ChangeChannelNameHook.Enable();
@@ -85,9 +89,6 @@ internal sealed unsafe class Chat : IDisposable
SetChatLogTellTargetHook = Plugin.GameInteropProvider.HookFromAddress<RaptureShellModule.Delegates.SetContextTellTarget>(RaptureShellModule.MemberFunctionPointers.SetContextTellTarget, SetContextTellTarget); SetChatLogTellTargetHook = Plugin.GameInteropProvider.HookFromAddress<RaptureShellModule.Delegates.SetContextTellTarget>(RaptureShellModule.MemberFunctionPointers.SetContextTellTarget, SetContextTellTarget);
SetChatLogTellTargetHook.Enable(); SetChatLogTellTargetHook.Enable();
// EurekaContextMenuTellHook = Plugin.GameInteropProvider.HookFromAddress<RaptureShellModule.Delegates.SetContextTellTargetInForay>(RaptureShellModule.MemberFunctionPointers.SetContextTellTargetInForay, SetContextTellTargetInForay);
// EurekaContextMenuTellHook.Enable();
Plugin.ClientState.Login += Login; Plugin.ClientState.Login += Login;
Login(); Login();
} }
@@ -100,7 +101,7 @@ internal sealed unsafe class Chat : IDisposable
ReplyInSelectedChatModeHook?.Dispose(); ReplyInSelectedChatModeHook?.Dispose();
ChangeChannelNameHook?.Dispose(); ChangeChannelNameHook?.Dispose();
ChatLogRefreshHook?.Dispose(); ChatLogRefreshHook?.Dispose();
EurekaContextMenuTellHook?.Dispose(); ContextMenuTellInForayHook?.Dispose();
} }
internal string? GetLinkshellName(uint idx) internal string? GetLinkshellName(uint idx)
@@ -213,6 +214,11 @@ internal sealed unsafe class Chat : IDisposable
try try
{ {
// We already called this function once, so we skip the duplicated call
// Also return the original value here so that vanilla chat receives all information
if (Plugin.ChatLogWindow.TellSpecial)
return ChatLogRefreshHook!.Original(log, eventId, value);
Plugin.ChatLogWindow.Activated(new ChatActivatedArgs(new ChannelSwitchInfo(null)) { AddIfNotPresent = addIfNotPresent, }); Plugin.ChatLogWindow.Activated(new ChatActivatedArgs(new ChannelSwitchInfo(null)) { AddIfNotPresent = addIfNotPresent, });
} }
catch (Exception ex) catch (Exception ex)
@@ -294,35 +300,34 @@ internal sealed unsafe class Chat : IDisposable
return SetChatLogTellTargetHook!.Original(a1, playerName, worldName, worldId, accountId, contentId, reason, setChatType); return SetChatLogTellTargetHook!.Original(a1, playerName, worldName, worldId, accountId, contentId, reason, setChatType);
} }
// private void SetContextTellTargetInForay(RaptureShellModule* a1, Utf8String* playerName, Utf8String* worldName, ushort worldId, ulong accountId, ulong contentId, ushort reason) private void ContextMenuTellInForayDetour(RaptureShellModule* a1, Utf8String* playerName, Utf8String* worldName, ushort worldId, ulong accountId, ulong contentId, ushort reason)
// { {
// Plugin.Log.Information($"SetContextTellTargetInForay"); if (!UsesTellTempChannel)
// if (!UsesTellTempChannel) {
// { UsesTellTempChannel = true;
// UsesTellTempChannel = true; PreviousChannel = Channel.Channel;
// PreviousChannel = Channel.Channel; }
// }
// if (playerName != null)
// if (playerName != null) {
// { try
// try {
// { var target = new TellTarget(playerName->ToString(), worldId, contentId, (TellReason) reason);
// Plugin.Log.Information($"Name {playerName->ToString()} World {worldName->ToString()} WorldId {worldId} accountId {accountId} ContentId {contentId} Reason {reason} rapture reason {a1->TellReason}"); Plugin.ChatLogWindow.Activated(new ChatActivatedArgs(new ChannelSwitchInfo(InputChannel.Tell))
// var target = new TellTarget(playerName->ToString(), worldId, contentId, (TellReason) reason); {
// Activated?.Invoke(new ChatActivatedArgs(new ChannelSwitchInfo(InputChannel.Tell)) TellReason = (TellReason) reason,
// { TellTarget = target,
// TellReason = (TellReason) reason, TellSpecial = true,
// TellTarget = target, });
// }); }
// } catch (Exception ex)
// catch (Exception ex) {
// { Plugin.Log.Error(ex, "Error in chat Activated event");
// Plugin.Log.Error(ex, "Error in chat Activated event"); }
// } }
// }
// ContextMenuTellInForayHook!.Original(a1, playerName, worldName, worldId, accountId, contentId, reason);
// EurekaContextMenuTellHook!.Original(a1, playerName, worldName, worldId, accountId, contentId, reason); }
// }
/// <summary> /// <summary>
/// Returns true if the channel is any non-linkshell channel, or if the /// Returns true if the channel is any non-linkshell channel, or if the
@@ -457,6 +462,16 @@ internal sealed unsafe class Chat : IDisposable
return new TellHistoryInfo(name, world, contentId); return new TellHistoryInfo(name, world, contentId);
} }
internal void SendTellUsingCommandInner(byte[] message)
{
var mes = new Utf8String(message);
RaptureShellModule.Instance()->ExecuteCommandInner(&mes, UIModule.Instance());
RaptureAtkModule.Instance()->ClearFocus(); // Clear the focus of vanilla chat that was still active
mes.Dtor(true);
}
internal void SendTell(TellReason reason, ulong contentId, string name, ushort homeWorld, byte[] message, string rawText) internal void SendTell(TellReason reason, ulong contentId, string name, ushort homeWorld, byte[] message, string rawText)
{ {
var uName = Utf8String.FromString(name); var uName = Utf8String.FromString(name);
@@ -470,15 +485,19 @@ internal sealed unsafe class Chat : IDisposable
var logModule = RaptureLogModule.Instance(); var logModule = RaptureLogModule.Instance();
var networkModule = Framework.Instance()->GetNetworkModuleProxy()->NetworkModule; var networkModule = Framework.Instance()->GetNetworkModuleProxy()->NetworkModule;
// TODO: Remap TellReasons // // TODO: Remap TellReasons
if (reason == TellReason.Direct) if (reason == TellReason.Direct)
reason = TellReason.Friend; reason = TellReason.Friend;
var ok = SendTellNative(networkModule, contentId, homeWorld, uName, encoded, (ushort) reason, homeWorld); var ok = SendTellNative(networkModule, contentId, homeWorld, uName, encoded, (ushort) reason, homeWorld);
if (ok) if (ok == 1)
{
PrintTellNative(logModule, 33, uName, &decodedUtf8String, 0, contentId, homeWorld, 255, 0, 0); PrintTellNative(logModule, 33, uName, &decodedUtf8String, 0, contentId, homeWorld, 255, 0, 0);
}
else else
{
Plugin.ChatGui.PrintError(Language.Chat_SendTell_Error); Plugin.ChatGui.PrintError(Language.Chat_SendTell_Error);
}
encoded->Dtor(true); encoded->Dtor(true);
uName->Dtor(true); uName->Dtor(true);
+17 -5
View File
@@ -1,10 +1,12 @@
using ChatTwo.Resources;
using ChatTwo.Util; using ChatTwo.Util;
using Dalamud.Interface.ImGuiNotification;
using FFXIVClientStructs.FFXIV.Client.UI.Agent; using FFXIVClientStructs.FFXIV.Client.UI.Agent;
using FFXIVClientStructs.FFXIV.Client.UI.Info; using FFXIVClientStructs.FFXIV.Client.UI.Info;
namespace ChatTwo.GameFunctions; namespace ChatTwo.GameFunctions;
internal sealed unsafe class Party internal static unsafe class Party
{ {
internal static void InviteSameWorld(string name, ushort world, ulong contentId) internal static void InviteSameWorld(string name, ushort world, ulong contentId)
{ {
@@ -20,14 +22,24 @@ internal sealed unsafe class Party
// if they're not on that world, it will fail // if they're not on that world, it will fail
// pass 0 and it will work on any world EXCEPT for the world the // pass 0 and it will work on any world EXCEPT for the world the
// current player is on // current player is on
if (contentId != 0) if (contentId == 0)
InfoProxyPartyInvite.Instance()->InviteToPartyContentId(contentId, 0); {
WrapperUtil.AddNotification(Language.PartyInvite_NoId, NotificationType.Warning);
return;
}
InfoProxyPartyInvite.Instance()->InviteToPartyContentId(contentId, 0);
} }
internal static void InviteInInstance(ulong contentId) internal static void InviteInInstance(ulong contentId)
{ {
if (contentId != 0) if (contentId == 0)
InfoProxyPartyInvite.Instance()->InviteToPartyInInstance(contentId); {
WrapperUtil.AddNotification(Language.PartyInvite_NoId, NotificationType.Warning);
return;
}
InfoProxyPartyInvite.Instance()->InviteToPartyInInstance(contentId);
} }
internal static void Kick(string name, ulong contentId) internal static void Kick(string name, ulong contentId)
@@ -7,6 +7,7 @@ internal sealed class ChatActivatedArgs
internal ChannelSwitchInfo ChannelSwitchInfo { get; } internal ChannelSwitchInfo ChannelSwitchInfo { get; }
internal TellReason? TellReason { get; init; } internal TellReason? TellReason { get; init; }
internal TellTarget? TellTarget { get; init; } internal TellTarget? TellTarget { get; init; }
internal bool TellSpecial { get; init; } // specific to Eureka/Bozja/Zadnor
internal ChatActivatedArgs(ChannelSwitchInfo channelSwitchInfo) internal ChatActivatedArgs(ChannelSwitchInfo channelSwitchInfo)
{ {
+9
View File
@@ -3416,6 +3416,15 @@ namespace ChatTwo.Resources {
} }
} }
/// <summary>
/// Looks up a localized string similar to Unable to find ID for this message, please try another one..
/// </summary>
internal static string PartyInvite_NoId {
get {
return ResourceManager.GetString("PartyInvite_NoId", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to Discard. /// Looks up a localized string similar to Discard.
/// </summary> /// </summary>
+3
View File
@@ -964,6 +964,9 @@
<data name="Context_CopySuccess"> <data name="Context_CopySuccess">
<value>Copied message to clipboard</value> <value>Copied message to clipboard</value>
</data> </data>
<data name="PartyInvite_NoId">
<value>Unable to find ID for this message, please try another one.</value>
</data>
<data name="Context_CopyLink" xml:space="preserve"> <data name="Context_CopyLink" xml:space="preserve">
<value>Copy link to clipboard</value> <value>Copy link to clipboard</value>
</data> </data>
+18
View File
@@ -59,6 +59,7 @@ public sealed class ChatLogWindow : Window
private int LastTab { get; set; } private int LastTab { get; set; }
private InputChannel? TempChannel; private InputChannel? TempChannel;
private TellTarget? TellTarget; private TellTarget? TellTarget;
public bool TellSpecial;
private readonly Stopwatch LastResize = new(); private readonly Stopwatch LastResize = new();
private AutoCompleteInfo? AutoCompleteInfo; private AutoCompleteInfo? AutoCompleteInfo;
private bool AutoCompleteOpen; private bool AutoCompleteOpen;
@@ -145,6 +146,8 @@ public sealed class ChatLogWindow : Window
internal void Activated(ChatActivatedArgs args) internal void Activated(ChatActivatedArgs args)
{ {
TellSpecial = args.TellSpecial;
Activate = true; Activate = true;
PlayedClosingSound = false; PlayedClosingSound = false;
if (Plugin.Config.PlaySounds) if (Plugin.Config.PlaySounds)
@@ -857,6 +860,21 @@ public sealed class ChatLogWindow : Window
AddBacklog(trimmed); AddBacklog(trimmed);
InputBacklogIdx = -1; InputBacklogIdx = -1;
if (TellSpecial)
{
var tellBytes = Encoding.UTF8.GetBytes(trimmed);
AutoTranslate.ReplaceWithPayload(ref tellBytes);
Plugin.Functions.Chat.SendTellUsingCommandInner(tellBytes);
TellSpecial = false;
if (TempChannel is InputChannel.Tell)
TellTarget = null;
Chat = string.Empty;
return;
}
if (!trimmed.StartsWith('/')) if (!trimmed.StartsWith('/'))
{ {
if (TellTarget != null) if (TellTarget != null)