mirror of
https://github.com/goatcorp/Dalamud.git
synced 2025-12-12 18:27:23 +01:00
Merge branch 'net5'
This commit is contained in:
commit
1a2365a676
66 changed files with 964 additions and 899 deletions
|
|
@ -21,6 +21,8 @@ void from_json(const nlohmann::json& json, DalamudStartInfo::WaitMessageboxFlags
|
||||||
value = static_cast<DalamudStartInfo::WaitMessageboxFlags>(static_cast<int>(value) | static_cast<int>(DalamudStartInfo::WaitMessageboxFlags::BeforeInitialize));
|
value = static_cast<DalamudStartInfo::WaitMessageboxFlags>(static_cast<int>(value) | static_cast<int>(DalamudStartInfo::WaitMessageboxFlags::BeforeInitialize));
|
||||||
else if (item == "beforedalamudentrypoint")
|
else if (item == "beforedalamudentrypoint")
|
||||||
value = static_cast<DalamudStartInfo::WaitMessageboxFlags>(static_cast<int>(value) | static_cast<int>(DalamudStartInfo::WaitMessageboxFlags::BeforeDalamudEntrypoint));
|
value = static_cast<DalamudStartInfo::WaitMessageboxFlags>(static_cast<int>(value) | static_cast<int>(DalamudStartInfo::WaitMessageboxFlags::BeforeDalamudEntrypoint));
|
||||||
|
else if (item == "beforedalamudconstruct")
|
||||||
|
value = static_cast<DalamudStartInfo::WaitMessageboxFlags>(static_cast<int>(value) | static_cast<int>(DalamudStartInfo::WaitMessageboxFlags::BeforeDalamudConstruct));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ struct DalamudStartInfo {
|
||||||
None = 0,
|
None = 0,
|
||||||
BeforeInitialize = 1 << 0,
|
BeforeInitialize = 1 << 0,
|
||||||
BeforeDalamudEntrypoint = 1 << 1,
|
BeforeDalamudEntrypoint = 1 << 1,
|
||||||
|
BeforeDalamudConstruct = 1 << 2,
|
||||||
};
|
};
|
||||||
friend void from_json(const nlohmann::json&, WaitMessageboxFlags&);
|
friend void from_json(const nlohmann::json&, WaitMessageboxFlags&);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@
|
||||||
HMODULE g_hModule;
|
HMODULE g_hModule;
|
||||||
HINSTANCE g_hGameInstance = GetModuleHandleW(nullptr);
|
HINSTANCE g_hGameInstance = GetModuleHandleW(nullptr);
|
||||||
|
|
||||||
DllExport DWORD WINAPI Initialize(LPVOID lpParam, HANDLE hMainThreadContinue) {
|
DWORD WINAPI InitializeImpl(LPVOID lpParam, HANDLE hMainThreadContinue) {
|
||||||
g_startInfo.from_envvars();
|
g_startInfo.from_envvars();
|
||||||
|
|
||||||
std::string jsonParseError;
|
std::string jsonParseError;
|
||||||
|
|
@ -149,11 +149,6 @@ DllExport DWORD WINAPI Initialize(LPVOID lpParam, HANDLE hMainThreadContinue) {
|
||||||
if (static_cast<int>(g_startInfo.BootWaitMessageBox) & static_cast<int>(DalamudStartInfo::WaitMessageboxFlags::BeforeDalamudEntrypoint))
|
if (static_cast<int>(g_startInfo.BootWaitMessageBox) & static_cast<int>(DalamudStartInfo::WaitMessageboxFlags::BeforeDalamudEntrypoint))
|
||||||
MessageBoxW(nullptr, L"Press OK to continue (BeforeDalamudEntrypoint)", L"Dalamud Boot", MB_OK);
|
MessageBoxW(nullptr, L"Press OK to continue (BeforeDalamudEntrypoint)", L"Dalamud Boot", MB_OK);
|
||||||
|
|
||||||
if (hMainThreadContinue) {
|
|
||||||
// Let the game initialize.
|
|
||||||
SetEvent(hMainThreadContinue);
|
|
||||||
}
|
|
||||||
|
|
||||||
// We don't need to do this anymore, Dalamud now loads without needing the window to be there. Speed!
|
// We don't need to do this anymore, Dalamud now loads without needing the window to be there. Speed!
|
||||||
// utils::wait_for_game_window();
|
// utils::wait_for_game_window();
|
||||||
|
|
||||||
|
|
@ -164,6 +159,10 @@ DllExport DWORD WINAPI Initialize(LPVOID lpParam, HANDLE hMainThreadContinue) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DllExport DWORD WINAPI Initialize(LPVOID lpParam) {
|
||||||
|
return InitializeImpl(lpParam, CreateEvent(nullptr, TRUE, FALSE, nullptr));
|
||||||
|
}
|
||||||
|
|
||||||
BOOL APIENTRY DllMain(const HMODULE hModule, const DWORD dwReason, LPVOID lpReserved) {
|
BOOL APIENTRY DllMain(const HMODULE hModule, const DWORD dwReason, LPVOID lpReserved) {
|
||||||
DisableThreadLibraryCalls(hModule);
|
DisableThreadLibraryCalls(hModule);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
#include "logging.h"
|
#include "logging.h"
|
||||||
|
|
||||||
DllExport DWORD WINAPI Initialize(LPVOID lpParam, HANDLE hMainThreadContinue);
|
DWORD WINAPI InitializeImpl(LPVOID lpParam, HANDLE hMainThreadContinue);
|
||||||
|
|
||||||
struct RewrittenEntryPointParameters {
|
struct RewrittenEntryPointParameters {
|
||||||
void* pAllocation;
|
void* pAllocation;
|
||||||
|
|
@ -368,7 +368,7 @@ DllExport void WINAPI RewrittenEntryPoint(RewrittenEntryPointParameters& params)
|
||||||
loadInfo = params.pLoadInfo;
|
loadInfo = params.pLoadInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
Initialize(&loadInfo[0], params.hMainThreadContinue);
|
InitializeImpl(&loadInfo[0], params.hMainThreadContinue);
|
||||||
return 0;
|
return 0;
|
||||||
} catch (const std::exception& e) {
|
} catch (const std::exception& e) {
|
||||||
MessageBoxA(nullptr, std::format("Failed to load Dalamud.\n\nError: {}", e.what()).c_str(), "Dalamud.Boot", MB_OK | MB_ICONERROR);
|
MessageBoxA(nullptr, std::format("Failed to load Dalamud.\n\nError: {}", e.what()).c_str(), "Dalamud.Boot", MB_OK | MB_ICONERROR);
|
||||||
|
|
@ -380,6 +380,5 @@ DllExport void WINAPI RewrittenEntryPoint(RewrittenEntryPointParameters& params)
|
||||||
|
|
||||||
CloseHandle(params.hMainThread);
|
CloseHandle(params.hMainThread);
|
||||||
WaitForSingleObject(params.hMainThreadContinue, INFINITE);
|
WaitForSingleObject(params.hMainThreadContinue, INFINITE);
|
||||||
CloseHandle(params.hMainThreadContinue);
|
|
||||||
VirtualFree(params.pAllocation, 0, MEM_RELEASE);
|
VirtualFree(params.pAllocation, 0, MEM_RELEASE);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -61,6 +61,7 @@
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Iced" Version="1.13.0" />
|
<PackageReference Include="Iced" Version="1.13.0" />
|
||||||
|
<PackageReference Include="JetBrains.Annotations" Version="2022.1.0" />
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
||||||
<PackageReference Include="PeNet" Version="2.6.4" />
|
<PackageReference Include="PeNet" Version="2.6.4" />
|
||||||
<PackageReference Include="Reloaded.Memory" Version="4.1.1" />
|
<PackageReference Include="Reloaded.Memory" Version="4.1.1" />
|
||||||
|
|
|
||||||
|
|
@ -83,8 +83,14 @@ namespace Dalamud.Injector
|
||||||
}
|
}
|
||||||
|
|
||||||
startInfo = ExtractAndInitializeStartInfoFromArguments(startInfo, args);
|
startInfo = ExtractAndInitializeStartInfoFromArguments(startInfo, args);
|
||||||
args.Remove("--console"); // Remove "console" flag, already handled
|
// Remove already handled arguments
|
||||||
args.Remove("--etw"); // Remove "etw" flag, already handled
|
args.Remove("--console");
|
||||||
|
args.Remove("--msgbox1");
|
||||||
|
args.Remove("--msgbox2");
|
||||||
|
args.Remove("--msgbox3");
|
||||||
|
args.Remove("--etw");
|
||||||
|
args.Remove("--veh");
|
||||||
|
args.Remove("--veh-full");
|
||||||
|
|
||||||
var mainCommand = args[1].ToLowerInvariant();
|
var mainCommand = args[1].ToLowerInvariant();
|
||||||
if (mainCommand.Length > 0 && mainCommand.Length <= 6 && "inject"[..mainCommand.Length] == mainCommand)
|
if (mainCommand.Length > 0 && mainCommand.Length <= 6 && "inject"[..mainCommand.Length] == mainCommand)
|
||||||
|
|
@ -311,7 +317,11 @@ namespace Dalamud.Injector
|
||||||
startInfo.BootLogPath = GetLogPath("dalamud.boot");
|
startInfo.BootLogPath = GetLogPath("dalamud.boot");
|
||||||
startInfo.BootEnabledGameFixes = new List<string> { "prevent_devicechange_crashes", "disable_game_openprocess_access_check", "redirect_openprocess" };
|
startInfo.BootEnabledGameFixes = new List<string> { "prevent_devicechange_crashes", "disable_game_openprocess_access_check", "redirect_openprocess" };
|
||||||
startInfo.BootDotnetOpenProcessHookMode = 0;
|
startInfo.BootDotnetOpenProcessHookMode = 0;
|
||||||
// startInfo.BootWaitMessageBox = 2;
|
startInfo.BootWaitMessageBox |= args.Contains("--msgbox1") ? 1 : 0;
|
||||||
|
startInfo.BootWaitMessageBox |= args.Contains("--msgbox2") ? 2 : 0;
|
||||||
|
startInfo.BootWaitMessageBox |= args.Contains("--msgbox3") ? 4 : 0;
|
||||||
|
startInfo.BootVehEnabled = args.Contains("--veh");
|
||||||
|
startInfo.BootVehFull = args.Contains("--veh-full");
|
||||||
// startInfo.BootUnhookDlls = new List<string>() { "kernel32.dll", "ntdll.dll", "user32.dll" };
|
// startInfo.BootUnhookDlls = new List<string>() { "kernel32.dll", "ntdll.dll", "user32.dll" };
|
||||||
|
|
||||||
return startInfo;
|
return startInfo;
|
||||||
|
|
@ -338,6 +348,7 @@ namespace Dalamud.Injector
|
||||||
Console.WriteLine("{0} [-m entrypoint|inject] [--mode=entrypoint|inject]", exeSpaces);
|
Console.WriteLine("{0} [-m entrypoint|inject] [--mode=entrypoint|inject]", exeSpaces);
|
||||||
Console.WriteLine("{0} [--handle-owner=inherited-handle-value]", exeSpaces);
|
Console.WriteLine("{0} [--handle-owner=inherited-handle-value]", exeSpaces);
|
||||||
Console.WriteLine("{0} [--without-dalamud] [--no-fix-acl]", exeSpaces);
|
Console.WriteLine("{0} [--without-dalamud] [--no-fix-acl]", exeSpaces);
|
||||||
|
Console.WriteLine("{0} [--no-wait]", exeSpaces);
|
||||||
Console.WriteLine("{0} [-- game_arg1=value1 game_arg2=value2 ...]", exeSpaces);
|
Console.WriteLine("{0} [-- game_arg1=value1 game_arg2=value2 ...]", exeSpaces);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -349,6 +360,8 @@ namespace Dalamud.Injector
|
||||||
Console.WriteLine("Verbose logging:\t[-v]");
|
Console.WriteLine("Verbose logging:\t[-v]");
|
||||||
Console.WriteLine("Show Console:\t[--console]");
|
Console.WriteLine("Show Console:\t[--console]");
|
||||||
Console.WriteLine("Enable ETW:\t[--etw]");
|
Console.WriteLine("Enable ETW:\t[--etw]");
|
||||||
|
Console.WriteLine("Enable VEH:\t[--veh], [--veh-full]");
|
||||||
|
Console.WriteLine("Show messagebox:\t[--msgbox1], [--msgbox2], [--msgbox3]");
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
@ -464,6 +477,7 @@ namespace Dalamud.Injector
|
||||||
var handleOwner = IntPtr.Zero;
|
var handleOwner = IntPtr.Zero;
|
||||||
var withoutDalamud = false;
|
var withoutDalamud = false;
|
||||||
var noFixAcl = false;
|
var noFixAcl = false;
|
||||||
|
var waitForGameWindow = true;
|
||||||
|
|
||||||
var parsingGameArgument = false;
|
var parsingGameArgument = false;
|
||||||
for (var i = 2; i < args.Count; i++)
|
for (var i = 2; i < args.Count; i++)
|
||||||
|
|
@ -480,6 +494,8 @@ namespace Dalamud.Injector
|
||||||
useFakeArguments = true;
|
useFakeArguments = true;
|
||||||
else if (args[i] == "--without-dalamud")
|
else if (args[i] == "--without-dalamud")
|
||||||
withoutDalamud = true;
|
withoutDalamud = true;
|
||||||
|
else if (args[i] == "--no-wait")
|
||||||
|
waitForGameWindow = false;
|
||||||
else if (args[i] == "--no-fix-acl" || args[i] == "--no-acl-fix")
|
else if (args[i] == "--no-fix-acl" || args[i] == "--no-acl-fix")
|
||||||
noFixAcl = true;
|
noFixAcl = true;
|
||||||
else if (args[i] == "-g")
|
else if (args[i] == "-g")
|
||||||
|
|
@ -596,7 +612,7 @@ namespace Dalamud.Injector
|
||||||
|
|
||||||
Log.Verbose("RewriteRemoteEntryPointW called!");
|
Log.Verbose("RewriteRemoteEntryPointW called!");
|
||||||
}
|
}
|
||||||
});
|
}, waitForGameWindow);
|
||||||
|
|
||||||
Log.Verbose("Game process started with PID {0}", process.Id);
|
Log.Verbose("Game process started with PID {0}", process.Id);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -235,6 +235,11 @@ namespace Dalamud.Configuration.Internal
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool IsAntiAntiDebugEnabled { get; set; } = false;
|
public bool IsAntiAntiDebugEnabled { get; set; } = false;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether to resume game main thread after plugins load.
|
||||||
|
/// </summary>
|
||||||
|
public bool IsResumeGameAfterPluginLoad { get; set; } = false;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the kind of beta to download when <see cref="DalamudBetaKey"/> matches the server value.
|
/// Gets or sets the kind of beta to download when <see cref="DalamudBetaKey"/> matches the server value.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ using System.IO;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using Dalamud.Configuration.Internal;
|
using Dalamud.Configuration.Internal;
|
||||||
using Dalamud.Data;
|
using Dalamud.Data;
|
||||||
using Dalamud.Game;
|
using Dalamud.Game;
|
||||||
|
|
@ -49,7 +49,6 @@ namespace Dalamud
|
||||||
|
|
||||||
private readonly ManualResetEvent unloadSignal;
|
private readonly ManualResetEvent unloadSignal;
|
||||||
private readonly ManualResetEvent finishUnloadSignal;
|
private readonly ManualResetEvent finishUnloadSignal;
|
||||||
private readonly IntPtr mainThreadContinueEvent;
|
|
||||||
private MonoMod.RuntimeDetour.Hook processMonoHook;
|
private MonoMod.RuntimeDetour.Hook processMonoHook;
|
||||||
private bool hasDisposedPlugins = false;
|
private bool hasDisposedPlugins = false;
|
||||||
|
|
||||||
|
|
@ -65,10 +64,6 @@ namespace Dalamud
|
||||||
/// <param name="mainThreadContinueEvent">Event used to signal the main thread to continue.</param>
|
/// <param name="mainThreadContinueEvent">Event used to signal the main thread to continue.</param>
|
||||||
public Dalamud(DalamudStartInfo info, LoggingLevelSwitch loggingLevelSwitch, ManualResetEvent finishSignal, DalamudConfiguration configuration, IntPtr mainThreadContinueEvent)
|
public Dalamud(DalamudStartInfo info, LoggingLevelSwitch loggingLevelSwitch, ManualResetEvent finishSignal, DalamudConfiguration configuration, IntPtr mainThreadContinueEvent)
|
||||||
{
|
{
|
||||||
Service<Dalamud>.Set(this);
|
|
||||||
Service<DalamudStartInfo>.Set(info);
|
|
||||||
Service<DalamudConfiguration>.Set(configuration);
|
|
||||||
|
|
||||||
this.LogLevelSwitch = loggingLevelSwitch;
|
this.LogLevelSwitch = loggingLevelSwitch;
|
||||||
|
|
||||||
this.unloadSignal = new ManualResetEvent(false);
|
this.unloadSignal = new ManualResetEvent(false);
|
||||||
|
|
@ -77,7 +72,55 @@ namespace Dalamud
|
||||||
this.finishUnloadSignal = finishSignal;
|
this.finishUnloadSignal = finishSignal;
|
||||||
this.finishUnloadSignal.Reset();
|
this.finishUnloadSignal.Reset();
|
||||||
|
|
||||||
this.mainThreadContinueEvent = mainThreadContinueEvent;
|
SerilogEventSink.Instance.LogLine += SerilogOnLogLine;
|
||||||
|
|
||||||
|
Service<Dalamud>.Provide(this);
|
||||||
|
Service<DalamudStartInfo>.Provide(info);
|
||||||
|
Service<DalamudConfiguration>.Provide(configuration);
|
||||||
|
|
||||||
|
if (!configuration.IsResumeGameAfterPluginLoad)
|
||||||
|
{
|
||||||
|
NativeFunctions.SetEvent(mainThreadContinueEvent);
|
||||||
|
_ = ServiceManager.InitializeEarlyLoadableServices();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Task.Run(async () =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var tasks = new[]
|
||||||
|
{
|
||||||
|
ServiceManager.InitializeEarlyLoadableServices(),
|
||||||
|
ServiceManager.BlockingResolved,
|
||||||
|
};
|
||||||
|
|
||||||
|
await Task.WhenAny(tasks);
|
||||||
|
foreach (var task in tasks)
|
||||||
|
{
|
||||||
|
if (task.IsFaulted)
|
||||||
|
throw task.Exception!;
|
||||||
|
}
|
||||||
|
|
||||||
|
NativeFunctions.SetEvent(mainThreadContinueEvent);
|
||||||
|
|
||||||
|
await Task.WhenAll(tasks);
|
||||||
|
foreach (var task in tasks)
|
||||||
|
{
|
||||||
|
if (task.IsFaulted)
|
||||||
|
throw task.Exception!;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Log.Error(e, "Service initialization failure");
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
NativeFunctions.SetEvent(mainThreadContinueEvent);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -88,262 +131,7 @@ namespace Dalamud
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets location of stored assets.
|
/// Gets location of stored assets.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal DirectoryInfo AssetDirectory => new(Service<DalamudStartInfo>.Get().AssetDirectory);
|
internal DirectoryInfo AssetDirectory => new(Service<DalamudStartInfo>.Get().AssetDirectory!);
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Runs tier 1 of the Dalamud initialization process.
|
|
||||||
/// </summary>
|
|
||||||
public void LoadTier1()
|
|
||||||
{
|
|
||||||
using var tier1Timing = Timings.Start("Tier 1 Init");
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
SerilogEventSink.Instance.LogLine += SerilogOnLogLine;
|
|
||||||
|
|
||||||
Service<ServiceContainer>.Set();
|
|
||||||
|
|
||||||
// Initialize the process information.
|
|
||||||
var info = Service<DalamudStartInfo>.Get();
|
|
||||||
var cacheDir = new DirectoryInfo(Path.Combine(info.WorkingDirectory!, "cachedSigs"));
|
|
||||||
if (!cacheDir.Exists)
|
|
||||||
cacheDir.Create();
|
|
||||||
|
|
||||||
Service<SigScanner>.Set(
|
|
||||||
new SigScanner(true, new FileInfo(Path.Combine(cacheDir.FullName, $"{info.GameVersion}.json"))));
|
|
||||||
Service<HookManager>.Set();
|
|
||||||
|
|
||||||
// Initialize FFXIVClientStructs function resolver
|
|
||||||
using (Timings.Start("CS Resolver Init"))
|
|
||||||
{
|
|
||||||
FFXIVClientStructs.Resolver.InitializeParallel(new FileInfo(Path.Combine(cacheDir.FullName, $"{info.GameVersion}_cs.json")));
|
|
||||||
Log.Information("[T1] FFXIVClientStructs initialized!");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize game subsystem
|
|
||||||
var framework = Service<Framework>.Set();
|
|
||||||
Log.Information("[T1] Framework OK!");
|
|
||||||
|
|
||||||
#if DEBUG
|
|
||||||
Service<TaskTracker>.Set();
|
|
||||||
Log.Information("[T1] TaskTracker OK!");
|
|
||||||
#endif
|
|
||||||
Service<GameNetwork>.Set();
|
|
||||||
Service<GameGui>.Set();
|
|
||||||
|
|
||||||
framework.Enable();
|
|
||||||
|
|
||||||
Log.Information("[T1] Load complete!");
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Log.Error(ex, "Tier 1 load failed");
|
|
||||||
this.Unload();
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
// Signal the main game thread to continue
|
|
||||||
// TODO: This is done in rewrite_entrypoint.cpp again to avoid a race condition. Should be fixed!
|
|
||||||
// NativeFunctions.SetEvent(this.mainThreadContinueEvent);
|
|
||||||
|
|
||||||
// Timings.Event("Game kickoff");
|
|
||||||
// Log.Information("[T1] Game thread continued!");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Runs tier 2 of the Dalamud initialization process.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>Whether or not the load succeeded.</returns>
|
|
||||||
public bool LoadTier2()
|
|
||||||
{
|
|
||||||
// This marks the first time we are actually on the game's main thread
|
|
||||||
ThreadSafety.MarkMainThread();
|
|
||||||
|
|
||||||
using var tier2Timing = Timings.Start("Tier 2 Init");
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var configuration = Service<DalamudConfiguration>.Get();
|
|
||||||
|
|
||||||
var antiDebug = Service<AntiDebug>.Set();
|
|
||||||
if (!antiDebug.IsEnabled)
|
|
||||||
{
|
|
||||||
#if DEBUG
|
|
||||||
antiDebug.Enable();
|
|
||||||
#else
|
|
||||||
if (configuration.IsAntiAntiDebugEnabled)
|
|
||||||
antiDebug.Enable();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
Log.Information("[T2] AntiDebug OK!");
|
|
||||||
|
|
||||||
Service<WinSockHandlers>.Set();
|
|
||||||
Log.Information("[T2] WinSock OK!");
|
|
||||||
|
|
||||||
Service<NetworkHandlers>.Set();
|
|
||||||
Log.Information("[T2] NH OK!");
|
|
||||||
|
|
||||||
using (Timings.Start("DM Init"))
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Service<DataManager>.Set().Initialize(this.AssetDirectory.FullName);
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
Log.Error(e, "Could not initialize DataManager");
|
|
||||||
this.Unload();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Log.Information("[T2] Data OK!");
|
|
||||||
|
|
||||||
using (Timings.Start("CS Init"))
|
|
||||||
{
|
|
||||||
Service<ClientState>.Set();
|
|
||||||
Log.Information("[T2] CS OK!");
|
|
||||||
}
|
|
||||||
|
|
||||||
var localization = Service<Localization>.Set(new Localization(Path.Combine(this.AssetDirectory.FullName, "UIRes", "loc", "dalamud"), "dalamud_"));
|
|
||||||
if (!string.IsNullOrEmpty(configuration.LanguageOverride))
|
|
||||||
{
|
|
||||||
localization.SetupWithLangCode(configuration.LanguageOverride);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
localization.SetupWithUiCulture();
|
|
||||||
}
|
|
||||||
|
|
||||||
Log.Information("[T2] LOC OK!");
|
|
||||||
|
|
||||||
// This is enabled in ImGuiScene setup
|
|
||||||
using (Timings.Start("IME Init"))
|
|
||||||
{
|
|
||||||
Service<DalamudIME>.Set();
|
|
||||||
Log.Information("[T2] IME OK!");
|
|
||||||
}
|
|
||||||
|
|
||||||
using (Timings.Start("IM Enable"))
|
|
||||||
{
|
|
||||||
Service<InterfaceManager>.Set().Enable();
|
|
||||||
Log.Information("[T2] IM OK!");
|
|
||||||
}
|
|
||||||
|
|
||||||
using (Timings.Start("GFM Init"))
|
|
||||||
{
|
|
||||||
Service<GameFontManager>.Set();
|
|
||||||
Log.Information("[T2] GFM OK!");
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma warning disable CS0618 // Type or member is obsolete
|
|
||||||
Service<SeStringManager>.Set();
|
|
||||||
#pragma warning restore CS0618 // Type or member is obsolete
|
|
||||||
|
|
||||||
Log.Information("[T2] SeString OK!");
|
|
||||||
|
|
||||||
using (Timings.Start("CM Init"))
|
|
||||||
{
|
|
||||||
Service<CommandManager>.Set();
|
|
||||||
Service<DalamudCommands>.Set().SetupCommands();
|
|
||||||
Log.Information("[T2] CM OK!");
|
|
||||||
}
|
|
||||||
|
|
||||||
Service<ChatHandlers>.Set();
|
|
||||||
Log.Information("[T2] CH OK!");
|
|
||||||
|
|
||||||
using (Timings.Start("CS Enable"))
|
|
||||||
{
|
|
||||||
Service<ClientState>.Get().Enable();
|
|
||||||
Log.Information("[T2] CS ENABLE!");
|
|
||||||
}
|
|
||||||
|
|
||||||
Service<DalamudAtkTweaks>.Set().Enable();
|
|
||||||
Log.Information("[T2] ATKTWEAKS ENABLE!");
|
|
||||||
|
|
||||||
Log.Information("[T2] Load complete!");
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Log.Error(ex, "Tier 2 load failed");
|
|
||||||
this.Unload();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Runs tier 3 of the Dalamud initialization process.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>Whether or not the load succeeded.</returns>
|
|
||||||
public bool LoadTier3()
|
|
||||||
{
|
|
||||||
using var tier3Timing = Timings.Start("Tier 3 Init");
|
|
||||||
|
|
||||||
ThreadSafety.AssertMainThread();
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Log.Information("[T3] START!");
|
|
||||||
|
|
||||||
Service<TitleScreenMenu>.Set();
|
|
||||||
|
|
||||||
PluginManager pluginManager;
|
|
||||||
using (Timings.Start("PM Init"))
|
|
||||||
{
|
|
||||||
pluginManager = Service<PluginManager>.Set();
|
|
||||||
Service<CallGate>.Set();
|
|
||||||
Log.Information("[T3] PM OK!");
|
|
||||||
}
|
|
||||||
|
|
||||||
Service<DalamudInterface>.Set();
|
|
||||||
Log.Information("[T3] DUI OK!");
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
using (Timings.Start("PM Load Plugin Repos"))
|
|
||||||
{
|
|
||||||
_ = pluginManager.SetPluginReposFromConfigAsync(false);
|
|
||||||
pluginManager.OnInstalledPluginsChanged += Troubleshooting.LogTroubleshooting;
|
|
||||||
|
|
||||||
Log.Information("[T3] PM repos OK!");
|
|
||||||
}
|
|
||||||
|
|
||||||
using (Timings.Start("PM Cleanup Plugins"))
|
|
||||||
{
|
|
||||||
pluginManager.CleanupPlugins();
|
|
||||||
Log.Information("[T3] PMC OK!");
|
|
||||||
}
|
|
||||||
|
|
||||||
using (Timings.Start("PM Load Sync Plugins"))
|
|
||||||
{
|
|
||||||
pluginManager.LoadAllPlugins();
|
|
||||||
Log.Information("[T3] PML OK!");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Log.Error(ex, "Plugin load failed");
|
|
||||||
}
|
|
||||||
|
|
||||||
Troubleshooting.LogTroubleshooting();
|
|
||||||
|
|
||||||
Log.Information("Dalamud is ready");
|
|
||||||
Timings.Event("Dalamud ready");
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Log.Error(ex, "Tier 3 load failed");
|
|
||||||
this.Unload();
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Queue an unload of Dalamud when it gets the chance.
|
/// Queue an unload of Dalamud when it gets the chance.
|
||||||
|
|
|
||||||
|
|
@ -86,12 +86,12 @@ namespace Dalamud
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets a value that specifies how much to wait before a new Dalamud session.
|
/// Gets or sets a value that specifies how much to wait before a new Dalamud session.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public int DelayInitializeMs { get; set; } = 0;
|
public int DelayInitializeMs { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the path the boot log file is supposed to be written to.
|
/// Gets or sets the path the boot log file is supposed to be written to.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string BootLogPath { get; set; }
|
public string? BootLogPath { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets a value indicating whether a Boot console should be shown.
|
/// Gets or sets a value indicating whether a Boot console should be shown.
|
||||||
|
|
|
||||||
|
|
@ -26,22 +26,91 @@ namespace Dalamud.Data
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[PluginInterface]
|
[PluginInterface]
|
||||||
[InterfaceVersion("1.0")]
|
[InterfaceVersion("1.0")]
|
||||||
|
[ServiceManager.BlockingEarlyLoadedService]
|
||||||
public sealed class DataManager : IDisposable
|
public sealed class DataManager : IDisposable
|
||||||
{
|
{
|
||||||
private const string IconFileFormat = "ui/icon/{0:D3}000/{1}{2:D6}.tex";
|
private const string IconFileFormat = "ui/icon/{0:D3}000/{1}{2:D6}.tex";
|
||||||
|
|
||||||
private Thread luminaResourceThread;
|
private readonly Thread luminaResourceThread;
|
||||||
private CancellationTokenSource luminaCancellationTokenSource;
|
private readonly CancellationTokenSource luminaCancellationTokenSource;
|
||||||
|
|
||||||
/// <summary>
|
[ServiceManager.ServiceConstructor]
|
||||||
/// Initializes a new instance of the <see cref="DataManager"/> class.
|
private DataManager(DalamudStartInfo dalamudStartInfo, Dalamud dalamud)
|
||||||
/// </summary>
|
|
||||||
internal DataManager()
|
|
||||||
{
|
{
|
||||||
this.Language = Service<DalamudStartInfo>.Get().Language;
|
this.Language = dalamudStartInfo.Language;
|
||||||
|
|
||||||
// Set up default values so plugins do not null-reference when data is being loaded.
|
// Set up default values so plugins do not null-reference when data is being loaded.
|
||||||
this.ClientOpCodes = this.ServerOpCodes = new ReadOnlyDictionary<string, ushort>(new Dictionary<string, ushort>());
|
this.ClientOpCodes = this.ServerOpCodes = new ReadOnlyDictionary<string, ushort>(new Dictionary<string, ushort>());
|
||||||
|
|
||||||
|
var baseDir = dalamud.AssetDirectory.FullName;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Log.Verbose("Starting data load...");
|
||||||
|
|
||||||
|
var zoneOpCodeDict = JsonConvert.DeserializeObject<Dictionary<string, ushort>>(
|
||||||
|
File.ReadAllText(Path.Combine(baseDir, "UIRes", "serveropcode.json")))!;
|
||||||
|
this.ServerOpCodes = new ReadOnlyDictionary<string, ushort>(zoneOpCodeDict);
|
||||||
|
|
||||||
|
Log.Verbose("Loaded {0} ServerOpCodes.", zoneOpCodeDict.Count);
|
||||||
|
|
||||||
|
var clientOpCodeDict = JsonConvert.DeserializeObject<Dictionary<string, ushort>>(
|
||||||
|
File.ReadAllText(Path.Combine(baseDir, "UIRes", "clientopcode.json")))!;
|
||||||
|
this.ClientOpCodes = new ReadOnlyDictionary<string, ushort>(clientOpCodeDict);
|
||||||
|
|
||||||
|
Log.Verbose("Loaded {0} ClientOpCodes.", clientOpCodeDict.Count);
|
||||||
|
|
||||||
|
using (Timings.Start("Lumina Init"))
|
||||||
|
{
|
||||||
|
var luminaOptions = new LuminaOptions
|
||||||
|
{
|
||||||
|
LoadMultithreaded = true,
|
||||||
|
CacheFileResources = true,
|
||||||
|
#if DEBUG
|
||||||
|
PanicOnSheetChecksumMismatch = true,
|
||||||
|
#else
|
||||||
|
PanicOnSheetChecksumMismatch = false,
|
||||||
|
#endif
|
||||||
|
DefaultExcelLanguage = this.Language.ToLumina(),
|
||||||
|
};
|
||||||
|
|
||||||
|
var processModule = Process.GetCurrentProcess().MainModule;
|
||||||
|
if (processModule != null)
|
||||||
|
{
|
||||||
|
this.GameData = new GameData(Path.Combine(Path.GetDirectoryName(processModule.FileName)!, "sqpack"), luminaOptions);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new Exception("Could not main module.");
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.Information("Lumina is ready: {0}", this.GameData.DataPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.IsDataReady = true;
|
||||||
|
|
||||||
|
this.luminaCancellationTokenSource = new();
|
||||||
|
|
||||||
|
var luminaCancellationToken = this.luminaCancellationTokenSource.Token;
|
||||||
|
this.luminaResourceThread = new(() =>
|
||||||
|
{
|
||||||
|
while (!luminaCancellationToken.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
if (this.GameData.FileHandleManager.HasPendingFileLoads)
|
||||||
|
{
|
||||||
|
this.GameData.ProcessFileHandleQueue();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Thread.Sleep(5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.luminaResourceThread.Start();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Log.Error(ex, "Could not download data.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -68,7 +137,7 @@ namespace Dalamud.Data
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets an <see cref="ExcelModule"/> object which gives access to any of the game's sheet data.
|
/// Gets an <see cref="ExcelModule"/> object which gives access to any of the game's sheet data.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public ExcelModule Excel => this.GameData?.Excel;
|
public ExcelModule Excel => this.GameData.Excel;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets a value indicating whether Game Data is ready to be read.
|
/// Gets a value indicating whether Game Data is ready to be read.
|
||||||
|
|
@ -180,7 +249,7 @@ namespace Dalamud.Data
|
||||||
/// <param name="type">The type of the icon (e.g. 'hq' to get the HQ variant of an item icon).</param>
|
/// <param name="type">The type of the icon (e.g. 'hq' to get the HQ variant of an item icon).</param>
|
||||||
/// <param name="iconId">The icon ID.</param>
|
/// <param name="iconId">The icon ID.</param>
|
||||||
/// <returns>The <see cref="TexFile"/> containing the icon.</returns>
|
/// <returns>The <see cref="TexFile"/> containing the icon.</returns>
|
||||||
public TexFile? GetIcon(string type, uint iconId)
|
public TexFile? GetIcon(string? type, uint iconId)
|
||||||
{
|
{
|
||||||
type ??= string.Empty;
|
type ??= string.Empty;
|
||||||
if (type.Length > 0 && !type.EndsWith("/"))
|
if (type.Length > 0 && !type.EndsWith("/"))
|
||||||
|
|
@ -276,81 +345,5 @@ namespace Dalamud.Data
|
||||||
{
|
{
|
||||||
this.luminaCancellationTokenSource.Cancel();
|
this.luminaCancellationTokenSource.Cancel();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initialize this data manager.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="baseDir">The directory to load data from.</param>
|
|
||||||
internal void Initialize(string baseDir)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Log.Verbose("Starting data load...");
|
|
||||||
|
|
||||||
var zoneOpCodeDict = JsonConvert.DeserializeObject<Dictionary<string, ushort>>(
|
|
||||||
File.ReadAllText(Path.Combine(baseDir, "UIRes", "serveropcode.json")));
|
|
||||||
this.ServerOpCodes = new ReadOnlyDictionary<string, ushort>(zoneOpCodeDict);
|
|
||||||
|
|
||||||
Log.Verbose("Loaded {0} ServerOpCodes.", zoneOpCodeDict.Count);
|
|
||||||
|
|
||||||
var clientOpCodeDict = JsonConvert.DeserializeObject<Dictionary<string, ushort>>(
|
|
||||||
File.ReadAllText(Path.Combine(baseDir, "UIRes", "clientopcode.json")));
|
|
||||||
this.ClientOpCodes = new ReadOnlyDictionary<string, ushort>(clientOpCodeDict);
|
|
||||||
|
|
||||||
Log.Verbose("Loaded {0} ClientOpCodes.", clientOpCodeDict.Count);
|
|
||||||
|
|
||||||
using (Timings.Start("Lumina Init"))
|
|
||||||
{
|
|
||||||
var luminaOptions = new LuminaOptions
|
|
||||||
{
|
|
||||||
LoadMultithreaded = true,
|
|
||||||
CacheFileResources = true,
|
|
||||||
#if DEBUG
|
|
||||||
PanicOnSheetChecksumMismatch = true,
|
|
||||||
#else
|
|
||||||
PanicOnSheetChecksumMismatch = false,
|
|
||||||
#endif
|
|
||||||
DefaultExcelLanguage = this.Language.ToLumina(),
|
|
||||||
};
|
|
||||||
|
|
||||||
var processModule = Process.GetCurrentProcess().MainModule;
|
|
||||||
if (processModule != null)
|
|
||||||
{
|
|
||||||
this.GameData = new GameData(Path.Combine(Path.GetDirectoryName(processModule.FileName), "sqpack"), luminaOptions);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw new Exception("Could not main module.");
|
|
||||||
}
|
|
||||||
|
|
||||||
Log.Information("Lumina is ready: {0}", this.GameData.DataPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.IsDataReady = true;
|
|
||||||
|
|
||||||
this.luminaCancellationTokenSource = new();
|
|
||||||
|
|
||||||
var luminaCancellationToken = this.luminaCancellationTokenSource.Token;
|
|
||||||
this.luminaResourceThread = new(() =>
|
|
||||||
{
|
|
||||||
while (!luminaCancellationToken.IsCancellationRequested)
|
|
||||||
{
|
|
||||||
if (this.GameData.FileHandleManager.HasPendingFileLoads)
|
|
||||||
{
|
|
||||||
this.GameData.ProcessFileHandleQueue();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Thread.Sleep(5);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
this.luminaResourceThread.Start();
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Log.Error(ex, "Could not download data.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -47,8 +47,11 @@ namespace Dalamud
|
||||||
/// <param name="mainThreadContinueEvent">Event used to signal the main thread to continue.</param>
|
/// <param name="mainThreadContinueEvent">Event used to signal the main thread to continue.</param>
|
||||||
public static void Initialize(IntPtr infoPtr, IntPtr mainThreadContinueEvent)
|
public static void Initialize(IntPtr infoPtr, IntPtr mainThreadContinueEvent)
|
||||||
{
|
{
|
||||||
var infoStr = Marshal.PtrToStringUTF8(infoPtr);
|
var infoStr = Marshal.PtrToStringUTF8(infoPtr)!;
|
||||||
var info = JsonConvert.DeserializeObject<DalamudStartInfo>(infoStr);
|
var info = JsonConvert.DeserializeObject<DalamudStartInfo>(infoStr)!;
|
||||||
|
|
||||||
|
if ((info.BootWaitMessageBox & 4) != 0)
|
||||||
|
MessageBoxW(IntPtr.Zero, "Press OK to continue (BeforeDalamudConstruct)", "Dalamud Boot", MessageBoxType.Ok);
|
||||||
|
|
||||||
new Thread(() => RunThread(info, mainThreadContinueEvent)).Start();
|
new Thread(() => RunThread(info, mainThreadContinueEvent)).Start();
|
||||||
}
|
}
|
||||||
|
|
@ -148,9 +151,6 @@ namespace Dalamud
|
||||||
var dalamud = new Dalamud(info, levelSwitch, finishSignal, configuration, mainThreadContinueEvent);
|
var dalamud = new Dalamud(info, levelSwitch, finishSignal, configuration, mainThreadContinueEvent);
|
||||||
Log.Information("This is Dalamud - Core: {GitHash}, CS: {CsGitHash}", Util.GetGitHash(), Util.GetGitHashClientStructs());
|
Log.Information("This is Dalamud - Core: {GitHash}, CS: {CsGitHash}", Util.GetGitHash(), Util.GetGitHashClientStructs());
|
||||||
|
|
||||||
// Run session
|
|
||||||
dalamud.LoadTier1();
|
|
||||||
|
|
||||||
dalamud.WaitForUnload();
|
dalamud.WaitForUnload();
|
||||||
|
|
||||||
dalamud.Dispose();
|
dalamud.Dispose();
|
||||||
|
|
|
||||||
|
|
@ -20,15 +20,6 @@ namespace Dalamud.Game
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected bool IsResolved { get; set; }
|
protected bool IsResolved { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Setup the resolver, calling the appopriate method based on the process architecture.
|
|
||||||
/// </summary>
|
|
||||||
public void Setup()
|
|
||||||
{
|
|
||||||
var scanner = Service<SigScanner>.Get();
|
|
||||||
this.Setup(scanner);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Setup the resolver, calling the appopriate method based on the process architecture.
|
/// Setup the resolver, calling the appopriate method based on the process architecture.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -55,11 +46,13 @@ namespace Dalamud.Game
|
||||||
this.SetupInternal(scanner);
|
this.SetupInternal(scanner);
|
||||||
|
|
||||||
var className = this.GetType().Name;
|
var className = this.GetType().Name;
|
||||||
DebugScannedValues[className] = new List<(string, IntPtr)>();
|
var list = new List<(string, IntPtr)>();
|
||||||
|
lock (DebugScannedValues)
|
||||||
|
DebugScannedValues[className] = list;
|
||||||
|
|
||||||
foreach (var property in this.GetType().GetProperties().Where(x => x.PropertyType == typeof(IntPtr)))
|
foreach (var property in this.GetType().GetProperties().Where(x => x.PropertyType == typeof(IntPtr)))
|
||||||
{
|
{
|
||||||
DebugScannedValues[className].Add((property.Name, (IntPtr)property.GetValue(this)));
|
list.Add((property.Name, (IntPtr)property.GetValue(this)));
|
||||||
}
|
}
|
||||||
|
|
||||||
this.IsResolved = true;
|
this.IsResolved = true;
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@ namespace Dalamud.Game
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[PluginInterface]
|
[PluginInterface]
|
||||||
[InterfaceVersion("1.0")]
|
[InterfaceVersion("1.0")]
|
||||||
|
[ServiceManager.BlockingEarlyLoadedService]
|
||||||
public class ChatHandlers
|
public class ChatHandlers
|
||||||
{
|
{
|
||||||
// private static readonly Dictionary<string, string> UnicodeToDiscordEmojiDict = new()
|
// private static readonly Dictionary<string, string> UnicodeToDiscordEmojiDict = new()
|
||||||
|
|
@ -109,13 +110,9 @@ namespace Dalamud.Game
|
||||||
private bool hasSeenLoadingMsg;
|
private bool hasSeenLoadingMsg;
|
||||||
private bool hasAutoUpdatedPlugins;
|
private bool hasAutoUpdatedPlugins;
|
||||||
|
|
||||||
/// <summary>
|
[ServiceManager.ServiceConstructor]
|
||||||
/// Initializes a new instance of the <see cref="ChatHandlers"/> class.
|
private ChatHandlers(ChatGui chatGui)
|
||||||
/// </summary>
|
|
||||||
internal ChatHandlers()
|
|
||||||
{
|
{
|
||||||
var chatGui = Service<ChatGui>.Get();
|
|
||||||
|
|
||||||
chatGui.CheckMessageHandled += this.OnCheckMessageHandled;
|
chatGui.CheckMessageHandled += this.OnCheckMessageHandled;
|
||||||
chatGui.ChatMessage += this.OnChatMessage;
|
chatGui.ChatMessage += this.OnChatMessage;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -14,18 +14,18 @@ namespace Dalamud.Game.ClientState.Aetherytes
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[PluginInterface]
|
[PluginInterface]
|
||||||
[InterfaceVersion("1.0")]
|
[InterfaceVersion("1.0")]
|
||||||
|
[ServiceManager.BlockingEarlyLoadedService]
|
||||||
public sealed partial class AetheryteList
|
public sealed partial class AetheryteList
|
||||||
{
|
{
|
||||||
|
[ServiceManager.ServiceDependency]
|
||||||
|
private readonly ClientState clientState = Service<ClientState>.Get();
|
||||||
private readonly ClientStateAddressResolver address;
|
private readonly ClientStateAddressResolver address;
|
||||||
private readonly UpdateAetheryteListDelegate updateAetheryteListFunc;
|
private readonly UpdateAetheryteListDelegate updateAetheryteListFunc;
|
||||||
|
|
||||||
/// <summary>
|
[ServiceManager.ServiceConstructor]
|
||||||
/// Initializes a new instance of the <see cref="AetheryteList"/> class.
|
private AetheryteList()
|
||||||
/// </summary>
|
|
||||||
/// <param name="addressResolver">Client state address resolver.</param>
|
|
||||||
internal AetheryteList(ClientStateAddressResolver addressResolver)
|
|
||||||
{
|
{
|
||||||
this.address = addressResolver;
|
this.address = this.clientState.AddressResolver;
|
||||||
this.updateAetheryteListFunc = Marshal.GetDelegateForFunctionPointer<UpdateAetheryteListDelegate>(this.address.UpdateAetheryteList);
|
this.updateAetheryteListFunc = Marshal.GetDelegateForFunctionPointer<UpdateAetheryteListDelegate>(this.address.UpdateAetheryteList);
|
||||||
|
|
||||||
Log.Verbose($"Teleport address 0x{this.address.Telepo.ToInt64():X}");
|
Log.Verbose($"Teleport address 0x{this.address.Telepo.ToInt64():X}");
|
||||||
|
|
@ -40,9 +40,7 @@ namespace Dalamud.Game.ClientState.Aetherytes
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
var clientState = Service<ClientState>.Get();
|
if (this.clientState.LocalPlayer == null)
|
||||||
|
|
||||||
if (clientState.LocalPlayer == null)
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
this.Update();
|
this.Update();
|
||||||
|
|
@ -70,9 +68,7 @@ namespace Dalamud.Game.ClientState.Aetherytes
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
var clientState = Service<ClientState>.Get();
|
if (this.clientState.LocalPlayer == null)
|
||||||
|
|
||||||
if (clientState.LocalPlayer == null)
|
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
return new AetheryteEntry(TelepoStruct->TeleportList.Get((ulong)index));
|
return new AetheryteEntry(TelepoStruct->TeleportList.Get((ulong)index));
|
||||||
|
|
@ -81,10 +77,8 @@ namespace Dalamud.Game.ClientState.Aetherytes
|
||||||
|
|
||||||
private void Update()
|
private void Update()
|
||||||
{
|
{
|
||||||
var clientState = Service<ClientState>.Get();
|
|
||||||
|
|
||||||
// this is very very important as otherwise it crashes
|
// this is very very important as otherwise it crashes
|
||||||
if (clientState.LocalPlayer == null)
|
if (this.clientState.LocalPlayer == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
this.updateAetheryteListFunc(this.address.Telepo, 0);
|
this.updateAetheryteListFunc(this.address.Telepo, 0);
|
||||||
|
|
|
||||||
|
|
@ -15,19 +15,17 @@ namespace Dalamud.Game.ClientState.Buddy
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[PluginInterface]
|
[PluginInterface]
|
||||||
[InterfaceVersion("1.0")]
|
[InterfaceVersion("1.0")]
|
||||||
|
[ServiceManager.BlockingEarlyLoadedService]
|
||||||
public sealed partial class BuddyList
|
public sealed partial class BuddyList
|
||||||
{
|
{
|
||||||
private const uint InvalidObjectID = 0xE0000000;
|
private const uint InvalidObjectID = 0xE0000000;
|
||||||
|
|
||||||
private readonly ClientStateAddressResolver address;
|
private readonly ClientStateAddressResolver address;
|
||||||
|
|
||||||
/// <summary>
|
[ServiceManager.ServiceConstructor]
|
||||||
/// Initializes a new instance of the <see cref="BuddyList"/> class.
|
private BuddyList(ClientState clientState)
|
||||||
/// </summary>
|
|
||||||
/// <param name="addressResolver">Client state address resolver.</param>
|
|
||||||
internal BuddyList(ClientStateAddressResolver addressResolver)
|
|
||||||
{
|
{
|
||||||
this.address = addressResolver;
|
this.address = clientState.AddressResolver;
|
||||||
|
|
||||||
Log.Verbose($"Buddy list address 0x{this.address.BuddyList.ToInt64():X}");
|
Log.Verbose($"Buddy list address 0x{this.address.BuddyList.ToInt64():X}");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,7 @@ namespace Dalamud.Game.ClientState
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[PluginInterface]
|
[PluginInterface]
|
||||||
[InterfaceVersion("1.0")]
|
[InterfaceVersion("1.0")]
|
||||||
|
[ServiceManager.BlockingEarlyLoadedService]
|
||||||
public sealed class ClientState : IDisposable
|
public sealed class ClientState : IDisposable
|
||||||
{
|
{
|
||||||
private readonly ClientStateAddressResolver address;
|
private readonly ClientStateAddressResolver address;
|
||||||
|
|
@ -36,46 +37,26 @@ namespace Dalamud.Game.ClientState
|
||||||
private bool lastFramePvP = false;
|
private bool lastFramePvP = false;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="ClientState"/> class.
|
/// Gets client state address resolver.
|
||||||
/// Set up client state access.
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal ClientState()
|
internal ClientStateAddressResolver AddressResolver => this.address;
|
||||||
|
|
||||||
|
[ServiceManager.ServiceConstructor]
|
||||||
|
private ClientState(Framework framework, NetworkHandlers networkHandlers, SigScanner sigScanner, DalamudStartInfo startInfo)
|
||||||
{
|
{
|
||||||
this.address = new ClientStateAddressResolver();
|
this.address = new ClientStateAddressResolver();
|
||||||
this.address.Setup();
|
this.address.Setup(sigScanner);
|
||||||
|
|
||||||
Log.Verbose("===== C L I E N T S T A T E =====");
|
Log.Verbose("===== C L I E N T S T A T E =====");
|
||||||
|
|
||||||
this.ClientLanguage = Service<DalamudStartInfo>.Get().Language;
|
this.ClientLanguage = startInfo.Language;
|
||||||
|
|
||||||
Service<ObjectTable>.Set(this.address);
|
|
||||||
|
|
||||||
Service<FateTable>.Set(this.address);
|
|
||||||
|
|
||||||
Service<PartyList>.Set(this.address);
|
|
||||||
|
|
||||||
Service<BuddyList>.Set(this.address);
|
|
||||||
|
|
||||||
Service<JobGauges>.Set(this.address);
|
|
||||||
|
|
||||||
Service<KeyState>.Set(this.address);
|
|
||||||
|
|
||||||
Service<GamepadState>.Set(this.address);
|
|
||||||
|
|
||||||
Service<Conditions.Condition>.Set(this.address);
|
|
||||||
|
|
||||||
Service<TargetManager>.Set(this.address);
|
|
||||||
|
|
||||||
Service<AetheryteList>.Set(this.address);
|
|
||||||
|
|
||||||
Log.Verbose($"SetupTerritoryType address 0x{this.address.SetupTerritoryType.ToInt64():X}");
|
Log.Verbose($"SetupTerritoryType address 0x{this.address.SetupTerritoryType.ToInt64():X}");
|
||||||
|
|
||||||
this.setupTerritoryTypeHook = new Hook<SetupTerritoryTypeDelegate>(this.address.SetupTerritoryType, this.SetupTerritoryTypeDetour);
|
this.setupTerritoryTypeHook = new Hook<SetupTerritoryTypeDelegate>(this.address.SetupTerritoryType, this.SetupTerritoryTypeDetour);
|
||||||
|
|
||||||
var framework = Service<Framework>.Get();
|
|
||||||
framework.Update += this.FrameworkOnOnUpdateEvent;
|
framework.Update += this.FrameworkOnOnUpdateEvent;
|
||||||
|
|
||||||
var networkHandlers = Service<NetworkHandlers>.Get();
|
|
||||||
networkHandlers.CfPop += this.NetworkHandlersOnCfPop;
|
networkHandlers.CfPop += this.NetworkHandlersOnCfPop;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ namespace Dalamud.Game.ClientState.Conditions
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[PluginInterface]
|
[PluginInterface]
|
||||||
[InterfaceVersion("1.0")]
|
[InterfaceVersion("1.0")]
|
||||||
|
[ServiceManager.BlockingEarlyLoadedService]
|
||||||
public sealed partial class Condition
|
public sealed partial class Condition
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -20,12 +21,10 @@ namespace Dalamud.Game.ClientState.Conditions
|
||||||
|
|
||||||
private readonly bool[] cache = new bool[MaxConditionEntries];
|
private readonly bool[] cache = new bool[MaxConditionEntries];
|
||||||
|
|
||||||
/// <summary>
|
[ServiceManager.ServiceConstructor]
|
||||||
/// Initializes a new instance of the <see cref="Condition"/> class.
|
private Condition(ClientState clientState)
|
||||||
/// </summary>
|
|
||||||
/// <param name="resolver">The ClientStateAddressResolver instance.</param>
|
|
||||||
internal Condition(ClientStateAddressResolver resolver)
|
|
||||||
{
|
{
|
||||||
|
var resolver = clientState.AddressResolver;
|
||||||
this.Address = resolver.ConditionFlags;
|
this.Address = resolver.ConditionFlags;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,17 +13,15 @@ namespace Dalamud.Game.ClientState.Fates
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[PluginInterface]
|
[PluginInterface]
|
||||||
[InterfaceVersion("1.0")]
|
[InterfaceVersion("1.0")]
|
||||||
|
[ServiceManager.BlockingEarlyLoadedService]
|
||||||
public sealed partial class FateTable
|
public sealed partial class FateTable
|
||||||
{
|
{
|
||||||
private readonly ClientStateAddressResolver address;
|
private readonly ClientStateAddressResolver address;
|
||||||
|
|
||||||
/// <summary>
|
[ServiceManager.ServiceConstructor]
|
||||||
/// Initializes a new instance of the <see cref="FateTable"/> class.
|
private FateTable(ClientState clientState)
|
||||||
/// </summary>
|
|
||||||
/// <param name="addressResolver">Client state address resolver.</param>
|
|
||||||
internal FateTable(ClientStateAddressResolver addressResolver)
|
|
||||||
{
|
{
|
||||||
this.address = addressResolver;
|
this.address = clientState.AddressResolver;
|
||||||
|
|
||||||
Log.Verbose($"Fate table address 0x{this.address.FateTablePtr.ToInt64():X}");
|
Log.Verbose($"Fate table address 0x{this.address.FateTablePtr.ToInt64():X}");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ namespace Dalamud.Game.ClientState.GamePad
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[PluginInterface]
|
[PluginInterface]
|
||||||
[InterfaceVersion("1.0.0")]
|
[InterfaceVersion("1.0.0")]
|
||||||
|
[ServiceManager.BlockingEarlyLoadedService]
|
||||||
public unsafe class GamepadState : IDisposable
|
public unsafe class GamepadState : IDisposable
|
||||||
{
|
{
|
||||||
private readonly Hook<ControllerPoll> gamepadPoll;
|
private readonly Hook<ControllerPoll> gamepadPoll;
|
||||||
|
|
@ -26,12 +27,10 @@ namespace Dalamud.Game.ClientState.GamePad
|
||||||
private int rightStickX;
|
private int rightStickX;
|
||||||
private int rightStickY;
|
private int rightStickY;
|
||||||
|
|
||||||
/// <summary>
|
[ServiceManager.ServiceConstructor]
|
||||||
/// Initializes a new instance of the <see cref="GamepadState" /> class.
|
private GamepadState(ClientState clientState)
|
||||||
/// </summary>
|
|
||||||
/// <param name="resolver">Resolver knowing the pointer to the GamepadPoll function.</param>
|
|
||||||
public GamepadState(ClientStateAddressResolver resolver)
|
|
||||||
{
|
{
|
||||||
|
var resolver = clientState.AddressResolver;
|
||||||
Log.Verbose($"GamepadPoll address 0x{resolver.GamepadPoll.ToInt64():X}");
|
Log.Verbose($"GamepadPoll address 0x{resolver.GamepadPoll.ToInt64():X}");
|
||||||
this.gamepadPoll = new Hook<ControllerPoll>(resolver.GamepadPoll, this.GamepadPollDetour);
|
this.gamepadPoll = new Hook<ControllerPoll>(resolver.GamepadPoll, this.GamepadPollDetour);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,17 +14,15 @@ namespace Dalamud.Game.ClientState.JobGauge
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[PluginInterface]
|
[PluginInterface]
|
||||||
[InterfaceVersion("1.0")]
|
[InterfaceVersion("1.0")]
|
||||||
|
[ServiceManager.BlockingEarlyLoadedService]
|
||||||
public class JobGauges
|
public class JobGauges
|
||||||
{
|
{
|
||||||
private Dictionary<Type, JobGaugeBase> cache = new();
|
private Dictionary<Type, JobGaugeBase> cache = new();
|
||||||
|
|
||||||
/// <summary>
|
[ServiceManager.ServiceConstructor]
|
||||||
/// Initializes a new instance of the <see cref="JobGauges"/> class.
|
private JobGauges(ClientState clientState)
|
||||||
/// </summary>
|
|
||||||
/// <param name="addressResolver">Address resolver with the JobGauge memory location(s).</param>
|
|
||||||
public JobGauges(ClientStateAddressResolver addressResolver)
|
|
||||||
{
|
{
|
||||||
this.Address = addressResolver.JobGaugeData;
|
this.Address = clientState.AddressResolver.JobGaugeData;
|
||||||
|
|
||||||
Log.Verbose($"JobGaugeData address 0x{this.Address.ToInt64():X}");
|
Log.Verbose($"JobGaugeData address 0x{this.Address.ToInt64():X}");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ namespace Dalamud.Game.ClientState.Keys
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
[PluginInterface]
|
[PluginInterface]
|
||||||
[InterfaceVersion("1.0")]
|
[InterfaceVersion("1.0")]
|
||||||
|
[ServiceManager.BlockingEarlyLoadedService]
|
||||||
public class KeyState
|
public class KeyState
|
||||||
{
|
{
|
||||||
// The array is accessed in a way that this limit doesn't appear to exist
|
// The array is accessed in a way that this limit doesn't appear to exist
|
||||||
|
|
@ -32,14 +33,11 @@ namespace Dalamud.Game.ClientState.Keys
|
||||||
private readonly IntPtr indexBase;
|
private readonly IntPtr indexBase;
|
||||||
private VirtualKey[] validVirtualKeyCache = null;
|
private VirtualKey[] validVirtualKeyCache = null;
|
||||||
|
|
||||||
/// <summary>
|
[ServiceManager.ServiceConstructor]
|
||||||
/// Initializes a new instance of the <see cref="KeyState"/> class.
|
private KeyState(SigScanner sigScanner, ClientState clientState)
|
||||||
/// </summary>
|
|
||||||
/// <param name="addressResolver">The ClientStateAddressResolver instance.</param>
|
|
||||||
public KeyState(ClientStateAddressResolver addressResolver)
|
|
||||||
{
|
{
|
||||||
var moduleBaseAddress = Service<SigScanner>.Get().Module.BaseAddress;
|
var moduleBaseAddress = sigScanner.Module.BaseAddress;
|
||||||
|
var addressResolver = clientState.AddressResolver;
|
||||||
this.bufferBase = moduleBaseAddress + Marshal.ReadInt32(addressResolver.KeyboardState);
|
this.bufferBase = moduleBaseAddress + Marshal.ReadInt32(addressResolver.KeyboardState);
|
||||||
this.indexBase = moduleBaseAddress + Marshal.ReadInt32(addressResolver.KeyboardStateIndexArray);
|
this.indexBase = moduleBaseAddress + Marshal.ReadInt32(addressResolver.KeyboardStateIndexArray);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,19 +16,17 @@ namespace Dalamud.Game.ClientState.Objects
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[PluginInterface]
|
[PluginInterface]
|
||||||
[InterfaceVersion("1.0")]
|
[InterfaceVersion("1.0")]
|
||||||
|
[ServiceManager.BlockingEarlyLoadedService]
|
||||||
public sealed partial class ObjectTable
|
public sealed partial class ObjectTable
|
||||||
{
|
{
|
||||||
private const int ObjectTableLength = 424;
|
private const int ObjectTableLength = 424;
|
||||||
|
|
||||||
private readonly ClientStateAddressResolver address;
|
private readonly ClientStateAddressResolver address;
|
||||||
|
|
||||||
/// <summary>
|
[ServiceManager.ServiceConstructor]
|
||||||
/// Initializes a new instance of the <see cref="ObjectTable"/> class.
|
private ObjectTable(ClientState clientState)
|
||||||
/// </summary>
|
|
||||||
/// <param name="addressResolver">Client state address resolver.</param>
|
|
||||||
internal ObjectTable(ClientStateAddressResolver addressResolver)
|
|
||||||
{
|
{
|
||||||
this.address = addressResolver;
|
this.address = clientState.AddressResolver;
|
||||||
|
|
||||||
Log.Verbose($"Object table address 0x{this.address.ObjectTable.ToInt64():X}");
|
Log.Verbose($"Object table address 0x{this.address.ObjectTable.ToInt64():X}");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,17 +11,15 @@ namespace Dalamud.Game.ClientState.Objects
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[PluginInterface]
|
[PluginInterface]
|
||||||
[InterfaceVersion("1.0")]
|
[InterfaceVersion("1.0")]
|
||||||
|
[ServiceManager.BlockingEarlyLoadedService]
|
||||||
public sealed unsafe class TargetManager
|
public sealed unsafe class TargetManager
|
||||||
{
|
{
|
||||||
private readonly ClientStateAddressResolver address;
|
private readonly ClientStateAddressResolver address;
|
||||||
|
|
||||||
/// <summary>
|
[ServiceManager.ServiceConstructor]
|
||||||
/// Initializes a new instance of the <see cref="TargetManager"/> class.
|
private TargetManager(ClientState clientState)
|
||||||
/// </summary>
|
|
||||||
/// <param name="addressResolver">The ClientStateAddressResolver instance.</param>
|
|
||||||
internal TargetManager(ClientStateAddressResolver addressResolver)
|
|
||||||
{
|
{
|
||||||
this.address = addressResolver;
|
this.address = clientState.AddressResolver;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ namespace Dalamud.Game.ClientState.Party
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[PluginInterface]
|
[PluginInterface]
|
||||||
[InterfaceVersion("1.0")]
|
[InterfaceVersion("1.0")]
|
||||||
|
[ServiceManager.BlockingEarlyLoadedService]
|
||||||
public sealed unsafe partial class PartyList
|
public sealed unsafe partial class PartyList
|
||||||
{
|
{
|
||||||
private const int GroupLength = 8;
|
private const int GroupLength = 8;
|
||||||
|
|
@ -21,13 +22,10 @@ namespace Dalamud.Game.ClientState.Party
|
||||||
|
|
||||||
private readonly ClientStateAddressResolver address;
|
private readonly ClientStateAddressResolver address;
|
||||||
|
|
||||||
/// <summary>
|
[ServiceManager.ServiceConstructor]
|
||||||
/// Initializes a new instance of the <see cref="PartyList"/> class.
|
private PartyList(ClientState clientState)
|
||||||
/// </summary>
|
|
||||||
/// <param name="addressResolver">Client state address resolver.</param>
|
|
||||||
internal PartyList(ClientStateAddressResolver addressResolver)
|
|
||||||
{
|
{
|
||||||
this.address = addressResolver;
|
this.address = clientState.AddressResolver;
|
||||||
|
|
||||||
Log.Verbose($"Group manager address 0x{this.address.GroupManager.ToInt64():X}");
|
Log.Verbose($"Group manager address 0x{this.address.GroupManager.ToInt64():X}");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@ namespace Dalamud.Game.Command
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[PluginInterface]
|
[PluginInterface]
|
||||||
[InterfaceVersion("1.0")]
|
[InterfaceVersion("1.0")]
|
||||||
|
[ServiceManager.BlockingEarlyLoadedService]
|
||||||
public sealed class CommandManager
|
public sealed class CommandManager
|
||||||
{
|
{
|
||||||
private readonly Dictionary<string, CommandInfo> commandMap = new();
|
private readonly Dictionary<string, CommandInfo> commandMap = new();
|
||||||
|
|
@ -27,13 +28,9 @@ namespace Dalamud.Game.Command
|
||||||
private readonly Regex commandRegexCn = new(@"^^(“|「)(?<command>.+)(”|」)(出现问题:该命令不存在|出現問題:該命令不存在)。$", RegexOptions.Compiled);
|
private readonly Regex commandRegexCn = new(@"^^(“|「)(?<command>.+)(”|」)(出现问题:该命令不存在|出現問題:該命令不存在)。$", RegexOptions.Compiled);
|
||||||
private readonly Regex currentLangCommandRegex;
|
private readonly Regex currentLangCommandRegex;
|
||||||
|
|
||||||
/// <summary>
|
[ServiceManager.ServiceConstructor]
|
||||||
/// Initializes a new instance of the <see cref="CommandManager"/> class.
|
private CommandManager(DalamudStartInfo startInfo)
|
||||||
/// </summary>
|
|
||||||
internal CommandManager()
|
|
||||||
{
|
{
|
||||||
var startInfo = Service<DalamudStartInfo>.Get();
|
|
||||||
|
|
||||||
this.currentLangCommandRegex = startInfo.Language switch
|
this.currentLangCommandRegex = startInfo.Language switch
|
||||||
{
|
{
|
||||||
ClientLanguage.Japanese => this.commandRegexJp,
|
ClientLanguage.Japanese => this.commandRegexJp,
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ namespace Dalamud.Game
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[PluginInterface]
|
[PluginInterface]
|
||||||
[InterfaceVersion("1.0")]
|
[InterfaceVersion("1.0")]
|
||||||
|
[ServiceManager.BlockingEarlyLoadedService]
|
||||||
public sealed class Framework : IDisposable
|
public sealed class Framework : IDisposable
|
||||||
{
|
{
|
||||||
private static Stopwatch statsStopwatch = new();
|
private static Stopwatch statsStopwatch = new();
|
||||||
|
|
@ -31,27 +32,28 @@ namespace Dalamud.Game
|
||||||
private readonly List<RunOnNextTickTaskBase> runOnNextTickTaskList = new();
|
private readonly List<RunOnNextTickTaskBase> runOnNextTickTaskList = new();
|
||||||
private readonly Stopwatch updateStopwatch = new();
|
private readonly Stopwatch updateStopwatch = new();
|
||||||
|
|
||||||
private bool tier2Initialized = false;
|
|
||||||
private bool tier3Initialized = false;
|
|
||||||
private bool tierInitError = false;
|
|
||||||
|
|
||||||
private Hook<OnUpdateDetour> updateHook;
|
private Hook<OnUpdateDetour> updateHook;
|
||||||
private Hook<OnDestroyDetour> freeHook;
|
private Hook<OnDestroyDetour> freeHook;
|
||||||
private Hook<OnRealDestroyDelegate> destroyHook;
|
private Hook<OnRealDestroyDelegate> destroyHook;
|
||||||
|
|
||||||
private Thread? frameworkUpdateThread;
|
private Thread? frameworkUpdateThread;
|
||||||
|
|
||||||
/// <summary>
|
[ServiceManager.ServiceConstructor]
|
||||||
/// Initializes a new instance of the <see cref="Framework"/> class.
|
private Framework(GameGui gameGui, GameNetwork gameNetwork, SigScanner sigScanner)
|
||||||
/// </summary>
|
|
||||||
internal Framework()
|
|
||||||
{
|
{
|
||||||
this.Address = new FrameworkAddressResolver();
|
this.Address = new FrameworkAddressResolver();
|
||||||
this.Address.Setup();
|
this.Address.Setup(sigScanner);
|
||||||
|
|
||||||
this.updateHook = new Hook<OnUpdateDetour>(this.Address.TickAddress, this.HandleFrameworkUpdate);
|
this.updateHook = new Hook<OnUpdateDetour>(this.Address.TickAddress, this.HandleFrameworkUpdate);
|
||||||
this.freeHook = new Hook<OnDestroyDetour>(this.Address.FreeAddress, this.HandleFrameworkFree);
|
this.freeHook = new Hook<OnDestroyDetour>(this.Address.FreeAddress, this.HandleFrameworkFree);
|
||||||
this.destroyHook = new Hook<OnRealDestroyDelegate>(this.Address.DestroyAddress, this.HandleFrameworkDestroy);
|
this.destroyHook = new Hook<OnRealDestroyDelegate>(this.Address.DestroyAddress, this.HandleFrameworkDestroy);
|
||||||
|
|
||||||
|
gameGui.Enable();
|
||||||
|
gameNetwork.Enable();
|
||||||
|
|
||||||
|
this.updateHook.Enable();
|
||||||
|
this.freeHook.Enable();
|
||||||
|
this.destroyHook.Enable();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -123,20 +125,6 @@ namespace Dalamud.Game
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal bool DispatchUpdateEvents { get; set; } = true;
|
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.freeHook.Enable();
|
|
||||||
this.destroyHook.Enable();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Run given function right away if this function has been called from game's Framework.Update thread, or otherwise run on next Framework.Update call.
|
/// Run given function right away if this function has been called from game's Framework.Update thread, or otherwise run on next Framework.Update call.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -238,39 +226,21 @@ namespace Dalamud.Game
|
||||||
|
|
||||||
private bool HandleFrameworkUpdate(IntPtr framework)
|
private bool HandleFrameworkUpdate(IntPtr framework)
|
||||||
{
|
{
|
||||||
// If any of the tier loads failed, just go to the original code.
|
|
||||||
if (this.tierInitError)
|
|
||||||
goto original;
|
|
||||||
|
|
||||||
this.frameworkUpdateThread ??= Thread.CurrentThread;
|
this.frameworkUpdateThread ??= Thread.CurrentThread;
|
||||||
|
|
||||||
var dalamud = Service<Dalamud>.Get();
|
ThreadSafety.MarkMainThread();
|
||||||
|
|
||||||
// If this is the first time we are running this loop, we need to init Dalamud subsystems synchronously
|
|
||||||
if (!this.tier2Initialized)
|
|
||||||
{
|
|
||||||
this.tier2Initialized = dalamud.LoadTier2();
|
|
||||||
if (!this.tier2Initialized)
|
|
||||||
this.tierInitError = true;
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
goto original;
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Service<ChatGui>.Get().UpdateQueue();
|
var chatGui = Service<ChatGui>.GetNullable();
|
||||||
Service<ToastGui>.Get().UpdateQueue();
|
var toastGui = Service<ToastGui>.GetNullable();
|
||||||
Service<GameNetwork>.Get().UpdateQueue();
|
var gameNetwork = Service<GameNetwork>.GetNullable();
|
||||||
|
if (chatGui == null || toastGui == null || gameNetwork == null)
|
||||||
|
goto original;
|
||||||
|
|
||||||
|
chatGui.UpdateQueue();
|
||||||
|
toastGui.UpdateQueue();
|
||||||
|
gameNetwork.UpdateQueue();
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ namespace Dalamud.Game.Gui
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[PluginInterface]
|
[PluginInterface]
|
||||||
[InterfaceVersion("1.0")]
|
[InterfaceVersion("1.0")]
|
||||||
|
[ServiceManager.BlockingEarlyLoadedService]
|
||||||
public sealed class ChatGui : IDisposable
|
public sealed class ChatGui : IDisposable
|
||||||
{
|
{
|
||||||
private readonly ChatGuiAddressResolver address;
|
private readonly ChatGuiAddressResolver address;
|
||||||
|
|
@ -34,14 +35,11 @@ namespace Dalamud.Game.Gui
|
||||||
|
|
||||||
private IntPtr baseAddress = IntPtr.Zero;
|
private IntPtr baseAddress = IntPtr.Zero;
|
||||||
|
|
||||||
/// <summary>
|
[ServiceManager.ServiceConstructor]
|
||||||
/// Initializes a new instance of the <see cref="ChatGui"/> class.
|
private ChatGui(SigScanner sigScanner)
|
||||||
/// </summary>
|
|
||||||
/// <param name="baseAddress">The base address of the ChatManager.</param>
|
|
||||||
internal ChatGui()
|
|
||||||
{
|
{
|
||||||
this.address = new ChatGuiAddressResolver();
|
this.address = new ChatGuiAddressResolver();
|
||||||
this.address.Setup();
|
this.address.Setup(sigScanner);
|
||||||
|
|
||||||
this.printMessageHook = new Hook<PrintMessageDelegate>(this.address.PrintMessage, this.HandlePrintMessageDetour);
|
this.printMessageHook = new Hook<PrintMessageDelegate>(this.address.PrintMessage, this.HandlePrintMessageDetour);
|
||||||
this.populateItemLinkHook = new Hook<PopulateItemLinkDelegate>(this.address.PopulateItemLinkObject, this.HandlePopulateItemLinkDetour);
|
this.populateItemLinkHook = new Hook<PopulateItemLinkDelegate>(this.address.PopulateItemLinkObject, this.HandlePopulateItemLinkDetour);
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ namespace Dalamud.Game.Gui.ContextMenus
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[PluginInterface]
|
[PluginInterface]
|
||||||
[InterfaceVersion("1.0")]
|
[InterfaceVersion("1.0")]
|
||||||
|
[ServiceManager.BlockingEarlyLoadedService]
|
||||||
public sealed class ContextMenu : IDisposable
|
public sealed class ContextMenu : IDisposable
|
||||||
{
|
{
|
||||||
private const int MaxContextMenuItemsPerContextMenu = 32;
|
private const int MaxContextMenuItemsPerContextMenu = 32;
|
||||||
|
|
@ -47,13 +48,11 @@ namespace Dalamud.Game.Gui.ContextMenus
|
||||||
private OpenSubContextMenuItem? selectedOpenSubContextMenuItem;
|
private OpenSubContextMenuItem? selectedOpenSubContextMenuItem;
|
||||||
private ContextMenuOpenedArgs? currentContextMenuOpenedArgs;
|
private ContextMenuOpenedArgs? currentContextMenuOpenedArgs;
|
||||||
|
|
||||||
/// <summary>
|
[ServiceManager.ServiceConstructor]
|
||||||
/// Initializes a new instance of the <see cref="ContextMenu"/> class.
|
private ContextMenu(SigScanner sigScanner)
|
||||||
/// </summary>
|
|
||||||
public ContextMenu()
|
|
||||||
{
|
{
|
||||||
this.Address = new ContextMenuAddressResolver();
|
this.Address = new ContextMenuAddressResolver();
|
||||||
this.Address.Setup();
|
this.Address.Setup(sigScanner);
|
||||||
|
|
||||||
unsafe
|
unsafe
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@ namespace Dalamud.Game.Gui.Dtr
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[PluginInterface]
|
[PluginInterface]
|
||||||
[InterfaceVersion("1.0")]
|
[InterfaceVersion("1.0")]
|
||||||
|
[ServiceManager.BlockingEarlyLoadedService]
|
||||||
public sealed unsafe class DtrBar : IDisposable
|
public sealed unsafe class DtrBar : IDisposable
|
||||||
{
|
{
|
||||||
private const uint BaseNodeId = 1000;
|
private const uint BaseNodeId = 1000;
|
||||||
|
|
@ -24,13 +25,10 @@ namespace Dalamud.Game.Gui.Dtr
|
||||||
private List<DtrBarEntry> entries = new();
|
private List<DtrBarEntry> entries = new();
|
||||||
private uint runningNodeIds = BaseNodeId;
|
private uint runningNodeIds = BaseNodeId;
|
||||||
|
|
||||||
/// <summary>
|
[ServiceManager.ServiceConstructor]
|
||||||
/// Initializes a new instance of the <see cref="DtrBar"/> class.
|
private DtrBar(DalamudConfiguration configuration)
|
||||||
/// </summary>
|
|
||||||
internal DtrBar()
|
|
||||||
{
|
{
|
||||||
Service<Framework>.Get().Update += this.Update;
|
Service<Framework>.Get().Update += this.Update;
|
||||||
var configuration = Service<DalamudConfiguration>.Get();
|
|
||||||
|
|
||||||
configuration.DtrOrder ??= new List<string>();
|
configuration.DtrOrder ??= new List<string>();
|
||||||
configuration.DtrIgnore ??= new List<string>();
|
configuration.DtrIgnore ??= new List<string>();
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ namespace Dalamud.Game.Gui.FlyText
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[PluginInterface]
|
[PluginInterface]
|
||||||
[InterfaceVersion("1.0")]
|
[InterfaceVersion("1.0")]
|
||||||
|
[ServiceManager.BlockingEarlyLoadedService]
|
||||||
public sealed class FlyTextGui : IDisposable
|
public sealed class FlyTextGui : IDisposable
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -28,13 +29,11 @@ namespace Dalamud.Game.Gui.FlyText
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private readonly Hook<CreateFlyTextDelegate> createFlyTextHook;
|
private readonly Hook<CreateFlyTextDelegate> createFlyTextHook;
|
||||||
|
|
||||||
/// <summary>
|
[ServiceManager.ServiceConstructor]
|
||||||
/// Initializes a new instance of the <see cref="FlyTextGui"/> class.
|
private FlyTextGui(SigScanner sigScanner)
|
||||||
/// </summary>
|
|
||||||
internal FlyTextGui()
|
|
||||||
{
|
{
|
||||||
this.Address = new FlyTextGuiAddressResolver();
|
this.Address = new FlyTextGuiAddressResolver();
|
||||||
this.Address.Setup();
|
this.Address.Setup(sigScanner);
|
||||||
|
|
||||||
this.addFlyTextNative = Marshal.GetDelegateForFunctionPointer<AddFlyTextDelegate>(this.Address.AddFlyText);
|
this.addFlyTextNative = Marshal.GetDelegateForFunctionPointer<AddFlyTextDelegate>(this.Address.AddFlyText);
|
||||||
this.createFlyTextHook = new Hook<CreateFlyTextDelegate>(this.Address.CreateFlyText, this.CreateFlyTextDetour);
|
this.createFlyTextHook = new Hook<CreateFlyTextDelegate>(this.Address.CreateFlyText, this.CreateFlyTextDetour);
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,7 @@ namespace Dalamud.Game.Gui
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[PluginInterface]
|
[PluginInterface]
|
||||||
[InterfaceVersion("1.0")]
|
[InterfaceVersion("1.0")]
|
||||||
|
[ServiceManager.BlockingEarlyLoadedService]
|
||||||
public sealed unsafe class GameGui : IDisposable
|
public sealed unsafe class GameGui : IDisposable
|
||||||
{
|
{
|
||||||
private readonly GameGuiAddressResolver address;
|
private readonly GameGuiAddressResolver address;
|
||||||
|
|
@ -46,14 +47,11 @@ namespace Dalamud.Game.Gui
|
||||||
private GetUIMapObjectDelegate getUIMapObject;
|
private GetUIMapObjectDelegate getUIMapObject;
|
||||||
private OpenMapWithFlagDelegate openMapWithFlag;
|
private OpenMapWithFlagDelegate openMapWithFlag;
|
||||||
|
|
||||||
/// <summary>
|
[ServiceManager.ServiceConstructor]
|
||||||
/// Initializes a new instance of the <see cref="GameGui"/> class.
|
private GameGui(SigScanner sigScanner)
|
||||||
/// This class is responsible for many aspects of interacting with the native game UI.
|
|
||||||
/// </summary>
|
|
||||||
internal GameGui()
|
|
||||||
{
|
{
|
||||||
this.address = new GameGuiAddressResolver();
|
this.address = new GameGuiAddressResolver();
|
||||||
this.address.Setup();
|
this.address.Setup(sigScanner);
|
||||||
|
|
||||||
Log.Verbose("===== G A M E G U I =====");
|
Log.Verbose("===== G A M E G U I =====");
|
||||||
Log.Verbose($"GameGuiManager address 0x{this.address.BaseAddress.ToInt64():X}");
|
Log.Verbose($"GameGuiManager address 0x{this.address.BaseAddress.ToInt64():X}");
|
||||||
|
|
@ -62,13 +60,6 @@ namespace Dalamud.Game.Gui
|
||||||
Log.Verbose($"HandleItemOut address 0x{this.address.HandleItemOut.ToInt64():X}");
|
Log.Verbose($"HandleItemOut address 0x{this.address.HandleItemOut.ToInt64():X}");
|
||||||
Log.Verbose($"HandleImm address 0x{this.address.HandleImm.ToInt64():X}");
|
Log.Verbose($"HandleImm address 0x{this.address.HandleImm.ToInt64():X}");
|
||||||
|
|
||||||
Service<ChatGui>.Set();
|
|
||||||
Service<PartyFinderGui>.Set();
|
|
||||||
Service<ToastGui>.Set();
|
|
||||||
Service<FlyTextGui>.Set();
|
|
||||||
Service<ContextMenu>.Set();
|
|
||||||
Service<DtrBar>.Set();
|
|
||||||
|
|
||||||
this.setGlobalBgmHook = new Hook<SetGlobalBgmDelegate>(this.address.SetGlobalBgm, this.HandleSetGlobalBgmDetour);
|
this.setGlobalBgmHook = new Hook<SetGlobalBgmDelegate>(this.address.SetGlobalBgm, this.HandleSetGlobalBgmDetour);
|
||||||
|
|
||||||
this.handleItemHoverHook = new Hook<HandleItemHoverDelegate>(this.address.HandleItemHover, this.HandleItemHoverDetour);
|
this.handleItemHoverHook = new Hook<HandleItemHoverDelegate>(this.address.HandleItemHover, this.HandleItemHoverDetour);
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ namespace Dalamud.Game.Gui.Internal
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// This class handles IME for non-English users.
|
/// This class handles IME for non-English users.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[ServiceManager.EarlyLoadedService]
|
||||||
internal unsafe class DalamudIME : IDisposable
|
internal unsafe class DalamudIME : IDisposable
|
||||||
{
|
{
|
||||||
private static readonly ModuleLog Log = new("IME");
|
private static readonly ModuleLog Log = new("IME");
|
||||||
|
|
@ -32,7 +33,9 @@ namespace Dalamud.Game.Gui.Internal
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="DalamudIME"/> class.
|
/// Initializes a new instance of the <see cref="DalamudIME"/> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal DalamudIME()
|
/// <param name="tag">Tag.</param>
|
||||||
|
[ServiceManager.ServiceConstructor]
|
||||||
|
private DalamudIME()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ namespace Dalamud.Game.Gui.PartyFinder
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[PluginInterface]
|
[PluginInterface]
|
||||||
[InterfaceVersion("1.0")]
|
[InterfaceVersion("1.0")]
|
||||||
|
[ServiceManager.BlockingEarlyLoadedService]
|
||||||
public sealed class PartyFinderGui : IDisposable
|
public sealed class PartyFinderGui : IDisposable
|
||||||
{
|
{
|
||||||
private readonly PartyFinderAddressResolver address;
|
private readonly PartyFinderAddressResolver address;
|
||||||
|
|
@ -25,10 +26,12 @@ namespace Dalamud.Game.Gui.PartyFinder
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="PartyFinderGui"/> class.
|
/// Initializes a new instance of the <see cref="PartyFinderGui"/> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal PartyFinderGui()
|
/// <param name="tag">Tag.</param>
|
||||||
|
[ServiceManager.ServiceConstructor]
|
||||||
|
private PartyFinderGui(SigScanner sigScanner)
|
||||||
{
|
{
|
||||||
this.address = new PartyFinderAddressResolver();
|
this.address = new PartyFinderAddressResolver();
|
||||||
this.address.Setup();
|
this.address.Setup(sigScanner);
|
||||||
|
|
||||||
this.memory = Marshal.AllocHGlobal(PartyFinderPacket.PacketSize);
|
this.memory = Marshal.AllocHGlobal(PartyFinderPacket.PacketSize);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ namespace Dalamud.Game.Gui.Toast
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[PluginInterface]
|
[PluginInterface]
|
||||||
[InterfaceVersion("1.0")]
|
[InterfaceVersion("1.0")]
|
||||||
|
[ServiceManager.BlockingEarlyLoadedService]
|
||||||
public sealed partial class ToastGui : IDisposable
|
public sealed partial class ToastGui : IDisposable
|
||||||
{
|
{
|
||||||
private const uint QuestToastCheckmarkMagic = 60081;
|
private const uint QuestToastCheckmarkMagic = 60081;
|
||||||
|
|
@ -31,10 +32,12 @@ namespace Dalamud.Game.Gui.Toast
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="ToastGui"/> class.
|
/// Initializes a new instance of the <see cref="ToastGui"/> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal ToastGui()
|
/// <param name="tag">Tag.</param>
|
||||||
|
[ServiceManager.ServiceConstructor]
|
||||||
|
private ToastGui(SigScanner sigScanner)
|
||||||
{
|
{
|
||||||
this.address = new ToastGuiAddressResolver();
|
this.address = new ToastGuiAddressResolver();
|
||||||
this.address.Setup();
|
this.address.Setup(sigScanner);
|
||||||
|
|
||||||
this.showNormalToastHook = new Hook<ShowNormalToastDelegate>(this.address.ShowNormalToast, new ShowNormalToastDelegate(this.HandleNormalToastDetour));
|
this.showNormalToastHook = new Hook<ShowNormalToastDelegate>(this.address.ShowNormalToast, new ShowNormalToastDelegate(this.HandleNormalToastDetour));
|
||||||
this.showQuestToastHook = new Hook<ShowQuestToastDelegate>(this.address.ShowQuestToast, new ShowQuestToastDelegate(this.HandleQuestToastDetour));
|
this.showQuestToastHook = new Hook<ShowQuestToastDelegate>(this.address.ShowQuestToast, new ShowQuestToastDelegate(this.HandleQuestToastDetour));
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using Dalamud.Configuration.Internal;
|
||||||
using Serilog;
|
using Serilog;
|
||||||
|
|
||||||
namespace Dalamud.Game.Internal
|
namespace Dalamud.Game.Internal
|
||||||
|
|
@ -8,22 +8,19 @@ namespace Dalamud.Game.Internal
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// This class disables anti-debug functionality in the game client.
|
/// This class disables anti-debug functionality in the game client.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[ServiceManager.EarlyLoadedService]
|
||||||
internal sealed partial class AntiDebug
|
internal sealed partial class AntiDebug
|
||||||
{
|
{
|
||||||
private readonly byte[] nop = new byte[] { 0x31, 0xC0, 0x90, 0x90, 0x90, 0x90 };
|
private readonly byte[] nop = new byte[] { 0x31, 0xC0, 0x90, 0x90, 0x90, 0x90 };
|
||||||
private byte[] original;
|
private byte[] original;
|
||||||
private IntPtr debugCheckAddress;
|
private IntPtr debugCheckAddress;
|
||||||
|
|
||||||
/// <summary>
|
[ServiceManager.ServiceConstructor]
|
||||||
/// Initializes a new instance of the <see cref="AntiDebug"/> class.
|
private AntiDebug(SigScanner sigScanner)
|
||||||
/// </summary>
|
|
||||||
public AntiDebug()
|
|
||||||
{
|
{
|
||||||
var scanner = Service<SigScanner>.Get();
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
this.debugCheckAddress = scanner.ScanText("FF 15 ?? ?? ?? ?? 85 C0 74 11 41");
|
this.debugCheckAddress = sigScanner.ScanText("FF 15 ?? ?? ?? ?? 85 C0 74 11 41");
|
||||||
}
|
}
|
||||||
catch (KeyNotFoundException)
|
catch (KeyNotFoundException)
|
||||||
{
|
{
|
||||||
|
|
@ -31,6 +28,16 @@ namespace Dalamud.Game.Internal
|
||||||
}
|
}
|
||||||
|
|
||||||
Log.Verbose($"Debug check address 0x{this.debugCheckAddress.ToInt64():X}");
|
Log.Verbose($"Debug check address 0x{this.debugCheckAddress.ToInt64():X}");
|
||||||
|
|
||||||
|
if (!this.IsEnabled)
|
||||||
|
{
|
||||||
|
#if DEBUG
|
||||||
|
this.Enable();
|
||||||
|
#else
|
||||||
|
if (Service<DalamudConfiguration>.Get().IsAntiAntiDebugEnabled)
|
||||||
|
this.Enable();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ using System.Diagnostics;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
using Dalamud.Game.Internal.DXGI.Definitions;
|
using Dalamud.Game.Internal.DXGI.Definitions;
|
||||||
|
using FFXIVClientStructs.FFXIV.Client.Graphics.Kernel;
|
||||||
using Serilog;
|
using Serilog;
|
||||||
|
|
||||||
namespace Dalamud.Game.Internal.DXGI
|
namespace Dalamud.Game.Internal.DXGI
|
||||||
|
|
@ -30,9 +31,28 @@ namespace Dalamud.Game.Internal.DXGI
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
protected override unsafe void Setup64Bit(SigScanner sig)
|
protected override unsafe void Setup64Bit(SigScanner sig)
|
||||||
{
|
{
|
||||||
var kernelDev = FFXIVClientStructs.FFXIV.Client.Graphics.Kernel.Device.Instance();
|
Device* kernelDev;
|
||||||
|
SwapChain* swapChain;
|
||||||
|
void* dxgiSwapChain;
|
||||||
|
|
||||||
var scVtbl = GetVTblAddresses(new IntPtr(kernelDev->SwapChain->DXGISwapChain), Enum.GetValues(typeof(IDXGISwapChainVtbl)).Length);
|
while (true)
|
||||||
|
{
|
||||||
|
kernelDev = Device.Instance();
|
||||||
|
if (kernelDev == null)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
swapChain = kernelDev->SwapChain;
|
||||||
|
if (swapChain == null)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
dxgiSwapChain = swapChain->DXGISwapChain;
|
||||||
|
if (dxgiSwapChain == null)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
var scVtbl = GetVTblAddresses(new IntPtr(dxgiSwapChain), Enum.GetValues(typeof(IDXGISwapChainVtbl)).Length);
|
||||||
|
|
||||||
this.Present = scVtbl[(int)IDXGISwapChainVtbl.Present];
|
this.Present = scVtbl[(int)IDXGISwapChainVtbl.Present];
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ namespace Dalamud.Game.Internal
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// This class implements in-game Dalamud options in the in-game System menu.
|
/// This class implements in-game Dalamud options in the in-game System menu.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[ServiceManager.EarlyLoadedService]
|
||||||
internal sealed unsafe partial class DalamudAtkTweaks
|
internal sealed unsafe partial class DalamudAtkTweaks
|
||||||
{
|
{
|
||||||
private readonly AtkValueChangeType atkValueChangeType;
|
private readonly AtkValueChangeType atkValueChangeType;
|
||||||
|
|
@ -37,13 +38,9 @@ namespace Dalamud.Game.Internal
|
||||||
private readonly string locDalamudPlugins;
|
private readonly string locDalamudPlugins;
|
||||||
private readonly string locDalamudSettings;
|
private readonly string locDalamudSettings;
|
||||||
|
|
||||||
/// <summary>
|
[ServiceManager.ServiceConstructor]
|
||||||
/// Initializes a new instance of the <see cref="DalamudAtkTweaks"/> class.
|
private DalamudAtkTweaks(SigScanner sigScanner)
|
||||||
/// </summary>
|
|
||||||
public DalamudAtkTweaks()
|
|
||||||
{
|
{
|
||||||
var sigScanner = Service<SigScanner>.Get();
|
|
||||||
|
|
||||||
var openSystemMenuAddress = sigScanner.ScanText("E8 ?? ?? ?? ?? 32 C0 4C 8B AC 24 ?? ?? ?? ?? 48 8B 8D ?? ?? ?? ??");
|
var openSystemMenuAddress = sigScanner.ScanText("E8 ?? ?? ?? ?? 32 C0 4C 8B AC 24 ?? ?? ?? ?? 48 8B 8D ?? ?? ?? ??");
|
||||||
|
|
||||||
this.hookAgentHudOpenSystemMenu = new Hook<AgentHudOpenSystemMenuPrototype>(openSystemMenuAddress, this.AgentHudOpenSystemMenuDetour);
|
this.hookAgentHudOpenSystemMenu = new Hook<AgentHudOpenSystemMenuPrototype>(openSystemMenuAddress, this.AgentHudOpenSystemMenuDetour);
|
||||||
|
|
|
||||||
|
|
@ -12,19 +12,18 @@ namespace Dalamud.Game.Libc
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[PluginInterface]
|
[PluginInterface]
|
||||||
[InterfaceVersion("1.0")]
|
[InterfaceVersion("1.0")]
|
||||||
|
[ServiceManager.BlockingEarlyLoadedService]
|
||||||
public sealed class LibcFunction
|
public sealed class LibcFunction
|
||||||
{
|
{
|
||||||
private readonly LibcFunctionAddressResolver address;
|
private readonly LibcFunctionAddressResolver address;
|
||||||
private readonly StdStringFromCStringDelegate stdStringCtorCString;
|
private readonly StdStringFromCStringDelegate stdStringCtorCString;
|
||||||
private readonly StdStringDeallocateDelegate stdStringDeallocate;
|
private readonly StdStringDeallocateDelegate stdStringDeallocate;
|
||||||
|
|
||||||
/// <summary>
|
[ServiceManager.ServiceConstructor]
|
||||||
/// Initializes a new instance of the <see cref="LibcFunction"/> class.
|
private LibcFunction(SigScanner sigScanner)
|
||||||
/// </summary>
|
|
||||||
public LibcFunction()
|
|
||||||
{
|
{
|
||||||
this.address = new LibcFunctionAddressResolver();
|
this.address = new LibcFunctionAddressResolver();
|
||||||
this.address.Setup();
|
this.address.Setup(sigScanner);
|
||||||
|
|
||||||
this.stdStringCtorCString = Marshal.GetDelegateForFunctionPointer<StdStringFromCStringDelegate>(this.address.StdStringFromCstring);
|
this.stdStringCtorCString = Marshal.GetDelegateForFunctionPointer<StdStringFromCStringDelegate>(this.address.StdStringFromCstring);
|
||||||
this.stdStringDeallocate = Marshal.GetDelegateForFunctionPointer<StdStringDeallocateDelegate>(this.address.StdStringDeallocate);
|
this.stdStringDeallocate = Marshal.GetDelegateForFunctionPointer<StdStringDeallocateDelegate>(this.address.StdStringDeallocate);
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ namespace Dalamud.Game.Network
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[PluginInterface]
|
[PluginInterface]
|
||||||
[InterfaceVersion("1.0")]
|
[InterfaceVersion("1.0")]
|
||||||
|
[ServiceManager.BlockingEarlyLoadedService]
|
||||||
public sealed class GameNetwork : IDisposable
|
public sealed class GameNetwork : IDisposable
|
||||||
{
|
{
|
||||||
private readonly GameNetworkAddressResolver address;
|
private readonly GameNetworkAddressResolver address;
|
||||||
|
|
@ -23,13 +24,11 @@ namespace Dalamud.Game.Network
|
||||||
|
|
||||||
private IntPtr baseAddress;
|
private IntPtr baseAddress;
|
||||||
|
|
||||||
/// <summary>
|
[ServiceManager.ServiceConstructor]
|
||||||
/// Initializes a new instance of the <see cref="GameNetwork"/> class.
|
private GameNetwork(SigScanner sigScanner)
|
||||||
/// </summary>
|
|
||||||
internal GameNetwork()
|
|
||||||
{
|
{
|
||||||
this.address = new GameNetworkAddressResolver();
|
this.address = new GameNetworkAddressResolver();
|
||||||
this.address.Setup();
|
this.address.Setup(sigScanner);
|
||||||
|
|
||||||
Log.Verbose("===== G A M E N E T W O R K =====");
|
Log.Verbose("===== G A M E N E T W O R K =====");
|
||||||
Log.Verbose($"ProcessZonePacketDown address 0x{this.address.ProcessZonePacketDown.ToInt64():X}");
|
Log.Verbose($"ProcessZonePacketDown address 0x{this.address.ProcessZonePacketDown.ToInt64():X}");
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ namespace Dalamud.Game.Network.Internal
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// This class handles network notifications and uploading market board data.
|
/// This class handles network notifications and uploading market board data.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[ServiceManager.EarlyLoadedService]
|
||||||
internal class NetworkHandlers
|
internal class NetworkHandlers
|
||||||
{
|
{
|
||||||
private readonly List<MarketBoardItemRequest> marketBoardRequests = new();
|
private readonly List<MarketBoardItemRequest> marketBoardRequests = new();
|
||||||
|
|
@ -29,14 +30,12 @@ namespace Dalamud.Game.Network.Internal
|
||||||
|
|
||||||
private MarketBoardPurchaseHandler marketBoardPurchaseHandler;
|
private MarketBoardPurchaseHandler marketBoardPurchaseHandler;
|
||||||
|
|
||||||
/// <summary>
|
[ServiceManager.ServiceConstructor]
|
||||||
/// Initializes a new instance of the <see cref="NetworkHandlers"/> class.
|
private NetworkHandlers(GameNetwork gameNetwork)
|
||||||
/// </summary>
|
|
||||||
public NetworkHandlers()
|
|
||||||
{
|
{
|
||||||
this.uploader = new UniversalisMarketBoardUploader();
|
this.uploader = new UniversalisMarketBoardUploader();
|
||||||
|
|
||||||
Service<GameNetwork>.Get().NetworkMessage += this.OnNetworkMessage;
|
gameNetwork.NetworkMessage += this.OnNetworkMessage;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
||||||
|
|
@ -9,14 +9,13 @@ namespace Dalamud.Game.Network.Internal
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// This class enables TCP optimizations in the game socket for better performance.
|
/// This class enables TCP optimizations in the game socket for better performance.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[ServiceManager.EarlyLoadedService]
|
||||||
internal sealed class WinSockHandlers : IDisposable
|
internal sealed class WinSockHandlers : IDisposable
|
||||||
{
|
{
|
||||||
private Hook<SocketDelegate> ws2SocketHook;
|
private Hook<SocketDelegate> ws2SocketHook;
|
||||||
|
|
||||||
/// <summary>
|
[ServiceManager.ServiceConstructor]
|
||||||
/// Initializes a new instance of the <see cref="WinSockHandlers"/> class.
|
private WinSockHandlers()
|
||||||
/// </summary>
|
|
||||||
public WinSockHandlers()
|
|
||||||
{
|
{
|
||||||
this.ws2SocketHook = Hook<SocketDelegate>.FromSymbol("ws2_32.dll", "socket", this.OnSocket, true);
|
this.ws2SocketHook = Hook<SocketDelegate>.FromSymbol("ws2_32.dll", "socket", this.OnSocket, true);
|
||||||
this.ws2SocketHook?.Enable();
|
this.ws2SocketHook?.Enable();
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ using System.Runtime.InteropServices;
|
||||||
|
|
||||||
using Dalamud.IoC;
|
using Dalamud.IoC;
|
||||||
using Dalamud.IoC.Internal;
|
using Dalamud.IoC.Internal;
|
||||||
|
using Dalamud.Utility.Timing;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using Serilog;
|
using Serilog;
|
||||||
|
|
||||||
|
|
@ -18,6 +19,7 @@ namespace Dalamud.Game
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[PluginInterface]
|
[PluginInterface]
|
||||||
[InterfaceVersion("1.0")]
|
[InterfaceVersion("1.0")]
|
||||||
|
[ServiceManager.BlockingEarlyLoadedService]
|
||||||
public class SigScanner : IDisposable
|
public class SigScanner : IDisposable
|
||||||
{
|
{
|
||||||
private readonly FileInfo? cacheFile;
|
private readonly FileInfo? cacheFile;
|
||||||
|
|
@ -27,6 +29,38 @@ namespace Dalamud.Game
|
||||||
|
|
||||||
private Dictionary<string, long>? textCache;
|
private Dictionary<string, long>? textCache;
|
||||||
|
|
||||||
|
[ServiceManager.ServiceConstructor]
|
||||||
|
private SigScanner(DalamudStartInfo startInfo)
|
||||||
|
{
|
||||||
|
// Initialize the process information.
|
||||||
|
var cacheDir = new DirectoryInfo(Path.Combine(startInfo.WorkingDirectory!, "cachedSigs"));
|
||||||
|
if (!cacheDir.Exists)
|
||||||
|
cacheDir.Create();
|
||||||
|
|
||||||
|
this.cacheFile = new FileInfo(Path.Combine(cacheDir.FullName, $"{startInfo.GameVersion}.json"));
|
||||||
|
this.Module = Process.GetCurrentProcess().MainModule!;
|
||||||
|
this.Is32BitProcess = !Environment.Is64BitProcess;
|
||||||
|
this.IsCopy = true;
|
||||||
|
|
||||||
|
// Limit the search space to .text section.
|
||||||
|
this.SetupSearchSpace(this.Module);
|
||||||
|
|
||||||
|
if (this.IsCopy)
|
||||||
|
this.SetupCopiedSegments();
|
||||||
|
|
||||||
|
Log.Verbose($"Module base: 0x{this.TextSectionBase.ToInt64():X}");
|
||||||
|
Log.Verbose($"Module size: 0x{this.TextSectionSize:X}");
|
||||||
|
|
||||||
|
if (this.cacheFile != null)
|
||||||
|
this.Load();
|
||||||
|
|
||||||
|
// Initialize FFXIVClientStructs function resolver
|
||||||
|
using (Timings.Start("CS Resolver Init"))
|
||||||
|
{
|
||||||
|
FFXIVClientStructs.Resolver.InitializeParallel(new FileInfo(Path.Combine(cacheDir.FullName, $"{startInfo.GameVersion}_cs.json")));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="SigScanner"/> class using the main module of the current process.
|
/// Initializes a new instance of the <see cref="SigScanner"/> class using the main module of the current process.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
||||||
|
|
@ -12,13 +12,12 @@ namespace Dalamud.Game.Text.SeStringHandling
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[PluginInterface]
|
[PluginInterface]
|
||||||
[InterfaceVersion("1.0")]
|
[InterfaceVersion("1.0")]
|
||||||
|
[ServiceManager.BlockingEarlyLoadedService]
|
||||||
[Obsolete("This class is obsolete. Please use the static methods on SeString instead.")]
|
[Obsolete("This class is obsolete. Please use the static methods on SeString instead.")]
|
||||||
public sealed class SeStringManager
|
public sealed class SeStringManager
|
||||||
{
|
{
|
||||||
/// <summary>
|
[ServiceManager.ServiceConstructor]
|
||||||
/// Initializes a new instance of the <see cref="SeStringManager"/> class.
|
private SeStringManager()
|
||||||
/// </summary>
|
|
||||||
internal SeStringManager()
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,14 +13,13 @@ namespace Dalamud.Hooking.Internal
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// This class manages the final disposition of hooks, cleaning up any that have not reverted their changes.
|
/// This class manages the final disposition of hooks, cleaning up any that have not reverted their changes.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[ServiceManager.EarlyLoadedService]
|
||||||
internal class HookManager : IDisposable
|
internal class HookManager : IDisposable
|
||||||
{
|
{
|
||||||
private static readonly ModuleLog Log = new("HM");
|
private static readonly ModuleLog Log = new("HM");
|
||||||
|
|
||||||
/// <summary>
|
[ServiceManager.ServiceConstructor]
|
||||||
/// Initializes a new instance of the <see cref="HookManager"/> class.
|
private HookManager()
|
||||||
/// </summary>
|
|
||||||
public HookManager()
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ using System.Linq;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using Dalamud.Data;
|
using Dalamud.Data;
|
||||||
using Dalamud.Interface.Internal;
|
using Dalamud.Interface.Internal;
|
||||||
using Dalamud.Utility.Timing;
|
using Dalamud.Utility.Timing;
|
||||||
|
|
@ -17,9 +17,10 @@ namespace Dalamud.Interface.GameFonts
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Loads game font for use in ImGui.
|
/// Loads game font for use in ImGui.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[ServiceManager.EarlyLoadedService]
|
||||||
internal class GameFontManager : IDisposable
|
internal class GameFontManager : IDisposable
|
||||||
{
|
{
|
||||||
private static readonly string[] FontNames =
|
private static readonly string?[] FontNames =
|
||||||
{
|
{
|
||||||
null,
|
null,
|
||||||
"AXIS_96", "AXIS_12", "AXIS_14", "AXIS_18", "AXIS_36",
|
"AXIS_96", "AXIS_12", "AXIS_14", "AXIS_18", "AXIS_36",
|
||||||
|
|
@ -31,8 +32,6 @@ namespace Dalamud.Interface.GameFonts
|
||||||
|
|
||||||
private readonly object syncRoot = new();
|
private readonly object syncRoot = new();
|
||||||
|
|
||||||
private readonly InterfaceManager interfaceManager;
|
|
||||||
|
|
||||||
private readonly FdtReader?[] fdts;
|
private readonly FdtReader?[] fdts;
|
||||||
private readonly List<byte[]> texturePixels;
|
private readonly List<byte[]> texturePixels;
|
||||||
private readonly Dictionary<GameFontStyle, ImFontPtr> fonts = new();
|
private readonly Dictionary<GameFontStyle, ImFontPtr> fonts = new();
|
||||||
|
|
@ -42,40 +41,28 @@ namespace Dalamud.Interface.GameFonts
|
||||||
private bool isBetweenBuildFontsAndRightAfterImGuiIoFontsBuild = false;
|
private bool isBetweenBuildFontsAndRightAfterImGuiIoFontsBuild = false;
|
||||||
private bool isBuildingAsFallbackFontMode = false;
|
private bool isBuildingAsFallbackFontMode = false;
|
||||||
|
|
||||||
/// <summary>
|
[ServiceManager.ServiceConstructor]
|
||||||
/// Initializes a new instance of the <see cref="GameFontManager"/> class.
|
private GameFontManager(DataManager dataManager)
|
||||||
/// </summary>
|
|
||||||
public GameFontManager()
|
|
||||||
{
|
{
|
||||||
var dataManager = Service<DataManager>.Get();
|
using (Timings.Start("Getting fdt data"))
|
||||||
|
|
||||||
using (Timings.Start("Load FDTs"))
|
|
||||||
{
|
{
|
||||||
this.fdts = FontNames.Select(fontName =>
|
this.fdts = FontNames.Select(fontName => fontName == null ? null : new FdtReader(dataManager.GetFile($"common/font/{fontName}.fdt")!.Data)).ToArray();
|
||||||
{
|
|
||||||
var fileName = $"common/font/{fontName}.fdt";
|
|
||||||
using (Timings.Start($"Loading FDT: {fileName}"))
|
|
||||||
{
|
|
||||||
var file = fontName == null ? null : dataManager.GetFile(fileName);
|
|
||||||
return file == null ? null : new FdtReader(file!.Data);
|
|
||||||
}
|
|
||||||
}).ToArray();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
using (Timings.Start("Getting texture data"))
|
using (Timings.Start("Getting texture data"))
|
||||||
{
|
{
|
||||||
this.texturePixels = Enumerable.Range(1, 1 + this.fdts.Where(x => x != null).Select(x => x.Glyphs.Select(x => x.TextureFileIndex).Max()).Max()).Select(
|
var texTasks = Enumerable
|
||||||
x =>
|
.Range(1, 1 + this.fdts
|
||||||
{
|
.Where(x => x != null)
|
||||||
var fileName = $"common/font/font{x}.tex";
|
.Select(x => x.Glyphs.Select(y => y.TextureFileIndex).Max())
|
||||||
using (Timings.Start($"Get tex: {fileName}"))
|
.Max())
|
||||||
{
|
.Select(x => dataManager.GetFile<TexFile>($"common/font/font{x}.tex")!)
|
||||||
return dataManager.GameData.GetFile<TexFile>(fileName)!.ImageData;
|
.Select(x => new Task<byte[]>(() => x.ImageData!))
|
||||||
}
|
.ToArray();
|
||||||
}).ToList();
|
foreach (var task in texTasks)
|
||||||
|
task.Start();
|
||||||
|
this.texturePixels = texTasks.Select(x => x.GetAwaiter().GetResult()).ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.interfaceManager = Service<InterfaceManager>.Get();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -183,6 +170,7 @@ namespace Dalamud.Interface.GameFonts
|
||||||
/// <returns>Handle to game font that may or may not be ready yet.</returns>
|
/// <returns>Handle to game font that may or may not be ready yet.</returns>
|
||||||
public GameFontHandle NewFontRef(GameFontStyle style)
|
public GameFontHandle NewFontRef(GameFontStyle style)
|
||||||
{
|
{
|
||||||
|
var interfaceManager = Service<InterfaceManager>.Get();
|
||||||
var needRebuild = false;
|
var needRebuild = false;
|
||||||
|
|
||||||
lock (this.syncRoot)
|
lock (this.syncRoot)
|
||||||
|
|
@ -193,7 +181,7 @@ namespace Dalamud.Interface.GameFonts
|
||||||
needRebuild = !this.fonts.ContainsKey(style);
|
needRebuild = !this.fonts.ContainsKey(style);
|
||||||
if (needRebuild)
|
if (needRebuild)
|
||||||
{
|
{
|
||||||
if (Service<InterfaceManager>.Get().IsBuildingFontsBeforeAtlasBuild && this.isBetweenBuildFontsAndRightAfterImGuiIoFontsBuild)
|
if (interfaceManager.IsBuildingFontsBeforeAtlasBuild && this.isBetweenBuildFontsAndRightAfterImGuiIoFontsBuild)
|
||||||
{
|
{
|
||||||
Log.Information("[GameFontManager] NewFontRef: Building {0} right now, as it is called while BuildFonts is already in progress yet atlas build has not been called yet.", style.ToString());
|
Log.Information("[GameFontManager] NewFontRef: Building {0} right now, as it is called while BuildFonts is already in progress yet atlas build has not been called yet.", style.ToString());
|
||||||
this.EnsureFont(style);
|
this.EnsureFont(style);
|
||||||
|
|
@ -201,7 +189,7 @@ namespace Dalamud.Interface.GameFonts
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Log.Information("[GameFontManager] NewFontRef: Calling RebuildFonts because {0} has been requested.", style.ToString());
|
Log.Information("[GameFontManager] NewFontRef: Calling RebuildFonts because {0} has been requested.", style.ToString());
|
||||||
this.interfaceManager.RebuildFonts();
|
interfaceManager.RebuildFonts();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -294,10 +282,11 @@ namespace Dalamud.Interface.GameFonts
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public unsafe void AfterBuildFonts()
|
public unsafe void AfterBuildFonts()
|
||||||
{
|
{
|
||||||
|
var interfaceManager = Service<InterfaceManager>.Get();
|
||||||
var ioFonts = ImGui.GetIO().Fonts;
|
var ioFonts = ImGui.GetIO().Fonts;
|
||||||
ioFonts.GetTexDataAsRGBA32(out byte* pixels8, out var width, out var height);
|
ioFonts.GetTexDataAsRGBA32(out byte* pixels8, out var width, out var height);
|
||||||
var pixels32 = (uint*)pixels8;
|
var pixels32 = (uint*)pixels8;
|
||||||
var fontGamma = this.interfaceManager.FontGamma;
|
var fontGamma = interfaceManager.FontGamma;
|
||||||
|
|
||||||
foreach (var (style, font) in this.fonts)
|
foreach (var (style, font) in this.fonts)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -18,22 +18,12 @@ namespace Dalamud.Interface.Internal
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Class handling Dalamud core commands.
|
/// Class handling Dalamud core commands.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[ServiceManager.EarlyLoadedService]
|
||||||
internal class DalamudCommands
|
internal class DalamudCommands
|
||||||
{
|
{
|
||||||
/// <summary>
|
[ServiceManager.ServiceConstructor]
|
||||||
/// Initializes a new instance of the <see cref="DalamudCommands"/> class.
|
private DalamudCommands(CommandManager commandManager)
|
||||||
/// </summary>
|
|
||||||
public DalamudCommands()
|
|
||||||
{
|
{
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Register all command handlers with the Dalamud instance.
|
|
||||||
/// </summary>
|
|
||||||
public void SetupCommands()
|
|
||||||
{
|
|
||||||
var commandManager = Service<CommandManager>.Get();
|
|
||||||
|
|
||||||
commandManager.AddHandler("/xldclose", new CommandInfo(this.OnUnloadCommand)
|
commandManager.AddHandler("/xldclose", new CommandInfo(this.OnUnloadCommand)
|
||||||
{
|
{
|
||||||
HelpMessage = Loc.Localize("DalamudUnloadHelp", "Unloads XIVLauncher in-game addon."),
|
HelpMessage = Loc.Localize("DalamudUnloadHelp", "Unloads XIVLauncher in-game addon."),
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,7 @@ namespace Dalamud.Interface.Internal
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// This plugin implements all of the Dalamud interface separately, to allow for reloading of the interface and rapid prototyping.
|
/// This plugin implements all of the Dalamud interface separately, to allow for reloading of the interface and rapid prototyping.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[ServiceManager.EarlyLoadedService]
|
||||||
internal class DalamudInterface : IDisposable
|
internal class DalamudInterface : IDisposable
|
||||||
{
|
{
|
||||||
private static readonly ModuleLog Log = new("DUI");
|
private static readonly ModuleLog Log = new("DUI");
|
||||||
|
|
@ -75,14 +76,9 @@ namespace Dalamud.Interface.Internal
|
||||||
private bool isImGuiTestWindowsInMonospace = false;
|
private bool isImGuiTestWindowsInMonospace = false;
|
||||||
private bool isImGuiDrawMetricsWindow = false;
|
private bool isImGuiDrawMetricsWindow = false;
|
||||||
|
|
||||||
/// <summary>
|
[ServiceManager.ServiceConstructor]
|
||||||
/// Initializes a new instance of the <see cref="DalamudInterface"/> class.
|
private DalamudInterface(Dalamud dalamud, DalamudConfiguration configuration, InterfaceManager interfaceManager)
|
||||||
/// </summary>
|
|
||||||
public DalamudInterface()
|
|
||||||
{
|
{
|
||||||
var configuration = Service<DalamudConfiguration>.Get();
|
|
||||||
var interfaceManager = Service<InterfaceManager>.Get();
|
|
||||||
|
|
||||||
this.WindowSystem = new WindowSystem("DalamudCore");
|
this.WindowSystem = new WindowSystem("DalamudCore");
|
||||||
|
|
||||||
this.changelogWindow = new ChangelogWindow() { IsOpen = false };
|
this.changelogWindow = new ChangelogWindow() { IsOpen = false };
|
||||||
|
|
@ -123,7 +119,6 @@ namespace Dalamud.Interface.Internal
|
||||||
this.isImGuiDrawDevMenu = this.isImGuiDrawDevMenu || configuration.DevBarOpenAtStartup;
|
this.isImGuiDrawDevMenu = this.isImGuiDrawDevMenu || configuration.DevBarOpenAtStartup;
|
||||||
|
|
||||||
interfaceManager.Draw += this.OnDraw;
|
interfaceManager.Draw += this.OnDraw;
|
||||||
var dalamud = Service<Dalamud>.Get();
|
|
||||||
|
|
||||||
var logoTex =
|
var logoTex =
|
||||||
interfaceManager.LoadImage(Path.Combine(dalamud.AssetDirectory.FullName, "UIRes", "logo.png"));
|
interfaceManager.LoadImage(Path.Combine(dalamud.AssetDirectory.FullName, "UIRes", "logo.png"));
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ using System.Runtime.CompilerServices;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using Dalamud.Configuration.Internal;
|
using Dalamud.Configuration.Internal;
|
||||||
using Dalamud.Game;
|
using Dalamud.Game;
|
||||||
using Dalamud.Game.ClientState.GamePad;
|
using Dalamud.Game.ClientState.GamePad;
|
||||||
|
|
@ -44,6 +44,7 @@ namespace Dalamud.Interface.Internal
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// This class manages interaction with the ImGui interface.
|
/// This class manages interaction with the ImGui interface.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[ServiceManager.BlockingEarlyLoadedService]
|
||||||
internal class InterfaceManager : IDisposable
|
internal class InterfaceManager : IDisposable
|
||||||
{
|
{
|
||||||
private const float MinimumFallbackFontSizePt = 9.6f; // Game's minimum AXIS font size
|
private const float MinimumFallbackFontSizePt = 9.6f; // Game's minimum AXIS font size
|
||||||
|
|
@ -58,33 +59,27 @@ namespace Dalamud.Interface.Internal
|
||||||
private readonly HashSet<SpecialGlyphRequest> glyphRequests = new();
|
private readonly HashSet<SpecialGlyphRequest> glyphRequests = new();
|
||||||
private readonly Dictionary<ImFontPtr, TargetFontModification> loadedFontInfo = new();
|
private readonly Dictionary<ImFontPtr, TargetFontModification> loadedFontInfo = new();
|
||||||
|
|
||||||
private readonly Hook<PresentDelegate> presentHook;
|
|
||||||
private readonly Hook<ResizeBuffersDelegate> resizeBuffersHook;
|
|
||||||
private readonly Hook<SetCursorDelegate> setCursorHook;
|
|
||||||
|
|
||||||
private readonly ManualResetEvent fontBuildSignal;
|
private readonly ManualResetEvent fontBuildSignal;
|
||||||
private readonly SwapChainVtableResolver address;
|
private readonly SwapChainVtableResolver address;
|
||||||
|
private readonly TaskCompletionSource sceneInitializeTaskCompletionSource = new();
|
||||||
private RawDX11Scene? scene;
|
private RawDX11Scene? scene;
|
||||||
|
|
||||||
|
private Hook<PresentDelegate>? presentHook;
|
||||||
|
private Hook<ResizeBuffersDelegate>? resizeBuffersHook;
|
||||||
|
private Hook<SetCursorDelegate>? setCursorHook;
|
||||||
|
|
||||||
// can't access imgui IO before first present call
|
// can't access imgui IO before first present call
|
||||||
private bool lastWantCapture = false;
|
private bool lastWantCapture = false;
|
||||||
private bool isRebuildingFonts = false;
|
private bool isRebuildingFonts = false;
|
||||||
|
|
||||||
private bool isFallbackFontMode = false;
|
private bool isFallbackFontMode = false;
|
||||||
|
|
||||||
/// <summary>
|
[ServiceManager.ServiceConstructor]
|
||||||
/// Initializes a new instance of the <see cref="InterfaceManager"/> class.
|
private InterfaceManager()
|
||||||
/// </summary>
|
|
||||||
public InterfaceManager()
|
|
||||||
{
|
{
|
||||||
Service<NotificationManager>.Set();
|
|
||||||
|
|
||||||
var scanner = Service<SigScanner>.Get();
|
|
||||||
|
|
||||||
this.fontBuildSignal = new ManualResetEvent(false);
|
this.fontBuildSignal = new ManualResetEvent(false);
|
||||||
|
|
||||||
this.address = new SwapChainVtableResolver();
|
this.address = new SwapChainVtableResolver();
|
||||||
this.address.Setup(scanner);
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|
@ -106,16 +101,12 @@ namespace Dalamud.Interface.Internal
|
||||||
Log.Error(e, "RTSS Free failed");
|
Log.Error(e, "RTSS Free failed");
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setCursorHook = Hook<SetCursorDelegate>.FromSymbol("user32.dll", "SetCursor", this.SetCursorDetour, true);
|
Task.Run(async () =>
|
||||||
this.presentHook = new Hook<PresentDelegate>(this.address.Present, this.PresentDetour);
|
{
|
||||||
this.resizeBuffersHook = new Hook<ResizeBuffersDelegate>(this.address.ResizeBuffers, this.ResizeBuffersDetour);
|
var framework = await Service<Framework>.GetAsync();
|
||||||
|
var sigScanner = await Service<SigScanner>.GetAsync();
|
||||||
var setCursorAddress = this.setCursorHook?.Address ?? IntPtr.Zero;
|
await framework.RunOnFrameworkThread(() => this.Enable(sigScanner));
|
||||||
|
});
|
||||||
Log.Verbose("===== S W A P C H A I N =====");
|
|
||||||
Log.Verbose($"SetCursor address 0x{setCursorAddress.ToInt64():X}");
|
|
||||||
Log.Verbose($"Present address 0x{this.presentHook.Address.ToInt64():X}");
|
|
||||||
Log.Verbose($"ResizeBuffers address 0x{this.resizeBuffersHook.Address.ToInt64():X}");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
|
[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
|
||||||
|
|
@ -129,6 +120,11 @@ namespace Dalamud.Interface.Internal
|
||||||
|
|
||||||
private delegate void InstallRTSSHook();
|
private delegate void InstallRTSSHook();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a task that gets completed when scene gets initialized.
|
||||||
|
/// </summary>
|
||||||
|
public Task SceneInitializeTask => this.sceneInitializeTaskCompletionSource.Task;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// This event gets called each frame to facilitate ImGui drawing.
|
/// This event gets called each frame to facilitate ImGui drawing.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -259,34 +255,6 @@ namespace Dalamud.Interface.Internal
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool IsBuildingFontsBeforeAtlasBuild => this.isRebuildingFonts && !this.fontBuildSignal.WaitOne(0);
|
public bool IsBuildingFontsBeforeAtlasBuild => this.isRebuildingFonts && !this.fontBuildSignal.WaitOne(0);
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Enable this module.
|
|
||||||
/// </summary>
|
|
||||||
public void Enable()
|
|
||||||
{
|
|
||||||
this.setCursorHook?.Enable();
|
|
||||||
this.presentHook.Enable();
|
|
||||||
this.resizeBuffersHook.Enable();
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (!string.IsNullOrEmpty(this.rtssPath))
|
|
||||||
{
|
|
||||||
NativeFunctions.LoadLibraryW(this.rtssPath);
|
|
||||||
var rtssModule = NativeFunctions.GetModuleHandleW("RTSSHooks64.dll");
|
|
||||||
var installAddr = NativeFunctions.GetProcAddress(rtssModule, "InstallRTSSHook");
|
|
||||||
|
|
||||||
Log.Debug("Installing RTSS hook");
|
|
||||||
Marshal.GetDelegateForFunctionPointer<InstallRTSSHook>(installAddr).Invoke();
|
|
||||||
Log.Debug("RTSS hook OK!");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Log.Error(ex, "Could not reload RTSS");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Dispose of managed and unmanaged resources.
|
/// Dispose of managed and unmanaged resources.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -303,8 +271,8 @@ namespace Dalamud.Interface.Internal
|
||||||
|
|
||||||
this.scene?.Dispose();
|
this.scene?.Dispose();
|
||||||
this.setCursorHook?.Dispose();
|
this.setCursorHook?.Dispose();
|
||||||
this.presentHook.Dispose();
|
this.presentHook?.Dispose();
|
||||||
this.resizeBuffersHook.Dispose();
|
this.resizeBuffersHook?.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
#nullable enable
|
#nullable enable
|
||||||
|
|
@ -480,9 +448,11 @@ namespace Dalamud.Interface.Internal
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
this.scene = new RawDX11Scene(swapChain);
|
this.scene = new RawDX11Scene(swapChain);
|
||||||
|
this.sceneInitializeTaskCompletionSource.SetResult();
|
||||||
}
|
}
|
||||||
catch (DllNotFoundException ex)
|
catch (DllNotFoundException ex)
|
||||||
{
|
{
|
||||||
|
this.sceneInitializeTaskCompletionSource.SetException(ex);
|
||||||
Log.Error(ex, "Could not load ImGui dependencies.");
|
Log.Error(ex, "Could not load ImGui dependencies.");
|
||||||
|
|
||||||
var res = PInvoke.User32.MessageBox(
|
var res = PInvoke.User32.MessageBox(
|
||||||
|
|
@ -1003,6 +973,43 @@ namespace Dalamud.Interface.Internal
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void Enable(SigScanner sigScanner)
|
||||||
|
{
|
||||||
|
this.address.Setup(sigScanner);
|
||||||
|
this.setCursorHook = Hook<SetCursorDelegate>.FromSymbol("user32.dll", "SetCursor", this.SetCursorDetour, true);
|
||||||
|
this.presentHook = new Hook<PresentDelegate>(this.address.Present, this.PresentDetour);
|
||||||
|
this.resizeBuffersHook = new Hook<ResizeBuffersDelegate>(this.address.ResizeBuffers, this.ResizeBuffersDetour);
|
||||||
|
|
||||||
|
var setCursorAddress = this.setCursorHook?.Address ?? IntPtr.Zero;
|
||||||
|
|
||||||
|
Log.Verbose("===== S W A P C H A I N =====");
|
||||||
|
Log.Verbose($"SetCursor address 0x{setCursorAddress.ToInt64():X}");
|
||||||
|
Log.Verbose($"Present address 0x{this.presentHook.Address.ToInt64():X}");
|
||||||
|
Log.Verbose($"ResizeBuffers address 0x{this.resizeBuffersHook.Address.ToInt64():X}");
|
||||||
|
|
||||||
|
this.setCursorHook?.Enable();
|
||||||
|
this.presentHook.Enable();
|
||||||
|
this.resizeBuffersHook.Enable();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrEmpty(this.rtssPath))
|
||||||
|
{
|
||||||
|
NativeFunctions.LoadLibraryW(this.rtssPath);
|
||||||
|
var rtssModule = NativeFunctions.GetModuleHandleW("RTSSHooks64.dll");
|
||||||
|
var installAddr = NativeFunctions.GetProcAddress(rtssModule, "InstallRTSSHook");
|
||||||
|
|
||||||
|
Log.Debug("Installing RTSS hook");
|
||||||
|
Marshal.GetDelegateForFunctionPointer<InstallRTSSHook>(installAddr).Invoke();
|
||||||
|
Log.Debug("RTSS hook OK!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Log.Error(ex, "Could not reload RTSS");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void Disable()
|
private void Disable()
|
||||||
{
|
{
|
||||||
this.setCursorHook?.Disable();
|
this.setCursorHook?.Disable();
|
||||||
|
|
@ -1094,6 +1101,9 @@ namespace Dalamud.Interface.Internal
|
||||||
var gamepadState = Service<GamepadState>.GetNullable();
|
var gamepadState = Service<GamepadState>.GetNullable();
|
||||||
var keyState = Service<KeyState>.GetNullable();
|
var keyState = Service<KeyState>.GetNullable();
|
||||||
|
|
||||||
|
if (dalamudInterface == null || gamepadState == null || keyState == null)
|
||||||
|
return;
|
||||||
|
|
||||||
// fix for keys in game getting stuck, if you were holding a game key (like run)
|
// fix for keys in game getting stuck, if you were holding a game key (like run)
|
||||||
// and then clicked on an imgui textbox - imgui would swallow the keyup event,
|
// and then clicked on an imgui textbox - imgui would swallow the keyup event,
|
||||||
// so the game would think the key remained pressed continuously until you left
|
// so the game would think the key remained pressed continuously until you left
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ namespace Dalamud.Interface.Internal.Notifications
|
||||||
/// Class handling notifications/toasts in ImGui.
|
/// Class handling notifications/toasts in ImGui.
|
||||||
/// Ported from https://github.com/patrickcjk/imgui-notify.
|
/// Ported from https://github.com/patrickcjk/imgui-notify.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[ServiceManager.EarlyLoadedService]
|
||||||
internal class NotificationManager
|
internal class NotificationManager
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -54,6 +55,11 @@ namespace Dalamud.Interface.Internal.Notifications
|
||||||
|
|
||||||
private readonly List<Notification> notifications = new();
|
private readonly List<Notification> notifications = new();
|
||||||
|
|
||||||
|
[ServiceManager.ServiceConstructor]
|
||||||
|
private NotificationManager()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Add a notification to the notification queue.
|
/// Add a notification to the notification queue.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
||||||
|
|
@ -1485,7 +1485,7 @@ namespace Dalamud.Interface.Internal.Windows
|
||||||
ImGuiHelpers.ScaledDummy(20);
|
ImGuiHelpers.ScaledDummy(20);
|
||||||
|
|
||||||
// Needed to init the task tracker, if we're not on a debug build
|
// Needed to init the task tracker, if we're not on a debug build
|
||||||
var tracker = Service<TaskTracker>.GetNullable() ?? Service<TaskTracker>.Set();
|
Service<TaskTracker>.Get().Enable();
|
||||||
|
|
||||||
for (var i = 0; i < TaskTracker.Tasks.Count; i++)
|
for (var i = 0; i < TaskTracker.Tasks.Count; i++)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -60,6 +60,8 @@ namespace Dalamud.Interface.Internal.Windows
|
||||||
var dalamud = Service<Dalamud>.Get();
|
var dalamud = Service<Dalamud>.Get();
|
||||||
var interfaceManager = Service<InterfaceManager>.Get();
|
var interfaceManager = Service<InterfaceManager>.Get();
|
||||||
|
|
||||||
|
interfaceManager.SceneInitializeTask.Wait();
|
||||||
|
|
||||||
this.DefaultIcon = interfaceManager.LoadImage(Path.Combine(dalamud.AssetDirectory.FullName, "UIRes", "defaultIcon.png"))!;
|
this.DefaultIcon = interfaceManager.LoadImage(Path.Combine(dalamud.AssetDirectory.FullName, "UIRes", "defaultIcon.png"))!;
|
||||||
this.TroubleIcon = interfaceManager.LoadImage(Path.Combine(dalamud.AssetDirectory.FullName, "UIRes", "troubleIcon.png"))!;
|
this.TroubleIcon = interfaceManager.LoadImage(Path.Combine(dalamud.AssetDirectory.FullName, "UIRes", "troubleIcon.png"))!;
|
||||||
this.UpdateIcon = interfaceManager.LoadImage(Path.Combine(dalamud.AssetDirectory.FullName, "UIRes", "updateIcon.png"))!;
|
this.UpdateIcon = interfaceManager.LoadImage(Path.Combine(dalamud.AssetDirectory.FullName, "UIRes", "updateIcon.png"))!;
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,7 @@ namespace Dalamud.Interface.Internal.Windows
|
||||||
|
|
||||||
private XivChatType dalamudMessagesChatType;
|
private XivChatType dalamudMessagesChatType;
|
||||||
|
|
||||||
|
private bool doWaitForPluginsOnStartup;
|
||||||
private bool doCfTaskBarFlash;
|
private bool doCfTaskBarFlash;
|
||||||
private bool doCfChatMessage;
|
private bool doCfChatMessage;
|
||||||
private bool doMbCollect;
|
private bool doMbCollect;
|
||||||
|
|
@ -90,6 +91,7 @@ namespace Dalamud.Interface.Internal.Windows
|
||||||
|
|
||||||
this.dalamudMessagesChatType = configuration.GeneralChatType;
|
this.dalamudMessagesChatType = configuration.GeneralChatType;
|
||||||
|
|
||||||
|
this.doWaitForPluginsOnStartup = configuration.IsResumeGameAfterPluginLoad;
|
||||||
this.doCfTaskBarFlash = configuration.DutyFinderTaskbarFlash;
|
this.doCfTaskBarFlash = configuration.DutyFinderTaskbarFlash;
|
||||||
this.doCfChatMessage = configuration.DutyFinderChatMessage;
|
this.doCfChatMessage = configuration.DutyFinderChatMessage;
|
||||||
this.doMbCollect = configuration.IsMbCollect;
|
this.doMbCollect = configuration.IsMbCollect;
|
||||||
|
|
@ -261,6 +263,9 @@ namespace Dalamud.Interface.Internal.Windows
|
||||||
|
|
||||||
ImGuiHelpers.ScaledDummy(5);
|
ImGuiHelpers.ScaledDummy(5);
|
||||||
|
|
||||||
|
ImGui.Checkbox(Loc.Localize("DalamudSettingsWaitForPluginsOnStartup", "Wait for plugins before game loads"), ref this.doWaitForPluginsOnStartup);
|
||||||
|
ImGui.TextColored(ImGuiColors.DalamudGrey, Loc.Localize("DalamudSettingsWaitForPluginsOnStartupHint", "Do not let the game load, until plugins are loaded."));
|
||||||
|
|
||||||
ImGui.Checkbox(Loc.Localize("DalamudSettingsFlash", "Flash FFXIV window on duty pop"), ref this.doCfTaskBarFlash);
|
ImGui.Checkbox(Loc.Localize("DalamudSettingsFlash", "Flash FFXIV window on duty pop"), ref this.doCfTaskBarFlash);
|
||||||
ImGui.TextColored(ImGuiColors.DalamudGrey, Loc.Localize("DalamudSettingsFlashHint", "Flash the FFXIV window in your task bar when a duty is ready."));
|
ImGui.TextColored(ImGuiColors.DalamudGrey, Loc.Localize("DalamudSettingsFlashHint", "Flash the FFXIV window in your task bar when a duty is ready."));
|
||||||
|
|
||||||
|
|
@ -916,6 +921,7 @@ namespace Dalamud.Interface.Internal.Windows
|
||||||
|
|
||||||
configuration.GeneralChatType = this.dalamudMessagesChatType;
|
configuration.GeneralChatType = this.dalamudMessagesChatType;
|
||||||
|
|
||||||
|
configuration.IsResumeGameAfterPluginLoad = this.doWaitForPluginsOnStartup;
|
||||||
configuration.DutyFinderTaskbarFlash = this.doCfTaskBarFlash;
|
configuration.DutyFinderTaskbarFlash = this.doCfTaskBarFlash;
|
||||||
configuration.DutyFinderChatMessage = this.doCfChatMessage;
|
configuration.DutyFinderChatMessage = this.doCfChatMessage;
|
||||||
configuration.IsMbCollect = this.doMbCollect;
|
configuration.IsMbCollect = this.doMbCollect;
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ namespace Dalamud.Interface
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[PluginInterface]
|
[PluginInterface]
|
||||||
[InterfaceVersion("1.0")]
|
[InterfaceVersion("1.0")]
|
||||||
|
[ServiceManager.BlockingEarlyLoadedService]
|
||||||
public class TitleScreenMenu
|
public class TitleScreenMenu
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -21,6 +22,11 @@ namespace Dalamud.Interface
|
||||||
|
|
||||||
private readonly List<TitleScreenMenuEntry> entries = new();
|
private readonly List<TitleScreenMenuEntry> entries = new();
|
||||||
|
|
||||||
|
[ServiceManager.ServiceConstructor]
|
||||||
|
private TitleScreenMenu()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the list of entries in the title screen menu.
|
/// Gets the list of entries in the title screen menu.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace Dalamud.IoC.Internal
|
namespace Dalamud.IoC.Internal
|
||||||
{
|
{
|
||||||
|
|
@ -11,11 +12,12 @@ namespace Dalamud.IoC.Internal
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="ObjectInstance"/> class.
|
/// Initializes a new instance of the <see cref="ObjectInstance"/> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="instance">The underlying instance.</param>
|
/// <param name="instanceTask">Weak reference to the underlying instance.</param>
|
||||||
public ObjectInstance(object instance)
|
/// <param name="type">Type of the underlying instance.</param>
|
||||||
|
public ObjectInstance(Task<WeakReference> instanceTask, Type type)
|
||||||
{
|
{
|
||||||
this.Instance = new WeakReference(instance);
|
this.InstanceTask = instanceTask;
|
||||||
this.Version = instance.GetType().GetCustomAttribute<InterfaceVersionAttribute>();
|
this.Version = type.GetCustomAttribute<InterfaceVersionAttribute>();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -26,6 +28,7 @@ namespace Dalamud.IoC.Internal
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets a reference to the underlying instance.
|
/// Gets a reference to the underlying instance.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public WeakReference Instance { get; }
|
/// <returns>The underlying instance.</returns>
|
||||||
|
public Task<WeakReference> InstanceTask { get; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Runtime.Serialization;
|
using System.Runtime.Serialization;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using Dalamud.Logging.Internal;
|
using Dalamud.Logging.Internal;
|
||||||
|
|
||||||
namespace Dalamud.IoC.Internal
|
namespace Dalamud.IoC.Internal
|
||||||
|
|
@ -17,19 +17,26 @@ namespace Dalamud.IoC.Internal
|
||||||
|
|
||||||
private readonly Dictionary<Type, ObjectInstance> instances = new();
|
private readonly Dictionary<Type, ObjectInstance> instances = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="ServiceContainer"/> class.
|
||||||
|
/// </summary>
|
||||||
|
public ServiceContainer()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Register a singleton object of any type into the current IOC container.
|
/// Register a singleton object of any type into the current IOC container.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="instance">The existing instance to register in the container.</param>
|
/// <param name="instance">The existing instance to register in the container.</param>
|
||||||
/// <typeparam name="T">The interface to register.</typeparam>
|
/// <typeparam name="T">The interface to register.</typeparam>
|
||||||
public void RegisterSingleton<T>(T instance)
|
public void RegisterSingleton<T>(Task<T> instance)
|
||||||
{
|
{
|
||||||
if (instance == null)
|
if (instance == null)
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException(nameof(instance));
|
throw new ArgumentNullException(nameof(instance));
|
||||||
}
|
}
|
||||||
|
|
||||||
this.instances[typeof(T)] = new(instance);
|
this.instances[typeof(T)] = new(instance.ContinueWith(x => new WeakReference(x.Result)), typeof(T));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -38,12 +45,12 @@ namespace Dalamud.IoC.Internal
|
||||||
/// <param name="objectType">The type of object to create.</param>
|
/// <param name="objectType">The type of object to create.</param>
|
||||||
/// <param name="scopedObjects">Scoped objects to be included in the constructor.</param>
|
/// <param name="scopedObjects">Scoped objects to be included in the constructor.</param>
|
||||||
/// <returns>The created object.</returns>
|
/// <returns>The created object.</returns>
|
||||||
public object? Create(Type objectType, params object[] scopedObjects)
|
public async Task<object?> Create(Type objectType, params object[] scopedObjects)
|
||||||
{
|
{
|
||||||
var ctor = this.FindApplicableCtor(objectType, scopedObjects);
|
var ctor = this.FindApplicableCtor(objectType, scopedObjects);
|
||||||
if (ctor == null)
|
if (ctor == null)
|
||||||
{
|
{
|
||||||
Log.Error("Failed to create {TypeName}, an eligible ctor with satisfiable services could not be found", objectType.FullName);
|
Log.Error("Failed to create {TypeName}, an eligible ctor with satisfiable services could not be found", objectType.FullName!);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -59,36 +66,37 @@ namespace Dalamud.IoC.Internal
|
||||||
|
|
||||||
if (!versionCheck)
|
if (!versionCheck)
|
||||||
{
|
{
|
||||||
Log.Error("Failed to create {TypeName}, a RequestedVersion could not be satisfied", objectType.FullName);
|
Log.Error("Failed to create {TypeName}, a RequestedVersion could not be satisfied", objectType.FullName!);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
var resolvedParams = parameters
|
var resolvedParams =
|
||||||
.Select(p =>
|
await Task.WhenAll(
|
||||||
{
|
parameters
|
||||||
var service = this.GetService(p.parameterType, scopedObjects);
|
.Select(async p =>
|
||||||
|
{
|
||||||
|
var service = await this.GetService(p.parameterType, scopedObjects);
|
||||||
|
|
||||||
if (service == null)
|
if (service == null)
|
||||||
{
|
{
|
||||||
Log.Error("Requested service type {TypeName} was not available (null)", p.parameterType.FullName);
|
Log.Error("Requested service type {TypeName} was not available (null)", p.parameterType.FullName!);
|
||||||
}
|
}
|
||||||
|
|
||||||
return service;
|
return service;
|
||||||
})
|
}));
|
||||||
.ToArray();
|
|
||||||
|
|
||||||
var hasNull = resolvedParams.Any(p => p == null);
|
var hasNull = resolvedParams.Any(p => p == null);
|
||||||
if (hasNull)
|
if (hasNull)
|
||||||
{
|
{
|
||||||
Log.Error("Failed to create {TypeName}, a requested service type could not be satisfied", objectType.FullName);
|
Log.Error("Failed to create {TypeName}, a requested service type could not be satisfied", objectType.FullName!);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
var instance = FormatterServices.GetUninitializedObject(objectType);
|
var instance = FormatterServices.GetUninitializedObject(objectType);
|
||||||
|
|
||||||
if (!this.InjectProperties(instance, scopedObjects))
|
if (!await this.InjectProperties(instance, scopedObjects))
|
||||||
{
|
{
|
||||||
Log.Error("Failed to create {TypeName}, a requested property service type could not be satisfied", objectType.FullName);
|
Log.Error("Failed to create {TypeName}, a requested property service type could not be satisfied", objectType.FullName!);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -105,7 +113,7 @@ namespace Dalamud.IoC.Internal
|
||||||
/// <param name="instance">The object instance.</param>
|
/// <param name="instance">The object instance.</param>
|
||||||
/// <param name="scopedObjects">Scoped objects.</param>
|
/// <param name="scopedObjects">Scoped objects.</param>
|
||||||
/// <returns>Whether or not the injection was successful.</returns>
|
/// <returns>Whether or not the injection was successful.</returns>
|
||||||
public bool InjectProperties(object instance, params object[] scopedObjects)
|
public async Task<bool> InjectProperties(object instance, params object[] scopedObjects)
|
||||||
{
|
{
|
||||||
var objectType = instance.GetType();
|
var objectType = instance.GetType();
|
||||||
|
|
||||||
|
|
@ -121,17 +129,17 @@ namespace Dalamud.IoC.Internal
|
||||||
|
|
||||||
if (!versionCheck)
|
if (!versionCheck)
|
||||||
{
|
{
|
||||||
Log.Error("Failed to create {TypeName}, a RequestedVersion could not be satisfied", objectType.FullName);
|
Log.Error("Failed to create {TypeName}, a RequestedVersion could not be satisfied", objectType.FullName!);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var prop in props)
|
foreach (var prop in props)
|
||||||
{
|
{
|
||||||
var service = this.GetService(prop.propertyInfo.PropertyType, scopedObjects);
|
var service = await this.GetService(prop.propertyInfo.PropertyType, scopedObjects);
|
||||||
|
|
||||||
if (service == null)
|
if (service == null)
|
||||||
{
|
{
|
||||||
Log.Error("Requested service type {TypeName} was not available (null)", prop.propertyInfo.PropertyType.FullName);
|
Log.Error("Requested service type {TypeName} was not available (null)", prop.propertyInfo.PropertyType.FullName!);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -162,14 +170,14 @@ namespace Dalamud.IoC.Internal
|
||||||
"Requested version {ReqVersion} does not match the implemented version {ImplVersion} for param type {ParamType}",
|
"Requested version {ReqVersion} does not match the implemented version {ImplVersion} for param type {ParamType}",
|
||||||
requiredVersion.Version,
|
requiredVersion.Version,
|
||||||
declVersion.Version,
|
declVersion.Version,
|
||||||
parameterType.FullName);
|
parameterType.FullName!);
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private object? GetService(Type serviceType, object[] scopedObjects)
|
private async Task<object?> GetService(Type serviceType, object[] scopedObjects)
|
||||||
{
|
{
|
||||||
var singletonService = this.GetService(serviceType);
|
var singletonService = await this.GetService(serviceType);
|
||||||
if (singletonService != null)
|
if (singletonService != null)
|
||||||
{
|
{
|
||||||
return singletonService;
|
return singletonService;
|
||||||
|
|
@ -185,15 +193,13 @@ namespace Dalamud.IoC.Internal
|
||||||
return scoped;
|
return scoped;
|
||||||
}
|
}
|
||||||
|
|
||||||
private object? GetService(Type serviceType)
|
private async Task<object?> GetService(Type serviceType)
|
||||||
{
|
{
|
||||||
var hasInstance = this.instances.TryGetValue(serviceType, out var service);
|
if (!this.instances.TryGetValue(serviceType, out var service))
|
||||||
if (hasInstance && service.Instance.IsAlive)
|
return null;
|
||||||
{
|
|
||||||
return service.Instance.Target;
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
var instance = await service.InstanceTask;
|
||||||
|
return instance.Target;
|
||||||
}
|
}
|
||||||
|
|
||||||
private ConstructorInfo? FindApplicableCtor(Type type, object[] scopedObjects)
|
private ConstructorInfo? FindApplicableCtor(Type type, object[] scopedObjects)
|
||||||
|
|
@ -224,7 +230,7 @@ namespace Dalamud.IoC.Internal
|
||||||
var contains = types.Contains(parameter.ParameterType);
|
var contains = types.Contains(parameter.ParameterType);
|
||||||
if (!contains)
|
if (!contains)
|
||||||
{
|
{
|
||||||
Log.Error("Failed to validate {TypeName}, unable to find any services that satisfy the type", parameter.ParameterType.FullName);
|
Log.Error("Failed to validate {TypeName}, unable to find any services that satisfy the type", parameter.ParameterType.FullName!);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
|
||||||
using CheapLoc;
|
using CheapLoc;
|
||||||
|
using Dalamud.Configuration.Internal;
|
||||||
using Serilog;
|
using Serilog;
|
||||||
|
|
||||||
namespace Dalamud
|
namespace Dalamud
|
||||||
|
|
@ -12,6 +13,7 @@ namespace Dalamud
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Class handling localization.
|
/// Class handling localization.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[ServiceManager.EarlyLoadedService]
|
||||||
public class Localization
|
public class Localization
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -40,6 +42,16 @@ namespace Dalamud
|
||||||
this.assembly = Assembly.GetCallingAssembly();
|
this.assembly = Assembly.GetCallingAssembly();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[ServiceManager.ServiceConstructor]
|
||||||
|
private Localization(Dalamud dalamud, DalamudConfiguration configuration)
|
||||||
|
: this(Path.Combine(dalamud.AssetDirectory.FullName, "UIRes", "loc", "dalamud"), "dalamud_")
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrEmpty(configuration.LanguageOverride))
|
||||||
|
this.SetupWithLangCode(configuration.LanguageOverride);
|
||||||
|
else
|
||||||
|
this.SetupWithUiCulture();
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Delegate for the <see cref="Localization.LocalizationChanged"/> event that occurs when the language is changed.
|
/// Delegate for the <see cref="Localization.LocalizationChanged"/> event that occurs when the language is changed.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ namespace Dalamud.Logging.Internal
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Class offering various methods to allow for logging in Dalamud modules.
|
/// Class offering various methods to allow for logging in Dalamud modules.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal class ModuleLog
|
public class ModuleLog
|
||||||
{
|
{
|
||||||
private readonly string moduleName;
|
private readonly string moduleName;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ namespace Dalamud.Logging.Internal
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Class responsible for tracking asynchronous tasks.
|
/// Class responsible for tracking asynchronous tasks.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[ServiceManager.EarlyLoadedService]
|
||||||
internal class TaskTracker : IDisposable
|
internal class TaskTracker : IDisposable
|
||||||
{
|
{
|
||||||
private static readonly ModuleLog Log = new("TT");
|
private static readonly ModuleLog Log = new("TT");
|
||||||
|
|
@ -20,16 +21,14 @@ namespace Dalamud.Logging.Internal
|
||||||
private static bool clearRequested = false;
|
private static bool clearRequested = false;
|
||||||
|
|
||||||
private MonoMod.RuntimeDetour.Hook? scheduleAndStartHook;
|
private MonoMod.RuntimeDetour.Hook? scheduleAndStartHook;
|
||||||
|
private bool enabled = false;
|
||||||
|
|
||||||
/// <summary>
|
[ServiceManager.ServiceConstructor]
|
||||||
/// Initializes a new instance of the <see cref="TaskTracker"/> class.
|
private TaskTracker()
|
||||||
/// </summary>
|
|
||||||
public TaskTracker()
|
|
||||||
{
|
{
|
||||||
this.ApplyPatch();
|
#if DEBUG
|
||||||
|
this.Enable();
|
||||||
var framework = Service<Framework>.Get();
|
#endif
|
||||||
framework.Update += this.FrameworkOnUpdate;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -102,6 +101,21 @@ namespace Dalamud.Logging.Internal
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Enables TaskTracker.
|
||||||
|
/// </summary>
|
||||||
|
public void Enable()
|
||||||
|
{
|
||||||
|
if (this.enabled)
|
||||||
|
return;
|
||||||
|
|
||||||
|
this.ApplyPatch();
|
||||||
|
|
||||||
|
var framework = Service<Framework>.Get();
|
||||||
|
framework.Update += this.FrameworkOnUpdate;
|
||||||
|
this.enabled = true;
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -378,7 +378,7 @@ namespace Dalamud.Plugin
|
||||||
realScopedObjects[0] = this;
|
realScopedObjects[0] = this;
|
||||||
Array.Copy(scopedObjects, 0, realScopedObjects, 1, scopedObjects.Length);
|
Array.Copy(scopedObjects, 0, realScopedObjects, 1, scopedObjects.Length);
|
||||||
|
|
||||||
return svcContainer.InjectProperties(instance, realScopedObjects);
|
return svcContainer.InjectProperties(instance, realScopedObjects).GetAwaiter().GetResult();
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,7 @@ namespace Dalamud.Plugin.Internal;
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Class responsible for loading and unloading plugins.
|
/// Class responsible for loading and unloading plugins.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[ServiceManager.EarlyLoadedService]
|
||||||
internal partial class PluginManager : IDisposable
|
internal partial class PluginManager : IDisposable
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -40,16 +41,17 @@ internal partial class PluginManager : IDisposable
|
||||||
private readonly DirectoryInfo devPluginDirectory;
|
private readonly DirectoryInfo devPluginDirectory;
|
||||||
private readonly BannedPlugin[] bannedPlugins;
|
private readonly BannedPlugin[] bannedPlugins;
|
||||||
|
|
||||||
/// <summary>
|
[ServiceManager.ServiceDependency]
|
||||||
/// Initializes a new instance of the <see cref="PluginManager"/> class.
|
private readonly DalamudConfiguration configuration = Service<DalamudConfiguration>.Get();
|
||||||
/// </summary>
|
|
||||||
public PluginManager()
|
|
||||||
{
|
|
||||||
var startInfo = Service<DalamudStartInfo>.Get();
|
|
||||||
var configuration = Service<DalamudConfiguration>.Get();
|
|
||||||
|
|
||||||
this.pluginDirectory = new DirectoryInfo(startInfo.PluginDirectory);
|
[ServiceManager.ServiceDependency]
|
||||||
this.devPluginDirectory = new DirectoryInfo(startInfo.DefaultPluginDirectory);
|
private readonly DalamudStartInfo startInfo = Service<DalamudStartInfo>.Get();
|
||||||
|
|
||||||
|
[ServiceManager.ServiceConstructor]
|
||||||
|
private PluginManager()
|
||||||
|
{
|
||||||
|
this.pluginDirectory = new DirectoryInfo(this.startInfo.PluginDirectory!);
|
||||||
|
this.devPluginDirectory = new DirectoryInfo(this.startInfo.DefaultPluginDirectory!);
|
||||||
|
|
||||||
if (!this.pluginDirectory.Exists)
|
if (!this.pluginDirectory.Exists)
|
||||||
this.pluginDirectory.Create();
|
this.pluginDirectory.Create();
|
||||||
|
|
@ -57,16 +59,16 @@ internal partial class PluginManager : IDisposable
|
||||||
if (!this.devPluginDirectory.Exists)
|
if (!this.devPluginDirectory.Exists)
|
||||||
this.devPluginDirectory.Create();
|
this.devPluginDirectory.Create();
|
||||||
|
|
||||||
this.SafeMode = EnvironmentConfiguration.DalamudNoPlugins || configuration.PluginSafeMode;
|
this.SafeMode = EnvironmentConfiguration.DalamudNoPlugins || this.configuration.PluginSafeMode;
|
||||||
if (this.SafeMode)
|
if (this.SafeMode)
|
||||||
{
|
{
|
||||||
configuration.PluginSafeMode = false;
|
this.configuration.PluginSafeMode = false;
|
||||||
configuration.Save();
|
this.configuration.Save();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.PluginConfigs = new PluginConfigurations(Path.Combine(Path.GetDirectoryName(startInfo.ConfigurationPath) ?? string.Empty, "pluginConfigs"));
|
this.PluginConfigs = new PluginConfigurations(Path.Combine(Path.GetDirectoryName(this.startInfo.ConfigurationPath) ?? string.Empty, "pluginConfigs"));
|
||||||
|
|
||||||
var bannedPluginsJson = File.ReadAllText(Path.Combine(startInfo.AssetDirectory, "UIRes", "bannedplugin.json"));
|
var bannedPluginsJson = File.ReadAllText(Path.Combine(this.startInfo.AssetDirectory!, "UIRes", "bannedplugin.json"));
|
||||||
this.bannedPlugins = JsonConvert.DeserializeObject<BannedPlugin[]>(bannedPluginsJson) ?? Array.Empty<BannedPlugin>();
|
this.bannedPlugins = JsonConvert.DeserializeObject<BannedPlugin[]>(bannedPluginsJson) ?? Array.Empty<BannedPlugin>();
|
||||||
|
|
||||||
this.ApplyPatches();
|
this.ApplyPatches();
|
||||||
|
|
@ -225,12 +227,10 @@ internal partial class PluginManager : IDisposable
|
||||||
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
|
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
|
||||||
public async Task SetPluginReposFromConfigAsync(bool notify)
|
public async Task SetPluginReposFromConfigAsync(bool notify)
|
||||||
{
|
{
|
||||||
var configuration = Service<DalamudConfiguration>.Get();
|
|
||||||
|
|
||||||
var repos = new List<PluginRepository>() { PluginRepository.MainRepo };
|
var repos = new List<PluginRepository>() { PluginRepository.MainRepo };
|
||||||
repos.AddRange(configuration.ThirdRepoList
|
repos.AddRange(this.configuration.ThirdRepoList
|
||||||
.Where(repo => repo.IsEnabled)
|
.Where(repo => repo.IsEnabled)
|
||||||
.Select(repo => new PluginRepository(repo.Url, repo.IsEnabled)));
|
.Select(repo => new PluginRepository(repo.Url, repo.IsEnabled)));
|
||||||
|
|
||||||
this.Repos = repos;
|
this.Repos = repos;
|
||||||
await this.ReloadPluginMastersAsync(notify);
|
await this.ReloadPluginMastersAsync(notify);
|
||||||
|
|
@ -251,8 +251,6 @@ internal partial class PluginManager : IDisposable
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var configuration = Service<DalamudConfiguration>.Get();
|
|
||||||
|
|
||||||
var pluginDefs = new List<PluginDef>();
|
var pluginDefs = new List<PluginDef>();
|
||||||
var devPluginDefs = new List<PluginDef>();
|
var devPluginDefs = new List<PluginDef>();
|
||||||
|
|
||||||
|
|
@ -282,7 +280,7 @@ internal partial class PluginManager : IDisposable
|
||||||
// devPlugins are more freeform. Look for any dll and hope to get lucky.
|
// devPlugins are more freeform. Look for any dll and hope to get lucky.
|
||||||
var devDllFiles = this.devPluginDirectory.GetFiles("*.dll", SearchOption.AllDirectories).ToList();
|
var devDllFiles = this.devPluginDirectory.GetFiles("*.dll", SearchOption.AllDirectories).ToList();
|
||||||
|
|
||||||
foreach (var setting in configuration.DevPluginLoadLocations)
|
foreach (var setting in this.configuration.DevPluginLoadLocations)
|
||||||
{
|
{
|
||||||
if (!setting.IsEnabled)
|
if (!setting.IsEnabled)
|
||||||
continue;
|
continue;
|
||||||
|
|
@ -420,15 +418,13 @@ internal partial class PluginManager : IDisposable
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var configuration = Service<DalamudConfiguration>.Get();
|
|
||||||
|
|
||||||
if (!this.devPluginDirectory.Exists)
|
if (!this.devPluginDirectory.Exists)
|
||||||
this.devPluginDirectory.Create();
|
this.devPluginDirectory.Create();
|
||||||
|
|
||||||
// devPlugins are more freeform. Look for any dll and hope to get lucky.
|
// devPlugins are more freeform. Look for any dll and hope to get lucky.
|
||||||
var devDllFiles = this.devPluginDirectory.GetFiles("*.dll", SearchOption.AllDirectories).ToList();
|
var devDllFiles = this.devPluginDirectory.GetFiles("*.dll", SearchOption.AllDirectories).ToList();
|
||||||
|
|
||||||
foreach (var setting in configuration.DevPluginLoadLocations)
|
foreach (var setting in this.configuration.DevPluginLoadLocations)
|
||||||
{
|
{
|
||||||
if (!setting.IsEnabled)
|
if (!setting.IsEnabled)
|
||||||
continue;
|
continue;
|
||||||
|
|
@ -682,9 +678,6 @@ internal partial class PluginManager : IDisposable
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void CleanupPlugins()
|
public void CleanupPlugins()
|
||||||
{
|
{
|
||||||
var configuration = Service<DalamudConfiguration>.Get();
|
|
||||||
var startInfo = Service<DalamudStartInfo>.Get();
|
|
||||||
|
|
||||||
foreach (var pluginDir in this.pluginDirectory.GetDirectories())
|
foreach (var pluginDir in this.pluginDirectory.GetDirectories())
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
|
@ -748,7 +741,7 @@ internal partial class PluginManager : IDisposable
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (manifest.ApplicableVersion < startInfo.GameVersion)
|
if (manifest.ApplicableVersion <this.startInfo.GameVersion)
|
||||||
{
|
{
|
||||||
Log.Information($"Inapplicable version: cleaning up {versionDir.FullName}");
|
Log.Information($"Inapplicable version: cleaning up {versionDir.FullName}");
|
||||||
versionDir.Delete(true);
|
versionDir.Delete(true);
|
||||||
|
|
@ -919,15 +912,12 @@ internal partial class PluginManager : IDisposable
|
||||||
/// <returns>If the manifest is eligible.</returns>
|
/// <returns>If the manifest is eligible.</returns>
|
||||||
public bool IsManifestEligible(PluginManifest manifest)
|
public bool IsManifestEligible(PluginManifest manifest)
|
||||||
{
|
{
|
||||||
var configuration = Service<DalamudConfiguration>.Get();
|
|
||||||
var startInfo = Service<DalamudStartInfo>.Get();
|
|
||||||
|
|
||||||
// Testing exclusive
|
// Testing exclusive
|
||||||
if (manifest.IsTestingExclusive && !configuration.DoPluginTest)
|
if (manifest.IsTestingExclusive && !configuration.DoPluginTest)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Applicable version
|
// Applicable version
|
||||||
if (manifest.ApplicableVersion < startInfo.GameVersion)
|
if (manifest.ApplicableVersion <this.startInfo.GameVersion)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// API level
|
// API level
|
||||||
|
|
@ -945,7 +935,6 @@ internal partial class PluginManager : IDisposable
|
||||||
/// <returns>A value indicating whether the plugin/manifest has been banned.</returns>
|
/// <returns>A value indicating whether the plugin/manifest has been banned.</returns>
|
||||||
public bool IsManifestBanned(PluginManifest manifest)
|
public bool IsManifestBanned(PluginManifest manifest)
|
||||||
{
|
{
|
||||||
var configuration = Service<DalamudConfiguration>.Get();
|
|
||||||
return !configuration.LoadBannedPlugins && this.bannedPlugins.Any(ban => (ban.Name == manifest.InternalName || ban.Name == Hash.GetStringSha256Hash(manifest.InternalName))
|
return !configuration.LoadBannedPlugins && this.bannedPlugins.Any(ban => (ban.Name == manifest.InternalName || ban.Name == Hash.GetStringSha256Hash(manifest.InternalName))
|
||||||
&& ban.AssemblyVersion >= manifest.AssemblyVersion);
|
&& ban.AssemblyVersion >= manifest.AssemblyVersion);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
49
Dalamud/Plugin/Internal/StartupPluginLoader.cs
Normal file
49
Dalamud/Plugin/Internal/StartupPluginLoader.cs
Normal file
|
|
@ -0,0 +1,49 @@
|
||||||
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Dalamud.Logging.Internal;
|
||||||
|
using Dalamud.Support;
|
||||||
|
using Dalamud.Utility.Timing;
|
||||||
|
|
||||||
|
namespace Dalamud.Plugin.Internal;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Class responsible for loading plugins on startup.
|
||||||
|
/// </summary>
|
||||||
|
[ServiceManager.BlockingEarlyLoadedService]
|
||||||
|
public class StartupPluginLoader
|
||||||
|
{
|
||||||
|
private static readonly ModuleLog Log = new("SPL");
|
||||||
|
|
||||||
|
[ServiceManager.ServiceConstructor]
|
||||||
|
private StartupPluginLoader(PluginManager pluginManager)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using (Timings.Start("PM Load Plugin Repos"))
|
||||||
|
{
|
||||||
|
_ = pluginManager.SetPluginReposFromConfigAsync(false);
|
||||||
|
pluginManager.OnInstalledPluginsChanged += () => Task.Run(Troubleshooting.LogTroubleshooting);
|
||||||
|
|
||||||
|
Log.Information("[T3] PM repos OK!");
|
||||||
|
}
|
||||||
|
|
||||||
|
using (Timings.Start("PM Cleanup Plugins"))
|
||||||
|
{
|
||||||
|
pluginManager.CleanupPlugins();
|
||||||
|
Log.Information("[T3] PMC OK!");
|
||||||
|
}
|
||||||
|
|
||||||
|
using (Timings.Start("PM Load Sync Plugins"))
|
||||||
|
{
|
||||||
|
pluginManager.LoadAllPlugins();
|
||||||
|
Log.Information("[T3] PML OK!");
|
||||||
|
}
|
||||||
|
|
||||||
|
Task.Run(Troubleshooting.LogTroubleshooting);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Log.Error(ex, "Plugin load failed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -334,7 +334,7 @@ internal class LocalPlugin : IDisposable
|
||||||
this.DalamudInterface = new DalamudPluginInterface(this.pluginAssembly.GetName().Name!, this.DllFile, reason, this.IsDev);
|
this.DalamudInterface = new DalamudPluginInterface(this.pluginAssembly.GetName().Name!, this.DllFile, reason, this.IsDev);
|
||||||
|
|
||||||
var ioc = Service<ServiceContainer>.Get();
|
var ioc = Service<ServiceContainer>.Get();
|
||||||
this.instance = ioc.Create(this.pluginType, this.DalamudInterface) as IDalamudPlugin;
|
this.instance = ioc.Create(this.pluginType, this.DalamudInterface).GetAwaiter().GetResult() as IDalamudPlugin;
|
||||||
if (this.instance == null)
|
if (this.instance == null)
|
||||||
{
|
{
|
||||||
this.State = PluginState.LoadError;
|
this.State = PluginState.LoadError;
|
||||||
|
|
|
||||||
|
|
@ -5,14 +5,13 @@ namespace Dalamud.Plugin.Ipc.Internal
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// This class facilitates inter-plugin communication.
|
/// This class facilitates inter-plugin communication.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[ServiceManager.EarlyLoadedService]
|
||||||
internal class CallGate
|
internal class CallGate
|
||||||
{
|
{
|
||||||
private readonly Dictionary<string, CallGateChannel> gates = new();
|
private readonly Dictionary<string, CallGateChannel> gates = new();
|
||||||
|
|
||||||
/// <summary>
|
[ServiceManager.ServiceConstructor]
|
||||||
/// Initializes a new instance of the <see cref="CallGate"/> class.
|
private CallGate()
|
||||||
/// </summary>
|
|
||||||
internal CallGate()
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
177
Dalamud/ServiceManager.cs
Normal file
177
Dalamud/ServiceManager.cs
Normal file
|
|
@ -0,0 +1,177 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Dalamud.IoC.Internal;
|
||||||
|
using Dalamud.Logging.Internal;
|
||||||
|
using JetBrains.Annotations;
|
||||||
|
|
||||||
|
namespace Dalamud
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Class to initialize Service<T>s.
|
||||||
|
/// </summary>
|
||||||
|
internal static class ServiceManager
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Static log facility for Service{T}, to avoid duplicate instances for different types.
|
||||||
|
/// </summary>
|
||||||
|
public static readonly ModuleLog Log = new("SVC");
|
||||||
|
|
||||||
|
private static readonly TaskCompletionSource BlockingServicesLoadedTaskCompletionSource = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets task that gets completed when all blocking early loading services are done loading.
|
||||||
|
/// </summary>
|
||||||
|
public static Task BlockingResolved { get; } = BlockingServicesLoadedTaskCompletionSource.Task;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Kicks off construction of services that can handle early loading.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>Task for initializing all services.</returns>
|
||||||
|
public static async Task InitializeEarlyLoadableServices()
|
||||||
|
{
|
||||||
|
Service<ServiceContainer>.Provide(new ServiceContainer());
|
||||||
|
|
||||||
|
var service = typeof(Service<>);
|
||||||
|
var blockingEarlyLoadingServices = new List<Task>();
|
||||||
|
|
||||||
|
var dependencyServicesMap = new Dictionary<Type, List<Type>>();
|
||||||
|
var getAsyncTaskMap = new Dictionary<Type, Task>();
|
||||||
|
|
||||||
|
foreach (var serviceType in Assembly.GetExecutingAssembly().GetTypes())
|
||||||
|
{
|
||||||
|
var attr = serviceType.GetCustomAttribute<Service>(true)?.GetType();
|
||||||
|
if (attr?.IsAssignableTo(typeof(EarlyLoadedService)) != true)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var getTask = (Task)service.MakeGenericType(serviceType).InvokeMember(
|
||||||
|
"GetAsync",
|
||||||
|
BindingFlags.InvokeMethod | BindingFlags.Static | BindingFlags.Public,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null);
|
||||||
|
if (attr.IsAssignableTo(typeof(BlockingEarlyLoadedService)))
|
||||||
|
{
|
||||||
|
getAsyncTaskMap[serviceType] = getTask;
|
||||||
|
blockingEarlyLoadingServices.Add(getTask);
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencyServicesMap[serviceType] =
|
||||||
|
(List<Type>)service
|
||||||
|
.MakeGenericType(serviceType)
|
||||||
|
.InvokeMember(
|
||||||
|
"GetDependencyServices",
|
||||||
|
BindingFlags.InvokeMethod | BindingFlags.Static | BindingFlags.Public,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null);
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = Task.WhenAll(blockingEarlyLoadingServices).ContinueWith(x =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (x.IsFaulted)
|
||||||
|
BlockingServicesLoadedTaskCompletionSource.SetException(x.Exception!);
|
||||||
|
else
|
||||||
|
BlockingServicesLoadedTaskCompletionSource.SetResult();
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
// don't care, as this means task result/exception has already been set
|
||||||
|
}
|
||||||
|
}).ConfigureAwait(false);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var tasks = new List<Task>();
|
||||||
|
while (dependencyServicesMap.Any())
|
||||||
|
{
|
||||||
|
tasks.Clear();
|
||||||
|
foreach (var (serviceType, dependencies) in dependencyServicesMap.ToList())
|
||||||
|
{
|
||||||
|
if (!dependencies.All(
|
||||||
|
x => !getAsyncTaskMap.ContainsKey(x) || getAsyncTaskMap[x].IsCompleted))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
tasks.Add((Task)service.MakeGenericType(serviceType).InvokeMember(
|
||||||
|
"StartLoader",
|
||||||
|
BindingFlags.InvokeMethod | BindingFlags.Static | BindingFlags.Public,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null));
|
||||||
|
dependencyServicesMap.Remove(serviceType);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!tasks.Any())
|
||||||
|
throw new InvalidOperationException("Unresolvable dependency cycle detected");
|
||||||
|
|
||||||
|
await Task.WhenAll(tasks);
|
||||||
|
foreach (var task in tasks)
|
||||||
|
{
|
||||||
|
if (task.IsFaulted)
|
||||||
|
throw task.Exception!;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Log.Error(e, "Failed resolving services");
|
||||||
|
try
|
||||||
|
{
|
||||||
|
BlockingServicesLoadedTaskCompletionSource.SetException(e);
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
// don't care, as this means task result/exception has already been set
|
||||||
|
}
|
||||||
|
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Indicates that this constructor will be called for early initialization.
|
||||||
|
/// </summary>
|
||||||
|
[AttributeUsage(AttributeTargets.Constructor)]
|
||||||
|
[MeansImplicitUse(ImplicitUseKindFlags.InstantiatedNoFixedConstructorSignature)]
|
||||||
|
public class ServiceConstructor : Attribute
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Indicates that the field is a service that should be loaded before constructing the class.
|
||||||
|
/// </summary>
|
||||||
|
[AttributeUsage(AttributeTargets.Field)]
|
||||||
|
public class ServiceDependency : Attribute
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Indicates that the class is a service.
|
||||||
|
/// </summary>
|
||||||
|
[AttributeUsage(AttributeTargets.Class)]
|
||||||
|
public class Service : Attribute
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Indicates that the class is a service, and will be instantiated automatically on startup.
|
||||||
|
/// </summary>
|
||||||
|
[AttributeUsage(AttributeTargets.Class)]
|
||||||
|
public class EarlyLoadedService : Service
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Indicates that the class is a service, and will be instantiated automatically on startup,
|
||||||
|
/// blocking game main thread until it completes.
|
||||||
|
/// </summary>
|
||||||
|
[AttributeUsage(AttributeTargets.Class)]
|
||||||
|
public class BlockingEarlyLoadedService : EarlyLoadedService
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,9 +1,13 @@
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using Dalamud.IoC;
|
using Dalamud.IoC;
|
||||||
using Dalamud.IoC.Internal;
|
using Dalamud.IoC.Internal;
|
||||||
using Dalamud.Logging.Internal;
|
using Dalamud.Utility.Timing;
|
||||||
|
using JetBrains.Annotations;
|
||||||
|
|
||||||
namespace Dalamud
|
namespace Dalamud
|
||||||
{
|
{
|
||||||
|
|
@ -14,112 +18,154 @@ namespace Dalamud
|
||||||
/// Only used internally within Dalamud, if plugins need access to things it should be _only_ via DI.
|
/// Only used internally within Dalamud, if plugins need access to things it should be _only_ via DI.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
/// <typeparam name="T">The class you want to store in the service locator.</typeparam>
|
/// <typeparam name="T">The class you want to store in the service locator.</typeparam>
|
||||||
internal static class Service<T> where T : class
|
internal static class Service<T>
|
||||||
{
|
{
|
||||||
private static readonly ModuleLog Log = new("SVC");
|
// ReSharper disable once StaticMemberInGenericType
|
||||||
|
private static readonly TaskCompletionSource<T> InstanceTcs = new();
|
||||||
|
|
||||||
private static T? instance;
|
// ReSharper disable once StaticMemberInGenericType
|
||||||
|
private static bool startLoaderInvoked = false;
|
||||||
|
|
||||||
static Service()
|
static Service()
|
||||||
{
|
{
|
||||||
|
var exposeToPlugins = typeof(T).GetCustomAttribute<PluginInterfaceAttribute>() != null;
|
||||||
|
if (exposeToPlugins)
|
||||||
|
ServiceManager.Log.Debug("Service<{0}>: Static ctor called; will be exposed to plugins", typeof(T).Name);
|
||||||
|
else
|
||||||
|
ServiceManager.Log.Debug("Service<{0}>: Static ctor called", typeof(T).Name);
|
||||||
|
|
||||||
|
if (exposeToPlugins)
|
||||||
|
Service<ServiceContainer>.Get().RegisterSingleton(InstanceTcs.Task);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes the service.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The object.</returns>
|
||||||
|
[UsedImplicitly]
|
||||||
|
public static Task<T> StartLoader()
|
||||||
|
{
|
||||||
|
if (startLoaderInvoked)
|
||||||
|
throw new InvalidOperationException("StartLoader has already been called.");
|
||||||
|
|
||||||
|
var attr = typeof(T).GetCustomAttribute<ServiceManager.Service>(true)?.GetType();
|
||||||
|
if (attr?.IsAssignableTo(typeof(ServiceManager.EarlyLoadedService)) != true)
|
||||||
|
throw new InvalidOperationException($"{typeof(T).Name} is not an EarlyLoadedService");
|
||||||
|
|
||||||
|
startLoaderInvoked = true;
|
||||||
|
return Task.Run(async () =>
|
||||||
|
{
|
||||||
|
using (Timings.Start($"{typeof(T).Namespace} Enable"))
|
||||||
|
{
|
||||||
|
if (attr?.IsAssignableTo(typeof(ServiceManager.BlockingEarlyLoadedService)) == true)
|
||||||
|
ServiceManager.Log.Debug("Service<{0}>: Begin construction", typeof(T).Name);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var x = await ConstructObject();
|
||||||
|
if (attr?.IsAssignableTo(typeof(ServiceManager.BlockingEarlyLoadedService)) == true)
|
||||||
|
ServiceManager.Log.Debug("Service<{0}>: Construction complete", typeof(T).Name);
|
||||||
|
InstanceTcs.SetResult(x);
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
InstanceTcs.SetException(e);
|
||||||
|
if (attr?.IsAssignableTo(typeof(ServiceManager.BlockingEarlyLoadedService)) == true)
|
||||||
|
ServiceManager.Log.Error(e, "Service<{0}>: Construction failure", typeof(T).Name);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sets the type in the service locator to the given object.
|
/// Sets the type in the service locator to the given object.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="obj">Object to set.</param>
|
/// <param name="obj">Object to set.</param>
|
||||||
/// <returns>The set object.</returns>
|
public static void Provide(T obj)
|
||||||
public static T Set(T obj)
|
|
||||||
{
|
{
|
||||||
SetInstanceObject(obj);
|
InstanceTcs!.SetResult(obj);
|
||||||
|
ServiceManager.Log.Debug("Service<{0}>: Provided", typeof(T).Name);
|
||||||
return instance!;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sets the type in the service locator via the default parameterless constructor.
|
/// Pull the instance out of the service locator, waiting if necessary.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>The set object.</returns>
|
/// <returns>The object.</returns>
|
||||||
public static T Set()
|
|
||||||
{
|
|
||||||
if (instance != null)
|
|
||||||
throw new Exception($"Service {typeof(T).FullName} was set twice");
|
|
||||||
|
|
||||||
var obj = (T?)Activator.CreateInstance(typeof(T), true);
|
|
||||||
|
|
||||||
SetInstanceObject(obj);
|
|
||||||
|
|
||||||
return instance!;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Sets a type in the service locator via a constructor with the given parameter types.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="args">Constructor arguments.</param>
|
|
||||||
/// <returns>The set object.</returns>
|
|
||||||
public static T Set(params object[] args)
|
|
||||||
{
|
|
||||||
if (args == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(args), $"Service locator was passed a null for type {typeof(T).FullName} parameterized constructor ");
|
|
||||||
}
|
|
||||||
|
|
||||||
var flags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.CreateInstance | BindingFlags.OptionalParamBinding;
|
|
||||||
var obj = (T?)Activator.CreateInstance(typeof(T), flags, null, args, null, null);
|
|
||||||
|
|
||||||
SetInstanceObject(obj);
|
|
||||||
|
|
||||||
return obj;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Attempt to pull the instance out of the service locator.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>The object if registered.</returns>
|
|
||||||
/// <exception cref="InvalidOperationException">Thrown when the object instance is not present in the service locator.</exception>
|
|
||||||
public static T Get()
|
public static T Get()
|
||||||
{
|
{
|
||||||
return instance ?? throw new InvalidOperationException($"{typeof(T).FullName} has not been registered in the service locator!");
|
if (!InstanceTcs.Task.IsCompleted)
|
||||||
|
InstanceTcs.Task.Wait();
|
||||||
|
return InstanceTcs.Task.Result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Pull the instance out of the service locator, waiting if necessary.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The object.</returns>
|
||||||
|
[UsedImplicitly]
|
||||||
|
public static Task<T> GetAsync() => InstanceTcs.Task;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Attempt to pull the instance out of the service locator.
|
/// Attempt to pull the instance out of the service locator.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>The object if registered, null otherwise.</returns>
|
/// <returns>The object if registered, null otherwise.</returns>
|
||||||
public static T? GetNullable()
|
public static T? GetNullable() => InstanceTcs.Task.IsCompleted ? InstanceTcs.Task.Result : default;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets an enumerable containing Service<T>s that are required for this Service to initialize without blocking.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>List of dependency services.</returns>
|
||||||
|
public static List<Type> GetDependencyServices()
|
||||||
{
|
{
|
||||||
return instance;
|
var res = new List<Type>();
|
||||||
|
res.AddRange(GetServiceConstructor()
|
||||||
|
.GetParameters()
|
||||||
|
.Select(x => x.ParameterType));
|
||||||
|
res.AddRange(typeof(T)
|
||||||
|
.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
|
||||||
|
.Select(x => x.FieldType)
|
||||||
|
.Where(x => x.GetCustomAttribute<ServiceManager.ServiceDependency>(true) != null));
|
||||||
|
return res
|
||||||
|
.Distinct()
|
||||||
|
.Select(x => typeof(Service<>).MakeGenericType(x))
|
||||||
|
.ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void SetInstanceObject(T instance)
|
private static async Task<object?> GetServiceObjectConstructArgument(Type type)
|
||||||
{
|
{
|
||||||
Service<T>.instance = instance ?? throw new ArgumentNullException(nameof(instance), $"Service locator received a null for type {typeof(T).FullName}");
|
var task = (Task)typeof(Service<>)
|
||||||
|
.MakeGenericType(type)
|
||||||
var availableToPlugins = RegisterInIoCContainer(instance);
|
.InvokeMember(
|
||||||
|
"GetAsync",
|
||||||
if (availableToPlugins)
|
BindingFlags.InvokeMethod |
|
||||||
Log.Information($"Registered {typeof(T).FullName} into service locator and exposed to plugins");
|
BindingFlags.Static |
|
||||||
else
|
BindingFlags.Public,
|
||||||
Log.Information($"Registered {typeof(T).FullName} into service locator privately");
|
null,
|
||||||
|
null,
|
||||||
|
null)!;
|
||||||
|
await task;
|
||||||
|
return typeof(Task<>).MakeGenericType(type)
|
||||||
|
.GetProperty("Result", BindingFlags.Instance | BindingFlags.Public)!
|
||||||
|
.GetValue(task);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool RegisterInIoCContainer(T instance)
|
private static ConstructorInfo GetServiceConstructor()
|
||||||
{
|
{
|
||||||
var attr = typeof(T).GetCustomAttribute<PluginInterfaceAttribute>();
|
const BindingFlags ctorBindingFlags =
|
||||||
if (attr == null)
|
BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic |
|
||||||
{
|
BindingFlags.CreateInstance | BindingFlags.OptionalParamBinding;
|
||||||
return false;
|
return typeof(T)
|
||||||
}
|
.GetConstructors(ctorBindingFlags)
|
||||||
|
.Single(x => x.GetCustomAttributes(typeof(ServiceManager.ServiceConstructor), true).Any());
|
||||||
|
}
|
||||||
|
|
||||||
var ioc = Service<ServiceContainer>.GetNullable();
|
private static async Task<T> ConstructObject()
|
||||||
if (ioc == null)
|
{
|
||||||
{
|
var ctor = GetServiceConstructor();
|
||||||
return false;
|
var args = await Task.WhenAll(
|
||||||
}
|
ctor.GetParameters().Select(x => GetServiceObjectConstructArgument(x.ParameterType)));
|
||||||
|
return (T)ctor.Invoke(args)!;
|
||||||
ioc.RegisterSingleton(instance);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,12 +8,12 @@ namespace Dalamud.Utility;
|
||||||
public static class ThreadSafety
|
public static class ThreadSafety
|
||||||
{
|
{
|
||||||
[ThreadStatic]
|
[ThreadStatic]
|
||||||
private static bool isMainThread;
|
private static bool threadStaticIsMainThread;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets a value indicating whether the current thread is the main thread.
|
/// Gets a value indicating whether the current thread is the main thread.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static bool IsMainThread => isMainThread;
|
public static bool IsMainThread => threadStaticIsMainThread;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Throws an exception when the current thread is not the main thread.
|
/// Throws an exception when the current thread is not the main thread.
|
||||||
|
|
@ -21,7 +21,7 @@ public static class ThreadSafety
|
||||||
/// <exception cref="InvalidOperationException">Thrown when the current thread is not the main thread.</exception>
|
/// <exception cref="InvalidOperationException">Thrown when the current thread is not the main thread.</exception>
|
||||||
public static void AssertMainThread()
|
public static void AssertMainThread()
|
||||||
{
|
{
|
||||||
if (!isMainThread)
|
if (!threadStaticIsMainThread)
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException("Not on main thread!");
|
throw new InvalidOperationException("Not on main thread!");
|
||||||
}
|
}
|
||||||
|
|
@ -33,7 +33,7 @@ public static class ThreadSafety
|
||||||
/// <exception cref="InvalidOperationException">Thrown when the current thread is the main thread.</exception>
|
/// <exception cref="InvalidOperationException">Thrown when the current thread is the main thread.</exception>
|
||||||
public static void AssertNotMainThread()
|
public static void AssertNotMainThread()
|
||||||
{
|
{
|
||||||
if (isMainThread)
|
if (threadStaticIsMainThread)
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException("On main thread!");
|
throw new InvalidOperationException("On main thread!");
|
||||||
}
|
}
|
||||||
|
|
@ -44,6 +44,6 @@ public static class ThreadSafety
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static void MarkMainThread()
|
internal static void MarkMainThread()
|
||||||
{
|
{
|
||||||
isMainThread = true;
|
threadStaticIsMainThread = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue