From a33f569ad5a87ab8ce7d57a5c97906e33ac58a43 Mon Sep 17 00:00:00 2001 From: Kawe Mazidjatari <48657826+Mauler125@users.noreply.github.com> Date: Sun, 18 Jun 2023 17:10:57 +0200 Subject: [PATCH] Proper fix for playlists reloading on disconnect This fixes a crash/exception that would occur when getting disconnected from your own listenserver due to the playlists reload task dispatch. Moving this to client only code, in the disconnect routine (post disconnect) fixes this problem. --- r5dev/engine/client/clientstate.cpp | 29 ++++++++++++++++++++++++++++- r5dev/engine/client/clientstate.h | 21 +++++++++++++-------- r5dev/engine/net_chan.cpp | 11 ----------- 3 files changed, 41 insertions(+), 20 deletions(-) diff --git a/r5dev/engine/client/clientstate.cpp b/r5dev/engine/client/clientstate.cpp index d7ea44e1..35c528ac 100644 --- a/r5dev/engine/client/clientstate.cpp +++ b/r5dev/engine/client/clientstate.cpp @@ -9,8 +9,11 @@ // ///////////////////////////////////////////////////////////////////////////////// #include "core/stdafx.h" +#include "vpc/keyvalues.h" +#include "tier0/frametask.h" #include "engine/host.h" #include "clientstate.h" +#include "common/callback.h" #include "cdll_engine_int.h" #include "vgui/vgui_baseui_interface.h" @@ -117,7 +120,29 @@ float CClientState::GetFrameTime() const } //------------------------------------------------------------------------------ -// Purpose: +// Purpose: called when connection to the server has been closed +//------------------------------------------------------------------------------ +void CClientState::VConnectionClosing(CClientState* thisptr, const char* szReason) +{ + CClientState__ConnectionClosing(thisptr, szReason); + + // Delay execution to the next frame; this is required to avoid a rare crash. + // Cannot reload playlists while still disconnecting. + g_TaskScheduler->Dispatch([]() + { + // Reload the local playlist to override the cached + // one from the server we got disconnected from. + _DownloadPlaylists_f(); + KeyValues::InitPlaylists(); + }, 0); +} + +//------------------------------------------------------------------------------ +// Purpose: called when a SVC_ServerTick messages comes in. +// This function has an additional check for the command tick against '-1', +// if it is '-1', we process statistics only. This is required as the game +// no longer can process server ticks every frame unlike previous games. +// Without this, the server CPU and frame time don't get updated to the client. //------------------------------------------------------------------------------ bool CClientState::VProcessServerTick(CClientState* pClientState, SVC_ServerTick* pServerTick) { @@ -140,11 +165,13 @@ bool CClientState::VProcessServerTick(CClientState* pClientState, SVC_ServerTick void VClientState::Attach() const { + DetourAttach(&CClientState__ConnectionClosing, &CClientState::VConnectionClosing); DetourAttach(&CClientState__ProcessServerTick, &CClientState::VProcessServerTick); } void VClientState::Detach() const { + DetourDetach(&CClientState__ConnectionClosing, &CClientState::VConnectionClosing); DetourDetach(&CClientState__ProcessServerTick, &CClientState::VProcessServerTick); } diff --git a/r5dev/engine/client/clientstate.h b/r5dev/engine/client/clientstate.h index 4aca4547..6cf05ab2 100644 --- a/r5dev/engine/client/clientstate.h +++ b/r5dev/engine/client/clientstate.h @@ -24,6 +24,7 @@ public: class CClientState : CS_INetChannelHandler, IConnectionlessPacketHandler, IServerMessageHandler, CClientSnapshotManager { public: // Hook statics. + static void VConnectionClosing(CClientState* thisptr, const char* szReason); static bool VProcessServerTick(CClientState* thisptr, SVC_ServerTick* msg); public: @@ -202,13 +203,15 @@ extern CClientState** g_pClientState_Shifted; // Shifted by 0x10 forward! inline CMemory p_CClientState__RunFrame; inline auto CClientState__RunFrame = p_CClientState__RunFrame.RCast(); -inline CMemory p_CClientState__Disconnect; /*48 89 5C 24 ?? 55 57 41 56 48 83 EC 30 0F B6 EA*/ +inline CMemory p_CClientState__Disconnect; inline auto CClientState__Disconnect = p_CClientState__Disconnect.RCast(); +inline CMemory p_CClientState__ConnectionClosing; +inline auto CClientState__ConnectionClosing = p_CClientState__ConnectionClosing.RCast(); + inline CMemory p_CClientState__ProcessServerTick; inline auto CClientState__ProcessServerTick = p_CClientState__ProcessServerTick.RCast(); - /////////////////////////////////////////////////////////////////////////////// class VClientState : public IDetour { @@ -216,26 +219,28 @@ class VClientState : public IDetour { LogFunAdr("CClientState::RunFrame", p_CClientState__RunFrame.GetPtr()); LogFunAdr("CClientState::Disconnect", p_CClientState__Disconnect.GetPtr()); + LogFunAdr("CClientState::ConnectionClosing", p_CClientState__ConnectionClosing.GetPtr()); LogFunAdr("CClientState::ProcessServerTick", p_CClientState__ProcessServerTick.GetPtr()); LogVarAdr("g_pClientState", reinterpret_cast(g_pClientState)); LogVarAdr("g_pClientState_Shifted", reinterpret_cast(g_pClientState_Shifted)); } virtual void GetFun(void) const { + #if defined (GAMEDLL_S0) || defined (GAMEDLL_S1) p_CClientState__RunFrame = g_GameDll.FindPatternSIMD("48 89 4C 24 ?? 57 48 81 EC ?? ?? ?? ?? 83 B9 ?? ?? ?? ?? ??"); - CClientState__RunFrame = p_CClientState__RunFrame.RCast(); - p_CClientState__Disconnect = g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 55 57 41 56 48 83 EC 30 0F B6 EA"); - CClientState__Disconnect = p_CClientState__Disconnect.RCast(); /*48 89 5C 24 ?? 55 57 41 56 48 83 EC 30 0F B6 EA*/ + p_CClientState__ConnectionClosing = g_GameDll.FindPatternSIMD("40 53 48 83 EC 20 83 B9 ?? ?? ?? ?? ?? 48 8B DA 7E 6E"); #elif defined (GAMEDLL_S2) || defined (GAMEDLL_S3) p_CClientState__RunFrame = g_GameDll.FindPatternSIMD("40 53 48 81 EC ?? ?? ?? ?? 83 B9 ?? ?? ?? ?? ?? 48 8B D9 7D 0B"); - CClientState__RunFrame = p_CClientState__RunFrame.RCast(); - p_CClientState__Disconnect = g_GameDll.FindPatternSIMD("40 56 57 41 54 41 55 41 57 48 83 EC 30 44 0F B6 FA"); - CClientState__Disconnect = p_CClientState__Disconnect.RCast(); /*40 56 57 41 54 41 55 41 57 48 83 EC 30 44 0F B6 FA*/ + p_CClientState__ConnectionClosing = g_GameDll.FindPatternSIMD("40 53 48 83 EC 20 83 B9 ?? ?? ?? ?? ?? 48 8B DA 0F 8E ?? ?? ?? ??"); #endif p_CClientState__ProcessServerTick = g_GameDll.FindPatternSIMD("40 57 48 83 EC 20 83 B9 ?? ?? ?? ?? ?? 48 8B F9 7C 66"); + + CClientState__RunFrame = p_CClientState__RunFrame.RCast(); + CClientState__Disconnect = p_CClientState__Disconnect.RCast(); + CClientState__ConnectionClosing = p_CClientState__ConnectionClosing.RCast(); CClientState__ProcessServerTick = p_CClientState__ProcessServerTick.RCast(); } virtual void GetVar(void) const diff --git a/r5dev/engine/net_chan.cpp b/r5dev/engine/net_chan.cpp index d0dff030..e20e90e9 100644 --- a/r5dev/engine/net_chan.cpp +++ b/r5dev/engine/net_chan.cpp @@ -233,17 +233,6 @@ void CNetChan::Clear(bool bStopProcessing) void CNetChan::_Shutdown(CNetChan* pChan, const char* szReason, uint8_t bBadRep, bool bRemoveNow) { v_NetChan_Shutdown(pChan, szReason, bBadRep, bRemoveNow); - -#ifndef DEDICATED - // Delay execution to the next frame; this is required to avoid a rare crash. - // Cannot reload playlists while still disconnecting. - g_TaskScheduler->Dispatch([]() - { - // Re-load and re-init playlists from the disk to replace the cached one we received from the server. - _DownloadPlaylists_f(); - KeyValues::InitPlaylists(); - }, 0); -#endif // !DEDICATED } //-----------------------------------------------------------------------------