From 3239a9890f44f16f008c26d4e289ef29cb81e1b0 Mon Sep 17 00:00:00 2001 From: EricPlayZ Date: Mon, 19 Feb 2024 23:23:34 +0200 Subject: [PATCH] - Fixed frequent crashes when using DX12, sometimes DX11 too. - Fixed frequent crashing at game startup - Fixed crashing when trying to use ".model" mods with the custom file loading system; PLEASE keep in mind that I haven't found a fix for this yet! Custom ".model" files will not get loaded by this mod's system --- EGameTools/EGameTools.vcxproj.user | 11 ++- EGameTools/source/ImGui/impl/d3d11_impl.cpp | 34 ++++++--- EGameTools/source/ImGui/impl/d3d12_impl.cpp | 74 +++++++++++-------- EGameTools/source/core.cpp | 47 ++++++++---- .../source/game/Engine/engine_hooks.cpp | 48 +++++++----- 5 files changed, 138 insertions(+), 76 deletions(-) diff --git a/EGameTools/EGameTools.vcxproj.user b/EGameTools/EGameTools.vcxproj.user index 88a5509..153660d 100644 --- a/EGameTools/EGameTools.vcxproj.user +++ b/EGameTools/EGameTools.vcxproj.user @@ -1,4 +1,13 @@  - + + D:\Program Files (x86)\Steam\steamapps\common\Dying Light 2\ph\work\bin\x64\DyingLightGame_x64_rwdi.exe + D:\Program Files (x86)\Steam\steamapps\common\Dying Light 2\ph\work\bin\x64 + WindowsLocalDebugger + + + D:\Program Files (x86)\Steam\steamapps\common\Dying Light 2\ph\work\bin\x64\DyingLightGame_x64_rwdi.exe + D:\Program Files (x86)\Steam\steamapps\common\Dying Light 2\ph\work\bin\x64 + WindowsLocalDebugger + \ No newline at end of file diff --git a/EGameTools/source/ImGui/impl/d3d11_impl.cpp b/EGameTools/source/ImGui/impl/d3d11_impl.cpp index 5768e5d..fb15190 100644 --- a/EGameTools/source/ImGui/impl/d3d11_impl.cpp +++ b/EGameTools/source/ImGui/impl/d3d11_impl.cpp @@ -74,22 +74,32 @@ namespace impl { init = true; } - ImGui_ImplDX11_NewFrame(); - ImGui_ImplWin32_NewFrame(); - ImGui::NewFrame(); + for (int retries = 0;; retries++) { + try { + ImGui_ImplDX11_NewFrame(); + ImGui_ImplWin32_NewFrame(); + ImGui::NewFrame(); - Menu::FirstTimeRunning(); - if (Menu::menuToggle.GetValue()) - Menu::Render(); + Menu::FirstTimeRunning(); + if (Menu::menuToggle.GetValue()) + Menu::Render(); - ImGui::EndFrame(); - ImGui::Render(); + ImGui::EndFrame(); + ImGui::Render(); - if (d3d11RenderTargetView) - d3d11DeviceContext->OMSetRenderTargets(1, &d3d11RenderTargetView, nullptr); - ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData()); + if (d3d11RenderTargetView) + d3d11DeviceContext->OMSetRenderTargets(1, &d3d11RenderTargetView, nullptr); + ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData()); - return oPresent(pSwapChain, SyncInterval, Flags); + return oPresent(pSwapChain, SyncInterval, Flags); + } catch (const std::exception& e) { + spdlog::error("Exception thrown rendering ImGui in DX11: {}", e.what()); + if (retries >= 6) { + spdlog::error("Retried rendering ImGui in DX11 6 times, game will exit now."); + IM_ASSERT(retries < 6 && "Retried rendering ImGui in DX11 6 times, game will exit now."); + } + } + } } HRESULT(__stdcall* oResizeBuffers)(IDXGISwapChain*, UINT, UINT, UINT, DXGI_FORMAT, UINT); diff --git a/EGameTools/source/ImGui/impl/d3d12_impl.cpp b/EGameTools/source/ImGui/impl/d3d12_impl.cpp index fa93e37..00df7e2 100644 --- a/EGameTools/source/ImGui/impl/d3d12_impl.cpp +++ b/EGameTools/source/ImGui/impl/d3d12_impl.cpp @@ -105,47 +105,59 @@ namespace impl { init = true; } - if (!frameContext[0].main_render_target_resource) - CreateRenderTarget(pSwapChain); - if (!d3d12CommandQueue || !frameContext[0].main_render_target_resource) - return; + for (int retries = 0;; retries++) { + try { + if (!frameContext[0].main_render_target_resource) + CreateRenderTarget(pSwapChain); + if (!d3d12CommandQueue || !frameContext[0].main_render_target_resource) + return; - ImGui_ImplDX12_NewFrame(); - ImGui_ImplWin32_NewFrame(); - ImGui::NewFrame(); + ImGui_ImplDX12_NewFrame(); + ImGui_ImplWin32_NewFrame(); + ImGui::NewFrame(); - Menu::FirstTimeRunning(); - if (Menu::menuToggle.GetValue()) - Menu::Render(); + Menu::FirstTimeRunning(); + if (Menu::menuToggle.GetValue()) + Menu::Render(); - ImGui::Render(); + ImGui::Render(); - UINT backBufferIdx = pSwapChain->GetCurrentBackBufferIndex(); - ID3D12CommandAllocator* commandAllocator = frameContext[backBufferIdx].commandAllocator; - commandAllocator->Reset(); + UINT backBufferIdx = pSwapChain->GetCurrentBackBufferIndex(); + ID3D12CommandAllocator* commandAllocator = frameContext[backBufferIdx].commandAllocator; + commandAllocator->Reset(); - D3D12_RESOURCE_BARRIER barrier{}; - barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; - barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE; - barrier.Transition.pResource = frameContext[backBufferIdx].main_render_target_resource; - barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES; - barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_PRESENT; - barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET; - d3d12CommandList->Reset(commandAllocator, NULL); - d3d12CommandList->ResourceBarrier(1, &barrier); + D3D12_RESOURCE_BARRIER barrier{}; + barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; + barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE; + barrier.Transition.pResource = frameContext[backBufferIdx].main_render_target_resource; + barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES; + barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_PRESENT; + barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET; + d3d12CommandList->Reset(commandAllocator, NULL); + d3d12CommandList->ResourceBarrier(1, &barrier); - d3d12CommandList->OMSetRenderTargets(1, &frameContext[backBufferIdx].main_render_target_descriptor, FALSE, NULL); - d3d12CommandList->SetDescriptorHeaps(1, &d3d12DescriptorHeapImGuiRender); + d3d12CommandList->OMSetRenderTargets(1, &frameContext[backBufferIdx].main_render_target_descriptor, FALSE, NULL); + d3d12CommandList->SetDescriptorHeaps(1, &d3d12DescriptorHeapImGuiRender); - ImGui_ImplDX12_RenderDrawData(ImGui::GetDrawData(), d3d12CommandList); + ImGui_ImplDX12_RenderDrawData(ImGui::GetDrawData(), d3d12CommandList); - barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET; - barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_PRESENT; + barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET; + barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_PRESENT; - d3d12CommandList->ResourceBarrier(1, &barrier); - d3d12CommandList->Close(); + d3d12CommandList->ResourceBarrier(1, &barrier); + d3d12CommandList->Close(); - d3d12CommandQueue->ExecuteCommandLists(1, reinterpret_cast(&d3d12CommandList)); + d3d12CommandQueue->ExecuteCommandLists(1, reinterpret_cast(&d3d12CommandList)); + + break; + } catch (const std::exception& e) { + spdlog::error("Exception thrown rendering ImGui in DX12: {}", e.what()); + if (retries >= 6) { + spdlog::error("Retried rendering ImGui in DX12 6 times, game will exit now."); + IM_ASSERT(retries < 6 && "Retried rendering ImGui in DX12 6 times, game will exit now."); + } + } + } } HRESULT(__stdcall* oPresent)(IDXGISwapChain3*, UINT, UINT); diff --git a/EGameTools/source/core.cpp b/EGameTools/source/core.cpp index ddc9f27..35f5266 100644 --- a/EGameTools/source/core.cpp +++ b/EGameTools/source/core.cpp @@ -67,31 +67,48 @@ namespace Core { } static void CreateSymlinkForLoadingFiles() { - std::filesystem::create_directories("EGameTools\\UserModFiles"); + try { + const char* userModFilesPath = "..\\..\\..\\source\\data\\EGameTools\\UserModFiles"; + const char* EGameToolsPath = "..\\..\\..\\source\\data\\EGameTools"; + if (!std::filesystem::exists(userModFilesPath)) + std::filesystem::create_directories(userModFilesPath); - for (const auto& entry : std::filesystem::directory_iterator("..\\..\\data")) { - if (entry.path().filename().string() == "EGameTools" && is_symlink(entry.symlink_status())) { - if (std::filesystem::equivalent("..\\..\\data\\EGameTools", "EGameTools")) - return; - std::filesystem::remove(entry.path()); + for (const auto& entry : std::filesystem::directory_iterator(".")) { + if (entry.path().filename().string() == "EGameTools") { + if (is_symlink(entry.symlink_status()) && std::filesystem::equivalent("EGameTools", EGameToolsPath)) + return; + + std::filesystem::remove(entry.path()); + } } + spdlog::warn("Creating folder shortcut \"EGameTools\" for \"Dying Light 2\\ph\\source\\data\\EGameTools\" folder"); + std::filesystem::create_directory_symlink(EGameToolsPath, Utils::Files::GetCurrentProcDirectory() + "\\EGameTools"); + spdlog::info("Game shortcut created"); + } catch (const std::exception& e) { + spdlog::error("Exception thrown while trying to create folder shortcut: {}", e.what()); + spdlog::warn("This error should NOT affect any features of my mod. The shortcut is only a way for the user to easily access the folder \"Dying Light 2\\ph\\source\\data\\EGameTools\"."); } - spdlog::warn("Creating game shortcut for \"EGameTools\""); - std::filesystem::create_directory_symlink(Utils::Files::GetCurrentProcDirectory() + "\\EGameTools", "..\\..\\data\\EGameTools"); - spdlog::info("Game shortcut created"); } static void InitLogger() { constexpr size_t maxSize = static_cast(1048576) * 100; constexpr size_t maxFiles = 10; - static std::vector sinks{}; - sinks.push_back(std::make_shared("EGameTools\\log.txt", maxSize, maxFiles, true)); - sinks.push_back(std::make_shared()); - std::shared_ptr combined_logger = std::make_shared("EGameTools", std::begin(sinks), std::end(sinks)); - combined_logger->flush_on(spdlog::level::trace); + try { + static std::vector sinks{}; + sinks.push_back(std::make_shared("..\\..\\..\\source\\data\\EGameTools\\log.txt", maxSize, maxFiles, true)); + sinks.push_back(std::make_shared()); + std::shared_ptr combined_logger = std::make_shared("EGameTools", std::begin(sinks), std::end(sinks)); + combined_logger->flush_on(spdlog::level::trace); - spdlog::set_default_logger(combined_logger); + spdlog::set_default_logger(combined_logger); + } catch (const std::exception& e) { + UNREFERENCED_PARAMETER(e); + std::cout << "Failed creating spdlog instance! Please contact mod author, this is not supposed to happen!" << std::endl; + std::cout << "Game will exit in 30 seconds. Close this window once you've read the text." << std::endl; + Sleep(30000); + exit(0); + } } void OnPostUpdate() { diff --git a/EGameTools/source/game/Engine/engine_hooks.cpp b/EGameTools/source/game/Engine/engine_hooks.cpp index 21e9a0b..4977359 100644 --- a/EGameTools/source/game/Engine/engine_hooks.cpp +++ b/EGameTools/source/game/Engine/engine_hooks.cpp @@ -67,21 +67,18 @@ namespace Engine { #pragma endregion #pragma region fs::open - static LPVOID GetFsOpen() { - return Utils::Memory::GetProcAddr("filesystem_x64_rwdi.dll", "?open@fs@@YAPEAUSFsFile@@V?$string_const@D@ttl@@W4TYPE@EFSMode@@W45FFSOpenFlags@@@Z"); - } - static DWORD64 detourFsOpen(DWORD64 file, DWORD a2, DWORD a3); - static Utils::Hook::MHook FsOpenHook{ "fs::open", &GetFsOpen, &detourFsOpen }; - static std::vector cachedUserModDirs{}; + static Utils::Time::Timer timeSinceCache{ 0 }; static void CacheUserModDirs() { spdlog::warn("Recaching user mod directories"); if (!cachedUserModDirs.empty()) cachedUserModDirs.clear(); - cachedUserModDirs.push_back("EGameTools\\UserModFiles"); - for (const auto& entry : std::filesystem::recursive_directory_iterator("EGameTools\\UserModFiles")) { + const char* userModFilesPath = "..\\..\\..\\source\\data\\EGameTools\\UserModFiles"; + + cachedUserModDirs.push_back(userModFilesPath); + for (const auto& entry : std::filesystem::recursive_directory_iterator(userModFilesPath)) { const std::filesystem::path pathToDir = entry.path(); if (!std::filesystem::is_directory(pathToDir)) continue; @@ -90,13 +87,18 @@ namespace Engine { } } - static Utils::Time::Timer timeSinceCache{ 0 }; + static LPVOID GetFsOpen() { + return Utils::Memory::GetProcAddr("filesystem_x64_rwdi.dll", "?open@fs@@YAPEAUSFsFile@@V?$string_const@D@ttl@@W4TYPE@EFSMode@@W45FFSOpenFlags@@@Z"); + } + static DWORD64 detourFsOpen(DWORD64 file, DWORD a2, DWORD a3); + static Utils::Hook::MHook FsOpenHook{ "fs::open", &GetFsOpen, &detourFsOpen }; + static DWORD64 detourFsOpen(DWORD64 file, DWORD a2, DWORD a3) { const DWORD64 firstByte = (file >> 56) & 0xFF; // get first byte of addr const char* filePath = reinterpret_cast(file & 0x1FFFFFFFFFFFFFFF); // remove first byte of addr in case it exists const std::string fileName = std::filesystem::path(filePath).filename().string(); - if (fileName.empty()) + if (fileName.empty() || fileName.contains("EGameTools") || fileName.ends_with(".model")) return FsOpenHook.pOriginal(file, a2, a3); if (timeSinceCache.DidTimePass()) { @@ -104,15 +106,27 @@ namespace Engine { timeSinceCache = Utils::Time::Timer(5000); } - for (const auto& entry : cachedUserModDirs) { - const std::string finalPath = entry + "\\" + fileName; - if (!std::filesystem::exists(finalPath)) - continue; + std::string finalPath{}; - const char* filePath2 = finalPath.c_str(); - spdlog::warn("Loading user mod file \"{}\"", filePath2); + try { + for (const auto& entry : cachedUserModDirs) { + finalPath = entry + "\\" + fileName; + if (!std::filesystem::exists(finalPath)) + continue; - return FsOpenHook.pOriginal(firstByte != 0x0 ? (reinterpret_cast(filePath2) | (firstByte << 56)) : reinterpret_cast(filePath2), a2, a3); // restores first byte of addr if first byte was not 0 + const std::string finalPath2 = std::filesystem::absolute(finalPath).string(); + const char* filePath2 = finalPath2.c_str(); + spdlog::warn("Loading user mod file \"{}\"", finalPath.c_str()); + + const DWORD64 finalAddr = firstByte != 0x0 ? (reinterpret_cast(filePath2) | (firstByte << 56)) : reinterpret_cast(filePath2); // restores first byte of addr if first byte was not 0 + + const DWORD64 result = FsOpenHook.pOriginal(finalAddr, a2, a3); + if (!result) + spdlog::error("fs::open returned 0! Something went wrong with loading user mod file \"{}\"!", finalPath.c_str()); + return result; + } + } catch (const std::exception& e) { + spdlog::error("Exception thrown while loading user mod file \"{}\": {}", finalPath.c_str(), e.what()); } return FsOpenHook.pOriginal(file, a2, a3); }