From 6764b5e56e63af92bf5cb0aa677642d18fe59911 Mon Sep 17 00:00:00 2001 From: Kawe Mazidjatari <48657826+Mauler125@users.noreply.github.com> Date: Fri, 19 Aug 2022 21:33:31 +0200 Subject: [PATCH] Implement frame tasks Run all Cbuf_Execute calls in the main thread. This should fix every problem related to (but not only): * Connecting to server while RUI dialogue is still open. * Connecting to server while in an active game. * Running 'weapon_reparse'. --- r5dev/core/init.cpp | 10 +++-- r5dev/core/init.h | 4 +- r5dev/engine/host.cpp | 43 +++++++++++++++++++++ r5dev/engine/host.h | 19 +++++---- r5dev/gameui/IBrowser.cpp | 4 +- r5dev/gameui/IConsole.cpp | 4 +- r5dev/launcher/IApplication.cpp | 3 ++ r5dev/networksystem/listmanager.cpp | 4 +- r5dev/public/iframetask.h | 23 +++++++++++ r5dev/tier0/frametask.cpp | 55 +++++++++++++++++++++++++++ r5dev/tier0/frametask.h | 23 +++++++++++ r5dev/vproj/clientsdk.vcxproj | 4 ++ r5dev/vproj/clientsdk.vcxproj.filters | 12 ++++++ r5dev/vproj/dedicated.vcxproj | 4 ++ r5dev/vproj/dedicated.vcxproj.filters | 12 ++++++ r5dev/vproj/gamesdk.vcxproj | 4 ++ r5dev/vproj/gamesdk.vcxproj.filters | 12 ++++++ r5dev/windows/console.cpp | 3 +- 18 files changed, 222 insertions(+), 21 deletions(-) create mode 100644 r5dev/engine/host.cpp create mode 100644 r5dev/public/iframetask.h create mode 100644 r5dev/tier0/frametask.cpp create mode 100644 r5dev/tier0/frametask.h diff --git a/r5dev/core/init.cpp b/r5dev/core/init.cpp index 78a38ba1..012aa82f 100644 --- a/r5dev/core/init.cpp +++ b/r5dev/core/init.cpp @@ -137,7 +137,7 @@ void Systems_Init() initTimer.Start(); - WS_Init(); // Initialize Winsock. + WinSock_Init(); // Initialize Winsock. MathLib_Init(); // Initialize Mathlib. // Begin the detour transaction to hook the the process @@ -174,6 +174,7 @@ void Systems_Init() CServer_Attach(); // S1 and S2 CServer functions require work. #endif // !CLIENT_DLL && GAMEDLL_S3 + Host_Attach(); CHostState_Attach(); CModelBsp_Attach(); @@ -260,7 +261,7 @@ void Systems_Shutdown() shutdownTimer.Start(); // Shutdown Winsock system. - WS_Shutdown(); + WinSock_Shutdown(); // Begin the detour transaction to unhook the the process DetourTransactionBegin(); @@ -296,6 +297,7 @@ void Systems_Shutdown() CServer_Detach(); // S1 and S2 CServer functions require work. #endif // !CLIENT_DLL && GAMEDLL_S3 + Host_Detach(); CHostState_Detach(); CModelBsp_Detach(); @@ -361,7 +363,7 @@ void Systems_Shutdown() // ///////////////////////////////////////////////////// -void WS_Init() +void WinSock_Init() { WSAData wsaData{}; int nError = ::WSAStartup(MAKEWORD(2, 2), &wsaData); @@ -370,7 +372,7 @@ void WS_Init() std::cerr << "Failed to start Winsock via WSAStartup: (" << NET_ErrorString(WSAGetLastError()) << ")" << std::endl; } } -void WS_Shutdown() +void WinSock_Shutdown() { int nError = ::WSACleanup(); if (nError != 0) diff --git a/r5dev/core/init.h b/r5dev/core/init.h index 54e41969..96f76ed1 100644 --- a/r5dev/core/init.h +++ b/r5dev/core/init.h @@ -11,8 +11,8 @@ void R5Dev_Shutdown(); void Systems_Init(); void Systems_Shutdown(); -void WS_Init(); -void WS_Shutdown(); +void WinSock_Init(); +void WinSock_Shutdown(); void QuerySystemInfo(); void DetourInit(); diff --git a/r5dev/engine/host.cpp b/r5dev/engine/host.cpp new file mode 100644 index 00000000..230fdc6e --- /dev/null +++ b/r5dev/engine/host.cpp @@ -0,0 +1,43 @@ +//===== Copyright � 1996-2005, Valve Corporation, All rights reserved. ========// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include "core/stdafx.h" +#include "tier0/frametask.h" +#include "engine/host.h" + +/* +================== +_Host_RunFrame + +Runs all active servers +================== +*/ +void _Host_RunFrame(void* unused, float time) +{ + for (const auto& task : g_FrameTasks) + { + task->RunFrame(); + } + + g_FrameTasks.erase(std::remove_if(g_FrameTasks.begin(), g_FrameTasks.end(), [](const IFrameTask* task) + { + return task->IsFinished(); + }), g_FrameTasks.end()); + + return v_Host_RunFrame(unused, time); +} + +/////////////////////////////////////////////////////////////////////////////// +void Host_Attach() +{ + DetourAttach((LPVOID*)&v_Host_RunFrame, &_Host_RunFrame); +} + +void Host_Detach() +{ + DetourDetach((LPVOID*)&v_Host_RunFrame, &_Host_RunFrame); +} \ No newline at end of file diff --git a/r5dev/engine/host.h b/r5dev/engine/host.h index 3312962a..ecf24131 100644 --- a/r5dev/engine/host.h +++ b/r5dev/engine/host.h @@ -1,22 +1,25 @@ #pragma once inline CMemory p_Host_RunFrame; -inline auto _Host_RunFrame = p_Host_RunFrame.RCast(); +inline auto v_Host_RunFrame = p_Host_RunFrame.RCast(); inline CMemory p_Host_RunFrame_Render; -inline auto _Host_RunFrame_Render = p_Host_RunFrame_Render.RCast(); +inline auto v_Host_RunFrame_Render = p_Host_RunFrame_Render.RCast(); inline CMemory p_Host_Error; -inline auto Host_Error = p_Host_Error.RCast(); +inline auto v_Host_Error = p_Host_Error.RCast(); inline CMemory p_VCR_EnterPausedState; -inline auto VCR_EnterPausedState = p_VCR_EnterPausedState.RCast(); +inline auto v_VCR_EnterPausedState = p_VCR_EnterPausedState.RCast(); inline bool* g_bAbortServerSet = nullptr; inline jmp_buf* host_abortserver = nullptr; inline float* interval_per_tick = nullptr; + +void Host_Attach(); +void Host_Detach(); /////////////////////////////////////////////////////////////////////////////// class VHost : public IDetour { @@ -42,10 +45,10 @@ class VHost : public IDetour p_Host_Error = g_GameDll.FindPatternSIMD(reinterpret_cast("\x48\x89\x4C\x24\x00\x48\x89\x54\x24\x00\x4C\x89\x44\x24\x00\x4C\x89\x4C\x24\x00\x53\x57\x48\x81\xEC\x00\x00\x00\x00"), "xxxx?xxxx?xxxx?xxxx?xxxxx????"); p_VCR_EnterPausedState = g_GameDll.FindPatternSIMD(reinterpret_cast("\x40\x53\x48\x83\xEC\x20\x65\x48\x8B\x04\x25\x00\x00\x00\x00\xBB\x00\x00\x00\x00\xC6\x05\x00\x00\x00\x00\x00"), "xxxxxxxxxxx????x????xx?????"); - _Host_RunFrame = p_Host_RunFrame.RCast(); - _Host_RunFrame_Render = p_Host_Error.RCast(); - Host_Error = p_Host_Error.RCast(); - VCR_EnterPausedState = p_VCR_EnterPausedState.RCast(); + v_Host_RunFrame = p_Host_RunFrame.RCast(); + v_Host_RunFrame_Render = p_Host_Error.RCast(); + v_Host_Error = p_Host_Error.RCast(); + v_VCR_EnterPausedState = p_VCR_EnterPausedState.RCast(); } virtual void GetVar(void) const { diff --git a/r5dev/gameui/IBrowser.cpp b/r5dev/gameui/IBrowser.cpp index ff0eae67..7547f65b 100644 --- a/r5dev/gameui/IBrowser.cpp +++ b/r5dev/gameui/IBrowser.cpp @@ -14,6 +14,7 @@ History: #include "core/stdafx.h" #include "core/init.h" #include "core/resource.h" +#include "tier0/frametask.h" #include "tier0/commandline.h" #include "tier1/IConVar.h" #include "tier1/cvar.h" @@ -624,8 +625,7 @@ void CBrowser::SendHostingPostRequest(void) void CBrowser::ProcessCommand(const char* pszCommand) const { Cbuf_AddText(Cbuf_GetCurrentPlayer(), pszCommand, cmd_source_t::kCommandSrcCode); - std::thread t(Cbuf_Execute); - t.detach(); // Detatch from render thread. + g_DelayedCallTask->AddFunc(Cbuf_Execute, 0); // Run in main thread. } //----------------------------------------------------------------------------- diff --git a/r5dev/gameui/IConsole.cpp b/r5dev/gameui/IConsole.cpp index 0e226511..9d3ea116 100644 --- a/r5dev/gameui/IConsole.cpp +++ b/r5dev/gameui/IConsole.cpp @@ -15,6 +15,7 @@ History: #include "core/stdafx.h" #include "core/init.h" #include "core/resource.h" +#include "tier0/frametask.h" #include "tier0/commandline.h" #include "tier1/cvar.h" #include "windows/id3dx.h" @@ -534,8 +535,7 @@ void CConsole::ProcessCommand(const char* pszCommand) DevMsg(eDLL_T::COMMON, "] %s\n", pszCommand); Cbuf_AddText(Cbuf_GetCurrentPlayer(), pszCommand, cmd_source_t::kCommandSrcCode); - std::thread t(Cbuf_Execute); - t.detach(); // Detatch from render thread. + g_DelayedCallTask->AddFunc(Cbuf_Execute, 0); // Run in main thread. m_nHistoryPos = -1; for (size_t i = m_vHistory.size(); i-- > 0; ) diff --git a/r5dev/launcher/IApplication.cpp b/r5dev/launcher/IApplication.cpp index fcd345a0..590b5cf8 100644 --- a/r5dev/launcher/IApplication.cpp +++ b/r5dev/launcher/IApplication.cpp @@ -5,6 +5,7 @@ //=============================================================================// #include "core/stdafx.h" +#include "tier0/frametask.h" #include "tier0/commandline.h" #include "tier1/cvar.h" #include "vpc/interfaces.h" @@ -80,7 +81,9 @@ bool CModAppSystemGroup::Create(CModAppSystemGroup* pModAppSystemGroup) g_pHLClient = nullptr; } + g_FrameTasks.push_back(std::move(g_DelayedCallTask)); g_bAppSystemInit = true; + return CModAppSystemGroup_Create(pModAppSystemGroup); } diff --git a/r5dev/networksystem/listmanager.cpp b/r5dev/networksystem/listmanager.cpp index 698379c6..1bf68d49 100644 --- a/r5dev/networksystem/listmanager.cpp +++ b/r5dev/networksystem/listmanager.cpp @@ -13,6 +13,7 @@ #include "vpc/keyvalues.h" #include "pylon.h" #include "listmanager.h" +#include //----------------------------------------------------------------------------- // Purpose: @@ -89,8 +90,7 @@ void CServerListManager::ConnectToServer(const string& svServer, const string& s void CServerListManager::ProcessCommand(const char* pszCommand) const { Cbuf_AddText(Cbuf_GetCurrentPlayer(), pszCommand, cmd_source_t::kCommandSrcCode); - std::thread t(Cbuf_Execute); - t.detach(); // Detatch from caller thread (would otherwise deadlock!). + g_DelayedCallTask->AddFunc(Cbuf_Execute, 0); // Run in main thread. } CServerListManager* g_pServerListManager = new CServerListManager(); \ No newline at end of file diff --git a/r5dev/public/iframetask.h b/r5dev/public/iframetask.h new file mode 100644 index 00000000..dc747405 --- /dev/null +++ b/r5dev/public/iframetask.h @@ -0,0 +1,23 @@ +#ifndef TIER0_IFRAMETASK_H +#define TIER0_IFRAMETASK_H + +struct DelayedCall_s +{ + int m_nDelayedFrames; + std::function m_rFunctor; + DelayedCall_s(int frames, std::function functor) + { + m_nDelayedFrames = frames; + m_rFunctor = functor; + } +}; + +abstract_class IFrameTask +{ +public: + virtual ~IFrameTask() {} + virtual void RunFrame() = 0; + virtual bool IsFinished() const = 0; +}; + +#endif // TIER0_IFRAMETASK_H diff --git a/r5dev/tier0/frametask.cpp b/r5dev/tier0/frametask.cpp new file mode 100644 index 00000000..9b89f103 --- /dev/null +++ b/r5dev/tier0/frametask.cpp @@ -0,0 +1,55 @@ +//=============================================================================// +// +// Purpose: +// +//----------------------------------------------------------------------------- +// +//=============================================================================// +#include "core/stdafx.h" +#include "tier0/frametask.h" + +//----------------------------------------------------------------------------- +// Purpose: run frame task and process queued calls +//----------------------------------------------------------------------------- +void CFrameTask::RunFrame() +{ + std::lock_guard l(m_Mutex); + for (auto& delay : m_DelayedCalls) + { + delay.m_nDelayedFrames = (std::max)(delay.m_nDelayedFrames - 1, 0); + if (delay.m_nDelayedFrames == 0) + { + delay.m_rFunctor(); + } + } + + auto newEnd = std::remove_if(m_DelayedCalls.begin(), m_DelayedCalls.end(), [](const DelayedCall_s& delay) + { + return delay.m_nDelayedFrames == 0; + }); + m_DelayedCalls.erase(newEnd, m_DelayedCalls.end()); +} + +//----------------------------------------------------------------------------- +// Purpose: is the task finished +// Output : true if finished, false otherwise +//----------------------------------------------------------------------------- +bool CFrameTask::IsFinished() const +{ + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: adds function to list, to be called after 'i' frames. +// Input : functor - +// frames - +//----------------------------------------------------------------------------- +void CFrameTask::AddFunc(std::function functor, int frames) +{ + std::lock_guard l(m_Mutex); + m_DelayedCalls.emplace_back(frames, functor); +} + +//----------------------------------------------------------------------------- +std::list g_FrameTasks; +CFrameTask* g_DelayedCallTask = new CFrameTask(); diff --git a/r5dev/tier0/frametask.h b/r5dev/tier0/frametask.h new file mode 100644 index 00000000..15cd3c06 --- /dev/null +++ b/r5dev/tier0/frametask.h @@ -0,0 +1,23 @@ +#ifndef TIER0_FRAMETASK_H +#define TIER0_FRAMETASK_H + +#include "public/iframetask.h" + +class CFrameTask : public IFrameTask +{ +public: + virtual ~CFrameTask() {} + virtual void RunFrame(); + virtual bool IsFinished() const; + + void AddFunc(std::function functor, int frames); + +private: + std::mutex m_Mutex; + std::list m_DelayedCalls; +}; + +extern std::list g_FrameTasks; +extern CFrameTask* g_DelayedCallTask; + +#endif // TIER0_FRAMETASK_H diff --git a/r5dev/vproj/clientsdk.vcxproj b/r5dev/vproj/clientsdk.vcxproj index ad4d32d9..b71de9d4 100644 --- a/r5dev/vproj/clientsdk.vcxproj +++ b/r5dev/vproj/clientsdk.vcxproj @@ -36,6 +36,7 @@ + @@ -105,6 +106,7 @@ + @@ -256,6 +258,7 @@ + @@ -472,6 +475,7 @@ + diff --git a/r5dev/vproj/clientsdk.vcxproj.filters b/r5dev/vproj/clientsdk.vcxproj.filters index 04dcfe6b..302c518a 100644 --- a/r5dev/vproj/clientsdk.vcxproj.filters +++ b/r5dev/vproj/clientsdk.vcxproj.filters @@ -579,6 +579,12 @@ sdk\networksystem + + sdk\engine + + + sdk\tier0 + @@ -1727,6 +1733,12 @@ sdk\public + + sdk\public + + + sdk\tier0 + diff --git a/r5dev/vproj/dedicated.vcxproj b/r5dev/vproj/dedicated.vcxproj index bc09662e..80c60ae9 100644 --- a/r5dev/vproj/dedicated.vcxproj +++ b/r5dev/vproj/dedicated.vcxproj @@ -222,6 +222,7 @@ + @@ -427,6 +428,7 @@ + @@ -487,6 +489,7 @@ + @@ -555,6 +558,7 @@ + diff --git a/r5dev/vproj/dedicated.vcxproj.filters b/r5dev/vproj/dedicated.vcxproj.filters index 213a1d15..426ad0ba 100644 --- a/r5dev/vproj/dedicated.vcxproj.filters +++ b/r5dev/vproj/dedicated.vcxproj.filters @@ -1233,6 +1233,12 @@ sdk\public + + sdk\public + + + sdk\tier0 + @@ -1538,6 +1544,12 @@ sdk\tier1 + + sdk\engine + + + sdk\tier0 + diff --git a/r5dev/vproj/gamesdk.vcxproj b/r5dev/vproj/gamesdk.vcxproj index 64778283..7a8ae3e1 100644 --- a/r5dev/vproj/gamesdk.vcxproj +++ b/r5dev/vproj/gamesdk.vcxproj @@ -36,6 +36,7 @@ + @@ -115,6 +116,7 @@ + @@ -281,6 +283,7 @@ + @@ -499,6 +502,7 @@ + diff --git a/r5dev/vproj/gamesdk.vcxproj.filters b/r5dev/vproj/gamesdk.vcxproj.filters index 153770f7..5436d58a 100644 --- a/r5dev/vproj/gamesdk.vcxproj.filters +++ b/r5dev/vproj/gamesdk.vcxproj.filters @@ -618,6 +618,12 @@ sdk\networksystem + + sdk\tier0 + + + sdk\engine + @@ -1814,6 +1820,12 @@ sdk\public + + sdk\tier0 + + + sdk\public + diff --git a/r5dev/windows/console.cpp b/r5dev/windows/console.cpp index 68b8e7db..766585f1 100644 --- a/r5dev/windows/console.cpp +++ b/r5dev/windows/console.cpp @@ -7,6 +7,7 @@ #include "core/stdafx.h" #include "core/init.h" #include "core/logdef.h" +#include "tier0/frametask.h" #include "tier1/cmd.h" #ifndef DEDICATED #include "windows/id3dx.h" @@ -135,7 +136,7 @@ DWORD __stdcall ProcessConsoleWorker(LPVOID) // Execute the command. Cbuf_AddText(Cbuf_GetCurrentPlayer(), sCommand.c_str(), cmd_source_t::kCommandSrcCode); - Cbuf_Execute(); + g_DelayedCallTask->AddFunc(Cbuf_Execute, 0); sCommand.clear();