mirror of
https://github.com/goatcorp/Dalamud.git
synced 2025-12-30 04:13:40 +01:00
Revert "refactor(Dalamud): switch to file-scoped namespaces"
This reverts commit b5f34c3199.
This commit is contained in:
parent
d473826247
commit
1561fbac00
325 changed files with 45549 additions and 45209 deletions
|
|
@ -16,300 +16,301 @@ using Dalamud.IoC.Internal;
|
|||
using Dalamud.Utility;
|
||||
using Serilog;
|
||||
|
||||
namespace Dalamud.Game;
|
||||
|
||||
/// <summary>
|
||||
/// This class represents the Framework of the native game client and grants access to various subsystems.
|
||||
/// </summary>
|
||||
[PluginInterface]
|
||||
[InterfaceVersion("1.0")]
|
||||
public sealed class Framework : IDisposable
|
||||
namespace Dalamud.Game
|
||||
{
|
||||
private static Stopwatch statsStopwatch = new();
|
||||
private Stopwatch updateStopwatch = new();
|
||||
|
||||
private bool tier2Initialized = false;
|
||||
private bool tier3Initialized = false;
|
||||
private bool tierInitError = false;
|
||||
|
||||
private Hook<OnUpdateDetour> updateHook;
|
||||
private Hook<OnDestroyDetour> destroyHook;
|
||||
private Hook<OnRealDestroyDelegate> realDestroyHook;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Framework"/> class.
|
||||
/// This class represents the Framework of the native game client and grants access to various subsystems.
|
||||
/// </summary>
|
||||
internal Framework()
|
||||
[PluginInterface]
|
||||
[InterfaceVersion("1.0")]
|
||||
public sealed class Framework : IDisposable
|
||||
{
|
||||
this.Address = new FrameworkAddressResolver();
|
||||
this.Address.Setup();
|
||||
private static Stopwatch statsStopwatch = new();
|
||||
private Stopwatch updateStopwatch = new();
|
||||
|
||||
Log.Verbose($"Framework address 0x{this.Address.BaseAddress.ToInt64():X}");
|
||||
if (this.Address.BaseAddress == IntPtr.Zero)
|
||||
private bool tier2Initialized = false;
|
||||
private bool tier3Initialized = false;
|
||||
private bool tierInitError = false;
|
||||
|
||||
private Hook<OnUpdateDetour> updateHook;
|
||||
private Hook<OnDestroyDetour> destroyHook;
|
||||
private Hook<OnRealDestroyDelegate> realDestroyHook;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Framework"/> class.
|
||||
/// </summary>
|
||||
internal Framework()
|
||||
{
|
||||
throw new InvalidOperationException("Framework is not initalized yet.");
|
||||
this.Address = new FrameworkAddressResolver();
|
||||
this.Address.Setup();
|
||||
|
||||
Log.Verbose($"Framework address 0x{this.Address.BaseAddress.ToInt64():X}");
|
||||
if (this.Address.BaseAddress == IntPtr.Zero)
|
||||
{
|
||||
throw new InvalidOperationException("Framework is not initalized yet.");
|
||||
}
|
||||
|
||||
// Hook virtual functions
|
||||
this.HookVTable();
|
||||
}
|
||||
|
||||
// Hook virtual functions
|
||||
this.HookVTable();
|
||||
}
|
||||
/// <summary>
|
||||
/// A delegate type used with the <see cref="Update"/> event.
|
||||
/// </summary>
|
||||
/// <param name="framework">The Framework instance.</param>
|
||||
public delegate void OnUpdateDelegate(Framework framework);
|
||||
|
||||
/// <summary>
|
||||
/// A delegate type used with the <see cref="Update"/> event.
|
||||
/// </summary>
|
||||
/// <param name="framework">The Framework instance.</param>
|
||||
public delegate void OnUpdateDelegate(Framework framework);
|
||||
/// <summary>
|
||||
/// A delegate type used during the native Framework::destroy.
|
||||
/// </summary>
|
||||
/// <param name="framework">The native Framework address.</param>
|
||||
/// <returns>A value indicating if the call was successful.</returns>
|
||||
public delegate bool OnRealDestroyDelegate(IntPtr framework);
|
||||
|
||||
/// <summary>
|
||||
/// A delegate type used during the native Framework::destroy.
|
||||
/// </summary>
|
||||
/// <param name="framework">The native Framework address.</param>
|
||||
/// <returns>A value indicating if the call was successful.</returns>
|
||||
public delegate bool OnRealDestroyDelegate(IntPtr framework);
|
||||
/// <summary>
|
||||
/// A delegate type used during the native Framework::free.
|
||||
/// </summary>
|
||||
/// <returns>The native Framework address.</returns>
|
||||
public delegate IntPtr OnDestroyDelegate();
|
||||
|
||||
/// <summary>
|
||||
/// A delegate type used during the native Framework::free.
|
||||
/// </summary>
|
||||
/// <returns>The native Framework address.</returns>
|
||||
public delegate IntPtr OnDestroyDelegate();
|
||||
[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
|
||||
private delegate bool OnUpdateDetour(IntPtr framework);
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
|
||||
private delegate bool OnUpdateDetour(IntPtr framework);
|
||||
private delegate IntPtr OnDestroyDetour(); // OnDestroyDelegate
|
||||
|
||||
private delegate IntPtr OnDestroyDetour(); // OnDestroyDelegate
|
||||
/// <summary>
|
||||
/// Event that gets fired every time the game framework updates.
|
||||
/// </summary>
|
||||
public event OnUpdateDelegate Update;
|
||||
|
||||
/// <summary>
|
||||
/// Event that gets fired every time the game framework updates.
|
||||
/// </summary>
|
||||
public event OnUpdateDelegate Update;
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the collection of stats is enabled.
|
||||
/// </summary>
|
||||
public static bool StatsEnabled { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the collection of stats is enabled.
|
||||
/// </summary>
|
||||
public static bool StatsEnabled { get; set; }
|
||||
/// <summary>
|
||||
/// Gets the stats history mapping.
|
||||
/// </summary>
|
||||
public static Dictionary<string, List<double>> StatsHistory { get; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the stats history mapping.
|
||||
/// </summary>
|
||||
public static Dictionary<string, List<double>> StatsHistory { get; } = new();
|
||||
/// <summary>
|
||||
/// Gets a raw pointer to the instance of Client::Framework.
|
||||
/// </summary>
|
||||
public FrameworkAddressResolver Address { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a raw pointer to the instance of Client::Framework.
|
||||
/// </summary>
|
||||
public FrameworkAddressResolver Address { get; }
|
||||
/// <summary>
|
||||
/// Gets the last time that the Framework Update event was triggered.
|
||||
/// </summary>
|
||||
public DateTime LastUpdate { get; private set; } = DateTime.MinValue;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the last time that the Framework Update event was triggered.
|
||||
/// </summary>
|
||||
public DateTime LastUpdate { get; private set; } = DateTime.MinValue;
|
||||
/// <summary>
|
||||
/// Gets the last time in UTC that the Framework Update event was triggered.
|
||||
/// </summary>
|
||||
public DateTime LastUpdateUTC { get; private set; } = DateTime.MinValue;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the last time in UTC that the Framework Update event was triggered.
|
||||
/// </summary>
|
||||
public DateTime LastUpdateUTC { get; private set; } = DateTime.MinValue;
|
||||
/// <summary>
|
||||
/// Gets the delta between the last Framework Update and the currently executing one.
|
||||
/// </summary>
|
||||
public TimeSpan UpdateDelta { get; private set; } = TimeSpan.Zero;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the delta between the last Framework Update and the currently executing one.
|
||||
/// </summary>
|
||||
public TimeSpan UpdateDelta { get; private set; } = TimeSpan.Zero;
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether to dispatch update events.
|
||||
/// </summary>
|
||||
internal bool DispatchUpdateEvents { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether to dispatch update events.
|
||||
/// </summary>
|
||||
internal bool DispatchUpdateEvents { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Enable this module.
|
||||
/// </summary>
|
||||
public void Enable()
|
||||
{
|
||||
Service<LibcFunction>.Set();
|
||||
Service<GameGui>.Get().Enable();
|
||||
Service<GameNetwork>.Get().Enable();
|
||||
|
||||
this.updateHook.Enable();
|
||||
this.destroyHook.Enable();
|
||||
this.realDestroyHook.Enable();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dispose of managed and unmanaged resources.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
Service<GameGui>.GetNullable()?.Dispose();
|
||||
Service<GameNetwork>.GetNullable()?.Dispose();
|
||||
|
||||
this.updateHook?.Disable();
|
||||
this.destroyHook?.Disable();
|
||||
this.realDestroyHook?.Disable();
|
||||
Thread.Sleep(500);
|
||||
|
||||
this.updateHook?.Dispose();
|
||||
this.destroyHook?.Dispose();
|
||||
this.realDestroyHook?.Dispose();
|
||||
|
||||
this.updateStopwatch.Reset();
|
||||
statsStopwatch.Reset();
|
||||
}
|
||||
|
||||
private void HookVTable()
|
||||
{
|
||||
var vtable = Marshal.ReadIntPtr(this.Address.BaseAddress);
|
||||
// Virtual function layout:
|
||||
// .rdata:00000001411F1FE0 dq offset Xiv__Framework___dtor
|
||||
// .rdata:00000001411F1FE8 dq offset Xiv__Framework__init
|
||||
// .rdata:00000001411F1FF0 dq offset Xiv__Framework__destroy
|
||||
// .rdata:00000001411F1FF8 dq offset Xiv__Framework__free
|
||||
// .rdata:00000001411F2000 dq offset Xiv__Framework__update
|
||||
|
||||
var pUpdate = Marshal.ReadIntPtr(vtable, IntPtr.Size * 4);
|
||||
this.updateHook = new Hook<OnUpdateDetour>(pUpdate, this.HandleFrameworkUpdate);
|
||||
|
||||
var pDestroy = Marshal.ReadIntPtr(vtable, IntPtr.Size * 3);
|
||||
this.destroyHook = new Hook<OnDestroyDetour>(pDestroy, this.HandleFrameworkDestroy);
|
||||
|
||||
var pRealDestroy = Marshal.ReadIntPtr(vtable, IntPtr.Size * 2);
|
||||
this.realDestroyHook = new Hook<OnRealDestroyDelegate>(pRealDestroy, this.HandleRealDestroy);
|
||||
}
|
||||
|
||||
private bool HandleFrameworkUpdate(IntPtr framework)
|
||||
{
|
||||
// If any of the tier loads failed, just go to the original code.
|
||||
if (this.tierInitError)
|
||||
goto original;
|
||||
|
||||
var dalamud = Service<Dalamud>.Get();
|
||||
|
||||
// If this is the first time we are running this loop, we need to init Dalamud subsystems synchronously
|
||||
if (!this.tier2Initialized)
|
||||
/// <summary>
|
||||
/// Enable this module.
|
||||
/// </summary>
|
||||
public void Enable()
|
||||
{
|
||||
this.tier2Initialized = dalamud.LoadTier2();
|
||||
Service<LibcFunction>.Set();
|
||||
Service<GameGui>.Get().Enable();
|
||||
Service<GameNetwork>.Get().Enable();
|
||||
|
||||
this.updateHook.Enable();
|
||||
this.destroyHook.Enable();
|
||||
this.realDestroyHook.Enable();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dispose of managed and unmanaged resources.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
Service<GameGui>.GetNullable()?.Dispose();
|
||||
Service<GameNetwork>.GetNullable()?.Dispose();
|
||||
|
||||
this.updateHook?.Disable();
|
||||
this.destroyHook?.Disable();
|
||||
this.realDestroyHook?.Disable();
|
||||
Thread.Sleep(500);
|
||||
|
||||
this.updateHook?.Dispose();
|
||||
this.destroyHook?.Dispose();
|
||||
this.realDestroyHook?.Dispose();
|
||||
|
||||
this.updateStopwatch.Reset();
|
||||
statsStopwatch.Reset();
|
||||
}
|
||||
|
||||
private void HookVTable()
|
||||
{
|
||||
var vtable = Marshal.ReadIntPtr(this.Address.BaseAddress);
|
||||
// Virtual function layout:
|
||||
// .rdata:00000001411F1FE0 dq offset Xiv__Framework___dtor
|
||||
// .rdata:00000001411F1FE8 dq offset Xiv__Framework__init
|
||||
// .rdata:00000001411F1FF0 dq offset Xiv__Framework__destroy
|
||||
// .rdata:00000001411F1FF8 dq offset Xiv__Framework__free
|
||||
// .rdata:00000001411F2000 dq offset Xiv__Framework__update
|
||||
|
||||
var pUpdate = Marshal.ReadIntPtr(vtable, IntPtr.Size * 4);
|
||||
this.updateHook = new Hook<OnUpdateDetour>(pUpdate, this.HandleFrameworkUpdate);
|
||||
|
||||
var pDestroy = Marshal.ReadIntPtr(vtable, IntPtr.Size * 3);
|
||||
this.destroyHook = new Hook<OnDestroyDetour>(pDestroy, this.HandleFrameworkDestroy);
|
||||
|
||||
var pRealDestroy = Marshal.ReadIntPtr(vtable, IntPtr.Size * 2);
|
||||
this.realDestroyHook = new Hook<OnRealDestroyDelegate>(pRealDestroy, this.HandleRealDestroy);
|
||||
}
|
||||
|
||||
private bool HandleFrameworkUpdate(IntPtr framework)
|
||||
{
|
||||
// If any of the tier loads failed, just go to the original code.
|
||||
if (this.tierInitError)
|
||||
goto original;
|
||||
|
||||
var dalamud = Service<Dalamud>.Get();
|
||||
|
||||
// If this is the first time we are running this loop, we need to init Dalamud subsystems synchronously
|
||||
if (!this.tier2Initialized)
|
||||
this.tierInitError = true;
|
||||
{
|
||||
this.tier2Initialized = dalamud.LoadTier2();
|
||||
if (!this.tier2Initialized)
|
||||
this.tierInitError = true;
|
||||
|
||||
goto original;
|
||||
}
|
||||
goto original;
|
||||
}
|
||||
|
||||
// Plugins expect the interface to be available and ready, so we need to wait with plugins until we have init'd ImGui
|
||||
if (!this.tier3Initialized && Service<InterfaceManager>.GetNullable()?.IsReady == true)
|
||||
{
|
||||
this.tier3Initialized = dalamud.LoadTier3();
|
||||
if (!this.tier3Initialized)
|
||||
this.tierInitError = true;
|
||||
// Plugins expect the interface to be available and ready, so we need to wait with plugins until we have init'd ImGui
|
||||
if (!this.tier3Initialized && Service<InterfaceManager>.GetNullable()?.IsReady == true)
|
||||
{
|
||||
this.tier3Initialized = dalamud.LoadTier3();
|
||||
if (!this.tier3Initialized)
|
||||
this.tierInitError = true;
|
||||
|
||||
goto original;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
Service<ChatGui>.Get().UpdateQueue();
|
||||
Service<ToastGui>.Get().UpdateQueue();
|
||||
Service<GameNetwork>.Get().UpdateQueue();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, "Exception while handling Framework::Update hook.");
|
||||
}
|
||||
|
||||
if (this.DispatchUpdateEvents)
|
||||
{
|
||||
this.updateStopwatch.Stop();
|
||||
this.UpdateDelta = TimeSpan.FromMilliseconds(this.updateStopwatch.ElapsedMilliseconds);
|
||||
this.updateStopwatch.Restart();
|
||||
|
||||
this.LastUpdate = DateTime.Now;
|
||||
this.LastUpdateUTC = DateTime.UtcNow;
|
||||
goto original;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if (StatsEnabled && this.Update != null)
|
||||
{
|
||||
// Stat Tracking for Framework Updates
|
||||
var invokeList = this.Update.GetInvocationList();
|
||||
var notUpdated = StatsHistory.Keys.ToList();
|
||||
|
||||
// Individually invoke OnUpdate handlers and time them.
|
||||
foreach (var d in invokeList)
|
||||
{
|
||||
statsStopwatch.Restart();
|
||||
d.Method.Invoke(d.Target, new object[] { this });
|
||||
statsStopwatch.Stop();
|
||||
|
||||
var key = $"{d.Target}::{d.Method.Name}";
|
||||
if (notUpdated.Contains(key))
|
||||
notUpdated.Remove(key);
|
||||
|
||||
if (!StatsHistory.ContainsKey(key))
|
||||
StatsHistory.Add(key, new List<double>());
|
||||
|
||||
StatsHistory[key].Add(statsStopwatch.Elapsed.TotalMilliseconds);
|
||||
|
||||
if (StatsHistory[key].Count > 1000)
|
||||
{
|
||||
StatsHistory[key].RemoveRange(0, StatsHistory[key].Count - 1000);
|
||||
}
|
||||
}
|
||||
|
||||
// Cleanup handlers that are no longer being called
|
||||
foreach (var key in notUpdated)
|
||||
{
|
||||
if (StatsHistory[key].Count > 0)
|
||||
{
|
||||
StatsHistory[key].RemoveAt(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
StatsHistory.Remove(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
this.Update?.Invoke(this);
|
||||
}
|
||||
Service<ChatGui>.Get().UpdateQueue();
|
||||
Service<ToastGui>.Get().UpdateQueue();
|
||||
Service<GameNetwork>.Get().UpdateQueue();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, "Exception while dispatching Framework::Update event.");
|
||||
Log.Error(ex, "Exception while handling Framework::Update hook.");
|
||||
}
|
||||
|
||||
if (this.DispatchUpdateEvents)
|
||||
{
|
||||
this.updateStopwatch.Stop();
|
||||
this.UpdateDelta = TimeSpan.FromMilliseconds(this.updateStopwatch.ElapsedMilliseconds);
|
||||
this.updateStopwatch.Restart();
|
||||
|
||||
this.LastUpdate = DateTime.Now;
|
||||
this.LastUpdateUTC = DateTime.UtcNow;
|
||||
|
||||
try
|
||||
{
|
||||
if (StatsEnabled && this.Update != null)
|
||||
{
|
||||
// Stat Tracking for Framework Updates
|
||||
var invokeList = this.Update.GetInvocationList();
|
||||
var notUpdated = StatsHistory.Keys.ToList();
|
||||
|
||||
// Individually invoke OnUpdate handlers and time them.
|
||||
foreach (var d in invokeList)
|
||||
{
|
||||
statsStopwatch.Restart();
|
||||
d.Method.Invoke(d.Target, new object[] { this });
|
||||
statsStopwatch.Stop();
|
||||
|
||||
var key = $"{d.Target}::{d.Method.Name}";
|
||||
if (notUpdated.Contains(key))
|
||||
notUpdated.Remove(key);
|
||||
|
||||
if (!StatsHistory.ContainsKey(key))
|
||||
StatsHistory.Add(key, new List<double>());
|
||||
|
||||
StatsHistory[key].Add(statsStopwatch.Elapsed.TotalMilliseconds);
|
||||
|
||||
if (StatsHistory[key].Count > 1000)
|
||||
{
|
||||
StatsHistory[key].RemoveRange(0, StatsHistory[key].Count - 1000);
|
||||
}
|
||||
}
|
||||
|
||||
// Cleanup handlers that are no longer being called
|
||||
foreach (var key in notUpdated)
|
||||
{
|
||||
if (StatsHistory[key].Count > 0)
|
||||
{
|
||||
StatsHistory[key].RemoveAt(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
StatsHistory.Remove(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
this.Update?.Invoke(this);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Error(ex, "Exception while dispatching Framework::Update event.");
|
||||
}
|
||||
}
|
||||
|
||||
original:
|
||||
return this.updateHook.Original(framework);
|
||||
}
|
||||
|
||||
original:
|
||||
return this.updateHook.Original(framework);
|
||||
}
|
||||
|
||||
private bool HandleRealDestroy(IntPtr framework)
|
||||
{
|
||||
if (this.DispatchUpdateEvents)
|
||||
private bool HandleRealDestroy(IntPtr framework)
|
||||
{
|
||||
Log.Information("Framework::Destroy!");
|
||||
if (this.DispatchUpdateEvents)
|
||||
{
|
||||
Log.Information("Framework::Destroy!");
|
||||
|
||||
var dalamud = Service<Dalamud>.Get();
|
||||
dalamud.DisposePlugins();
|
||||
|
||||
Log.Information("Framework::Destroy OK!");
|
||||
}
|
||||
|
||||
this.DispatchUpdateEvents = false;
|
||||
|
||||
return this.realDestroyHook.Original(framework);
|
||||
}
|
||||
|
||||
private IntPtr HandleFrameworkDestroy()
|
||||
{
|
||||
Log.Information("Framework::Free!");
|
||||
|
||||
// Store the pointer to the original trampoline location
|
||||
var originalPtr = Marshal.GetFunctionPointerForDelegate(this.destroyHook.Original);
|
||||
|
||||
var dalamud = Service<Dalamud>.Get();
|
||||
dalamud.DisposePlugins();
|
||||
dalamud.Unload();
|
||||
dalamud.WaitForUnloadFinish();
|
||||
|
||||
Log.Information("Framework::Destroy OK!");
|
||||
Log.Information("Framework::Free OK!");
|
||||
|
||||
// Return the original trampoline location to cleanly exit
|
||||
return originalPtr;
|
||||
}
|
||||
|
||||
this.DispatchUpdateEvents = false;
|
||||
|
||||
return this.realDestroyHook.Original(framework);
|
||||
}
|
||||
|
||||
private IntPtr HandleFrameworkDestroy()
|
||||
{
|
||||
Log.Information("Framework::Free!");
|
||||
|
||||
// Store the pointer to the original trampoline location
|
||||
var originalPtr = Marshal.GetFunctionPointerForDelegate(this.destroyHook.Original);
|
||||
|
||||
var dalamud = Service<Dalamud>.Get();
|
||||
dalamud.Unload();
|
||||
dalamud.WaitForUnloadFinish();
|
||||
|
||||
Log.Information("Framework::Free OK!");
|
||||
|
||||
// Return the original trampoline location to cleanly exit
|
||||
return originalPtr;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue