Handle temp chat channel switching via hotkeys

This commit is contained in:
Loporrit
2025-07-25 21:22:31 +00:00
parent ec67d36d6f
commit 4b6978c1c7

View File

@@ -63,10 +63,36 @@ public unsafe sealed class GameChatHooks : IDisposable
DetourName = nameof(ShouldDoNameLookupDetour) DetourName = nameof(ShouldDoNameLookupDetour)
)] )]
private Hook<ShouldDoNameLookupDelegate>? ShouldDoNameLookupHook { get; init; } private Hook<ShouldDoNameLookupDelegate>? ShouldDoNameLookupHook { get; init; }
// Temporary chat channel change (via hotkey)
private delegate ulong TempChatChannelDelegate(RaptureShellModule* module, uint x, uint y, ulong z);
[Signature(
"48 89 5C 24 ?? 48 89 6C 24 ?? 48 89 74 24 ?? 57 48 83 EC ?? 83 B9 ?? ?? ?? ?? ?? 49 8B F9 41 8B F0",
DetourName = nameof(TempChatChannelDetour)
)]
private Hook<TempChatChannelDelegate>? TempChatChannelHook { get; init; }
// Temporary tell target change (via hotkey)
// Client::UI::Shell::RaptureShellModule::SetContextTellTargetInForay
private delegate ulong TempTellTargetDelegate(RaptureShellModule* module, ulong a, ulong b, ulong c, ushort d, ulong e, ulong f, ushort g);
[Signature(
"48 89 5C 24 ?? 48 89 6C 24 ?? 48 89 74 24 ?? 57 48 83 EC ?? 83 B9 ?? ?? ?? ?? ?? 41 0F B7 F9",
DetourName = nameof(TempTellTargetDetour)
)]
private Hook<TempTellTargetDelegate>? TempTellTargetHook { get; init; }
// Called every frame while the chat bar is not focused
private delegate void UnfocusTickDelegate(RaptureShellModule* module);
[Signature(
"40 53 48 83 EC ?? 83 B9 ?? ?? ?? ?? ?? 48 8B D9 0F 84 ?? ?? ?? ?? 48 8D 91",
DetourName = nameof(UnfocusTickDetour)
)]
private Hook<UnfocusTickDelegate>? UnfocusTickHook { get; init; }
#pragma warning restore CS0649 #pragma warning restore CS0649
#endregion #endregion
private ChatChannelOverride? _chatChannelOverride; private ChatChannelOverride? _chatChannelOverride;
private ChatChannelOverride? _chatChannelOverrideTempBuffer;
private bool _shouldForceNameLookup = false; private bool _shouldForceNameLookup = false;
private DateTime _nextMessageIsReply = DateTime.UnixEpoch; private DateTime _nextMessageIsReply = DateTime.UnixEpoch;
@@ -76,7 +102,27 @@ public unsafe sealed class GameChatHooks : IDisposable
get => _chatChannelOverride; get => _chatChannelOverride;
set { set {
_chatChannelOverride = value; _chatChannelOverride = value;
this._shouldForceNameLookup = true; _shouldForceNameLookup = true;
}
}
private void StashChatChannel()
{
if (_chatChannelOverride != null)
{
_logger.LogTrace("Stashing chat channel");
_chatChannelOverrideTempBuffer = _chatChannelOverride;
ChatChannelOverride = null;
}
}
private void UnstashChatChannel()
{
if (_chatChannelOverrideTempBuffer != null)
{
_logger.LogTrace("Unstashing chat channel");
ChatChannelOverride = _chatChannelOverrideTempBuffer;
_chatChannelOverrideTempBuffer = null;
} }
} }
@@ -87,18 +133,24 @@ public unsafe sealed class GameChatHooks : IDisposable
logger.LogInformation("Initializing GameChatHooks"); logger.LogInformation("Initializing GameChatHooks");
gameInteropProvider.InitializeFromAttributes(this); gameInteropProvider.InitializeFromAttributes(this);
this.SendMessageHook?.Enable(); SendMessageHook?.Enable();
this.SetChatChannelHook?.Enable(); SetChatChannelHook?.Enable();
this.ChangeChannelNameHook?.Enable(); ChangeChannelNameHook?.Enable();
this.ShouldDoNameLookupHook?.Enable(); ShouldDoNameLookupHook?.Enable();
TempChatChannelHook?.Enable();
TempTellTargetHook?.Enable();
UnfocusTickHook?.Enable();
} }
public void Dispose() public void Dispose()
{ {
this.SendMessageHook?.Dispose(); SendMessageHook?.Dispose();
this.SetChatChannelHook?.Dispose(); SetChatChannelHook?.Dispose();
this.ChangeChannelNameHook?.Dispose(); ChangeChannelNameHook?.Dispose();
this.ShouldDoNameLookupHook?.Dispose(); ShouldDoNameLookupHook?.Dispose();
TempChatChannelHook?.Dispose();
TempTellTargetHook?.Dispose();
UnfocusTickHook?.Dispose();
} }
private void SendMessageDetour(ShellCommandModule* thisPtr, Utf8String* message, UIModule* uiModule) private void SendMessageDetour(ShellCommandModule* thisPtr, Utf8String* message, UIModule* uiModule)
@@ -145,7 +197,7 @@ public unsafe sealed class GameChatHooks : IDisposable
_nextMessageIsReply = utcNow + TimeSpan.FromMilliseconds(100); _nextMessageIsReply = utcNow + TimeSpan.FromMilliseconds(100);
// 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 || this._chatChannelOverride == null) if (isCommand || _chatChannelOverride == null)
{ {
SendMessageHook!.OriginalDisposeSafe(thisPtr, message, uiModule); SendMessageHook!.OriginalDisposeSafe(thisPtr, message, uiModule);
return; return;
@@ -155,11 +207,11 @@ public unsafe sealed class GameChatHooks : IDisposable
// 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 pronounModule = UIModule.Instance()->GetPronounModule();
var chatString1 = pronounModule->ProcessString(message, true); var chatString1 = pronounModule->ProcessString(message, true);
var chatString2 = this._processStringStep2(pronounModule, chatString1, 1); var chatString2 = _processStringStep2(pronounModule, chatString1, 1);
var chatBytes = MemoryHelper.ReadRaw((nint)chatString2->StringPtr.Value, chatString2->Length); var chatBytes = MemoryHelper.ReadRaw((nint)chatString2->StringPtr.Value, chatString2->Length);
if (chatBytes.Length > 0) if (chatBytes.Length > 0)
this._chatChannelOverride.ChatMessageHandler?.Invoke(chatBytes); _chatChannelOverride.ChatMessageHandler?.Invoke(chatBytes);
} }
catch (Exception e) catch (Exception e)
{ {
@@ -171,10 +223,10 @@ public unsafe sealed class GameChatHooks : IDisposable
{ {
try try
{ {
if (this._chatChannelOverride != null) if (_chatChannelOverride != null)
{ {
this._chatChannelOverride = null; _chatChannelOverride = null;
this._shouldForceNameLookup = true; _shouldForceNameLookup = true;
} }
} }
catch (Exception e) catch (Exception e)
@@ -185,6 +237,32 @@ public unsafe sealed class GameChatHooks : IDisposable
SetChatChannelHook!.OriginalDisposeSafe(module, channel); SetChatChannelHook!.OriginalDisposeSafe(module, channel);
} }
private ulong TempChatChannelDetour(RaptureShellModule* module, uint x, uint y, ulong z)
{
var result = TempChatChannelHook!.OriginalDisposeSafe(module, x, y, z);
if (result != 0)
StashChatChannel();
return result;
}
private ulong TempTellTargetDetour(RaptureShellModule* module, ulong a, ulong b, ulong c, ushort d, ulong e, ulong f, ushort g)
{
var result = TempTellTargetHook!.OriginalDisposeSafe(module, a, b, c, d, e, f, g);
if (result != 0)
StashChatChannel();
return result;
}
private void UnfocusTickDetour(RaptureShellModule* module)
{
UnfocusTickHook!.OriginalDisposeSafe(module);
UnstashChatChannel();
}
private byte* ChangeChannelNameDetour(AgentChatLog* agent) private byte* ChangeChannelNameDetour(AgentChatLog* agent)
{ {
var originalResult = ChangeChannelNameHook!.OriginalDisposeSafe(agent); var originalResult = ChangeChannelNameHook!.OriginalDisposeSafe(agent);
@@ -192,9 +270,9 @@ public unsafe sealed class GameChatHooks : IDisposable
try try
{ {
// Replace the chat channel name on the UI if active // Replace the chat channel name on the UI if active
if (this._chatChannelOverride != null) if (_chatChannelOverride != null)
{ {
agent->ChannelLabel.SetString(this._chatChannelOverride.ChannelName); agent->ChannelLabel.SetString(_chatChannelOverride.ChannelName);
} }
} }
catch (Exception e) catch (Exception e)
@@ -212,7 +290,7 @@ public unsafe sealed class GameChatHooks : IDisposable
try try
{ {
// Force the chat channel name to update when required // Force the chat channel name to update when required
if (this._shouldForceNameLookup) if (_shouldForceNameLookup)
{ {
_shouldForceNameLookup = false; _shouldForceNameLookup = false;
return 1; return 1;