forked from aly/qstbak
qstcompanion v1.0.6
This commit is contained in:
parent
5e1e1decc5
commit
ada27cf05b
30 changed files with 3403 additions and 426 deletions
|
|
@ -0,0 +1,197 @@
|
|||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using Dalamud.Game.NativeWrapper;
|
||||
using Dalamud.Hooking;
|
||||
using Dalamud.Plugin.Services;
|
||||
using FFXIVClientStructs.FFXIV.Component.GUI;
|
||||
|
||||
namespace QuestionableCompanion.Services;
|
||||
|
||||
public class ErrorRecoveryService : IDisposable
|
||||
{
|
||||
private delegate char LobbyErrorHandlerDelegate(long a1, long a2, long a3);
|
||||
|
||||
private readonly IPluginLog log;
|
||||
|
||||
private readonly IGameInteropProvider hookProvider;
|
||||
|
||||
private readonly IClientState clientState;
|
||||
|
||||
private readonly IFramework framework;
|
||||
|
||||
private readonly IGameGui gameGui;
|
||||
|
||||
private readonly AutoRetainerIPC? autoRetainerIPC;
|
||||
|
||||
private Hook<LobbyErrorHandlerDelegate>? lobbyErrorHandlerHook;
|
||||
|
||||
private DateTime lastDialogClickTime = DateTime.MinValue;
|
||||
|
||||
public bool IsErrorDisconnect { get; private set; }
|
||||
|
||||
public string? LastDisconnectedCharacter { get; private set; }
|
||||
|
||||
public ErrorRecoveryService(IPluginLog log, IGameInteropProvider hookProvider, IClientState clientState, IFramework framework, IGameGui gameGui, AutoRetainerIPC? autoRetainerIPC = null)
|
||||
{
|
||||
this.log = log;
|
||||
this.hookProvider = hookProvider;
|
||||
this.clientState = clientState;
|
||||
this.framework = framework;
|
||||
this.gameGui = gameGui;
|
||||
this.autoRetainerIPC = autoRetainerIPC;
|
||||
framework.Update += OnFrameworkUpdate;
|
||||
InitializeHook();
|
||||
}
|
||||
|
||||
private void InitializeHook()
|
||||
{
|
||||
try
|
||||
{
|
||||
lobbyErrorHandlerHook = hookProvider.HookFromSignature<LobbyErrorHandlerDelegate>("40 53 48 83 EC 30 48 8B D9 49 8B C8 E8 ?? ?? ?? ?? 8B D0", LobbyErrorHandlerDetour);
|
||||
if (lobbyErrorHandlerHook != null && lobbyErrorHandlerHook.Address != IntPtr.Zero)
|
||||
{
|
||||
lobbyErrorHandlerHook.Enable();
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
private char LobbyErrorHandlerDetour(long a1, long a2, long a3)
|
||||
{
|
||||
try
|
||||
{
|
||||
nint p3 = new IntPtr(a3);
|
||||
byte t1 = Marshal.ReadByte(p3);
|
||||
int num = (((t1 & 0xF) > 0) ? Marshal.ReadInt32(p3 + 8) : 0);
|
||||
_ = 0;
|
||||
if (num != 0)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (autoRetainerIPC != null)
|
||||
{
|
||||
string currentChar = autoRetainerIPC.GetCurrentCharacter();
|
||||
if (!string.IsNullOrEmpty(currentChar))
|
||||
{
|
||||
LastDisconnectedCharacter = currentChar;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
}
|
||||
Marshal.WriteInt64(p3 + 8, 16000L);
|
||||
IsErrorDisconnect = true;
|
||||
if ((t1 & 0xF) > 0)
|
||||
{
|
||||
Marshal.ReadInt32(p3 + 8);
|
||||
}
|
||||
else
|
||||
_ = 0;
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
}
|
||||
return lobbyErrorHandlerHook.Original(a1, a2, a3);
|
||||
}
|
||||
|
||||
private unsafe void OnFrameworkUpdate(IFramework framework)
|
||||
{
|
||||
try
|
||||
{
|
||||
AtkUnitBasePtr dialoguePtr = gameGui.GetAddonByName("Dialogue");
|
||||
if (dialoguePtr == IntPtr.Zero)
|
||||
{
|
||||
return;
|
||||
}
|
||||
AtkUnitBase* dialogueAddon = (AtkUnitBase*)(nint)dialoguePtr;
|
||||
if (dialogueAddon == null || !dialogueAddon->IsVisible || (DateTime.Now - lastDialogClickTime).TotalMilliseconds < 1000.0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
AtkTextNode* textNode = dialogueAddon->GetTextNodeById(3u);
|
||||
if (textNode == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
string text = textNode->NodeText.ToString();
|
||||
if (string.IsNullOrEmpty(text) || (!text.Contains("server", StringComparison.OrdinalIgnoreCase) && !text.Contains("connection", StringComparison.OrdinalIgnoreCase) && !text.Contains("error", StringComparison.OrdinalIgnoreCase) && !text.Contains("lost", StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
return;
|
||||
}
|
||||
IsErrorDisconnect = true;
|
||||
try
|
||||
{
|
||||
if (autoRetainerIPC != null)
|
||||
{
|
||||
string currentChar = autoRetainerIPC.GetCurrentCharacter();
|
||||
if (!string.IsNullOrEmpty(currentChar))
|
||||
{
|
||||
LastDisconnectedCharacter = currentChar;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
try
|
||||
{
|
||||
AtkComponentButton* button = dialogueAddon->GetComponentButtonById(4u);
|
||||
if (button != null)
|
||||
{
|
||||
AtkResNode btnRes = button->AtkComponentBase.OwnerNode->AtkResNode;
|
||||
AtkEvent* evt = btnRes.AtkEventManager.Event;
|
||||
dialogueAddon->ReceiveEvent(evt->State.EventType, (int)evt->Param, btnRes.AtkEventManager.Event, null);
|
||||
lastDialogClickTime = DateTime.Now;
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
IsErrorDisconnect = false;
|
||||
LastDisconnectedCharacter = null;
|
||||
}
|
||||
|
||||
public bool RequestRelog()
|
||||
{
|
||||
if (autoRetainerIPC == null || !autoRetainerIPC.IsAvailable)
|
||||
{
|
||||
log.Warning("[ErrorRecovery] AutoRetainer IPC not available - cannot relog");
|
||||
return false;
|
||||
}
|
||||
if (string.IsNullOrEmpty(LastDisconnectedCharacter))
|
||||
{
|
||||
log.Warning("[ErrorRecovery] No character to relog to");
|
||||
return false;
|
||||
}
|
||||
log.Information("[ErrorRecovery] Requesting AutoRetainer relog to: " + LastDisconnectedCharacter);
|
||||
return autoRetainerIPC.SwitchCharacter(LastDisconnectedCharacter);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
try
|
||||
{
|
||||
framework.Update -= OnFrameworkUpdate;
|
||||
if (lobbyErrorHandlerHook != null)
|
||||
{
|
||||
lobbyErrorHandlerHook.Disable();
|
||||
lobbyErrorHandlerHook.Dispose();
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue