feat: delay main thread continuation until GameFixes are applied

This is a temporary workaround until we have infrastructure to intercept winapi in C++
This commit is contained in:
goaaats 2022-05-27 14:11:38 +02:00
parent 28102b405b
commit d2335274ee
No known key found for this signature in database
GPG key ID: 49E2AA8C6A76498B
8 changed files with 67 additions and 18 deletions

View file

@ -59,7 +59,7 @@ bool is_full_dumps()
return check_env_var("DALAMUD_IS_VEH_FULL");
}
DllExport DWORD WINAPI Initialize(LPVOID lpParam)
DllExport DWORD WINAPI Initialize(LPVOID lpParam, HANDLE hMainThreadContinue)
{
#ifndef NDEBUG
ConsoleSetup(L"Dalamud Boot");
@ -97,7 +97,7 @@ DllExport DWORD WINAPI Initialize(LPVOID lpParam)
if (result != 0)
return result;
typedef void (CORECLR_DELEGATE_CALLTYPE* custom_component_entry_point_fn)(LPVOID);
typedef void (CORECLR_DELEGATE_CALLTYPE* custom_component_entry_point_fn)(LPVOID, HANDLE);
custom_component_entry_point_fn entrypoint_fn = reinterpret_cast<custom_component_entry_point_fn>(entrypoint_vfn);
// ============================== VEH ======================================== //
@ -121,7 +121,7 @@ DllExport DWORD WINAPI Initialize(LPVOID lpParam)
// ============================== Dalamud ==================================== //
printf("Initializing Dalamud... ");
entrypoint_fn(lpParam);
entrypoint_fn(lpParam, hMainThreadContinue);
printf("Done!\n");
#ifndef NDEBUG

View file

@ -1,6 +1,6 @@
#include "pch.h"
DllExport DWORD WINAPI Initialize(LPVOID lpParam);
DllExport DWORD WINAPI Initialize(LPVOID lpParam, HANDLE hMainThreadContinue);
struct RewrittenEntryPointParameters {
void* pAllocation;
@ -379,8 +379,9 @@ DllExport void WINAPI RewrittenEntryPoint(RewrittenEntryPointParameters& params)
params.hMainThread = CreateThread(nullptr, 0, [](void* p) -> DWORD {
try {
std::string loadInfo;
auto& params = *reinterpret_cast<RewrittenEntryPointParameters*>(p);
{
auto& params = *reinterpret_cast<RewrittenEntryPointParameters*>(p);
// Restore original entry point.
// Use WriteProcessMemory instead of memcpy to avoid having to fiddle with VirtualProtect.
@ -390,12 +391,12 @@ DllExport void WINAPI RewrittenEntryPoint(RewrittenEntryPointParameters& params)
loadInfo = params.pLoadInfo;
// Let the game initialize.
SetEvent(params.hMainThreadContinue);
//SetEvent(params.hMainThreadContinue);
}
wait_for_game_window();
//wait_for_game_window();
Initialize(&loadInfo[0]);
Initialize(&loadInfo[0], params.hMainThreadContinue);
return 0;
} 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);

View file

@ -49,6 +49,7 @@ namespace Dalamud
private readonly ManualResetEvent unloadSignal;
private readonly ManualResetEvent finishUnloadSignal;
private readonly IntPtr mainThreadContinueEvent;
private MonoMod.RuntimeDetour.Hook processMonoHook;
private bool hasDisposedPlugins = false;
@ -61,7 +62,8 @@ namespace Dalamud
/// <param name="loggingLevelSwitch">LoggingLevelSwitch to control Serilog level.</param>
/// <param name="finishSignal">Signal signalling shutdown.</param>
/// <param name="configuration">The Dalamud configuration.</param>
public Dalamud(DalamudStartInfo info, LoggingLevelSwitch loggingLevelSwitch, ManualResetEvent finishSignal, DalamudConfiguration configuration)
/// <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)
{
this.ApplyProcessPatch();
@ -76,6 +78,8 @@ namespace Dalamud
this.finishUnloadSignal = finishSignal;
this.finishUnloadSignal.Reset();
this.mainThreadContinueEvent = mainThreadContinueEvent;
}
/// <summary>
@ -107,6 +111,13 @@ namespace Dalamud
var gameFixes = Service<GameFixes>.Set();
gameFixes.Apply();
Log.Information("[T1] GameFixes OK!");
// Signal the main game thread to continue
NativeFunctions.SetEvent(this.mainThreadContinueEvent);
Log.Information("[T1] Game thread continued!");
// Initialize FFXIVClientStructs function resolver
FFXIVClientStructs.Resolver.Initialize();
Log.Information("[T1] FFXIVClientStructs initialized!");

View file

@ -29,7 +29,8 @@ namespace Dalamud
/// A delegate used during initialization of the CLR from Dalamud.Boot.
/// </summary>
/// <param name="infoPtr">Pointer to a serialized <see cref="DalamudStartInfo"/> data.</param>
public delegate void InitDelegate(IntPtr infoPtr);
/// <param name="mainThreadContinueEvent">Event used to signal the main thread to continue.</param>
public delegate void InitDelegate(IntPtr infoPtr, IntPtr mainThreadContinueEvent);
/// <summary>
/// A delegate used from VEH handler on exception which CoreCLR will fast fail by default.
@ -43,12 +44,13 @@ namespace Dalamud
/// Initialize Dalamud.
/// </summary>
/// <param name="infoPtr">Pointer to a serialized <see cref="DalamudStartInfo"/> data.</param>
public static void Initialize(IntPtr infoPtr)
/// <param name="mainThreadContinueEvent">Event used to signal the main thread to continue.</param>
public static void Initialize(IntPtr infoPtr, IntPtr mainThreadContinueEvent)
{
var infoStr = Marshal.PtrToStringUTF8(infoPtr);
var info = JsonConvert.DeserializeObject<DalamudStartInfo>(infoStr);
new Thread(() => RunThread(info)).Start();
new Thread(() => RunThread(info, mainThreadContinueEvent)).Start();
}
/// <summary>
@ -106,7 +108,8 @@ namespace Dalamud
/// Initialize all Dalamud subsystems and start running on the main thread.
/// </summary>
/// <param name="info">The <see cref="DalamudStartInfo"/> containing information needed to initialize Dalamud.</param>
private static void RunThread(DalamudStartInfo info)
/// <param name="mainThreadContinueEvent">Event used to signal the main thread to continue.</param>
private static void RunThread(DalamudStartInfo info, IntPtr mainThreadContinueEvent)
{
// Setup logger
var levelSwitch = InitLogging(info.WorkingDirectory);
@ -142,11 +145,12 @@ namespace Dalamud
if (!Util.IsLinux())
InitSymbolHandler(info);
var dalamud = new Dalamud(info, levelSwitch, finishSignal, configuration);
var dalamud = new Dalamud(info, levelSwitch, finishSignal, configuration, mainThreadContinueEvent);
Log.Information("Starting a session..");
// Run session
dalamud.LoadTier1();
dalamud.WaitForUnload();
dalamud.Dispose();

View file

@ -46,8 +46,10 @@ internal class WndProcNullRefFix : IGameFix, IDisposable
Log.Information($"Applying WndProcNullRefFix at {patchAddress:X} with o1 {this.object1Address:X}, o2 {this.object2Address:X}");
this.wndProcHook = new Hook<WndProcDelegate>(patchAddress, this.WndProcDetour);
this.wndProcHook = new Hook<WndProcDelegate>(patchAddress, this.WndProcDetour, true);
Log.Information("Set up hook");
this.wndProcHook.Enable();
Log.Information("Enabled hook");
}
/// <inheritdoc/>

View file

@ -1,12 +1,17 @@
#define BOOT_AGING
using System;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Numerics;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using CheapLoc;
using Dalamud.Configuration.Internal;
using Dalamud.Game.ClientState.Conditions;
@ -29,7 +34,6 @@ using ImGuiNET;
using ImGuiScene;
using PInvoke;
using Serilog.Events;
namespace Dalamud.Interface.Internal
{
/// <summary>
@ -350,10 +354,25 @@ namespace Dalamud.Interface.Internal
#endregion
private bool signaledBoot = false;
private void OnDraw()
{
this.frameCount++;
#if BOOT_AGING
if (this.frameCount > 500 && !this.signaledBoot)
{
this.signaledBoot = true;
Task.Run(async () =>
{
using var client = new HttpClient();
await client.PostAsync("http://localhost:1415/aging/success", new StringContent(string.Empty));
});
}
#endif
try
{
this.DrawHiddenDevMenuOpener();

View file

@ -1393,6 +1393,18 @@ namespace Dalamud
WriteCombine = 0x400,
}
/// <summary>
/// See https://docs.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-setevent
/// Sets the specified event object to the signaled state.
/// </summary>
/// <param name="hEvent">A handle to the event object. The CreateEvent or OpenEvent function returns this handle.</param>
/// <returns>
/// If the function succeeds, the return value is nonzero.
/// If the function fails, the return value is zero. To get extended error information, call GetLastError.
/// </returns>
[DllImport("kernel32.dll")]
public static extern bool SetEvent(IntPtr hEvent);
/// <summary>
/// See https://docs.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-freelibrary.
/// Frees the loaded dynamic-link library (DLL) module and, if necessary, decrements its reference count. When the reference

View file

@ -105,7 +105,7 @@ int InitializeClrAndGetEntryPoint(
dotnet_path,
};
printf("Loading coreclr... ");;
printf("Loading coreclr... ");
if ((result = g_clr->load_runtime(runtimeconfig_path, &runtime_parameters)) != 0)
{
printf("\nError: Failed to load coreclr (err=%d)\n", result);