r5sdk/r5dev/engine/client/clientstate.cpp
Kawe Mazidjatari a33f569ad5 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.
2023-06-18 17:13:42 +02:00

181 lines
6.7 KiB
C++

//=============================================================================//
//
// Purpose:
//
// $NoKeywords: $
//
//=============================================================================//
// clientstate.cpp: implementation of the CClientState class.
//
/////////////////////////////////////////////////////////////////////////////////
#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"
//------------------------------------------------------------------------------
// Purpose: returns true if client simulation is paused
//------------------------------------------------------------------------------
bool CClientState::IsPaused() const
{
return m_bPaused || !*host_initialized || g_pEngineVGui->ShouldPause();
}
//------------------------------------------------------------------------------
// Purpose: returns true if client is fully connected and active
//------------------------------------------------------------------------------
bool CClientState::IsActive(void) const
{
return m_nSignonState == SIGNONSTATE::SIGNONSTATE_FULL;
};
//------------------------------------------------------------------------------
// Purpose: returns true if client connected but not active
//------------------------------------------------------------------------------
bool CClientState::IsConnected(void) const
{
return m_nSignonState >= SIGNONSTATE::SIGNONSTATE_CONNECTED;
};
//------------------------------------------------------------------------------
// Purpose: returns true if client is still connecting
//------------------------------------------------------------------------------
bool CClientState::IsConnecting(void) const
{
return m_nSignonState >= SIGNONSTATE::SIGNONSTATE_NONE;
}
//------------------------------------------------------------------------------
// Purpose: gets the client time
// Technically doesn't belong here
//------------------------------------------------------------------------------
float CClientState::GetClientTime() const
{
if (m_bClockCorrectionEnabled)
{
return (float)m_ClockDriftMgr.m_nSimulationTick * (*(float*)&interval_per_tick); // VERIFY DEREF
}
else
{
return m_flClockDriftFrameTime;
}
}
//------------------------------------------------------------------------------
// Purpose: gets the simulation tick count
//------------------------------------------------------------------------------
int CClientState::GetTick() const
{
return m_ClockDriftMgr.m_nSimulationTick;
}
//------------------------------------------------------------------------------
// Purpose: gets the last-received server tick count
//------------------------------------------------------------------------------
int CClientState::GetServerTickCount() const
{
return m_ClockDriftMgr.m_nServerTick;
}
//------------------------------------------------------------------------------
// Purpose: sets the server tick count
//------------------------------------------------------------------------------
void CClientState::SetServerTickCount(int tick)
{
m_ClockDriftMgr.m_nServerTick = tick;
}
//------------------------------------------------------------------------------
// Purpose: gets the client tick count
//------------------------------------------------------------------------------
int CClientState::GetClientTickCount() const
{
return m_ClockDriftMgr.m_nClientTick;
}
//------------------------------------------------------------------------------
// Purpose: sets the client tick count
//------------------------------------------------------------------------------
void CClientState::SetClientTickCount(int tick)
{
m_ClockDriftMgr.m_nClientTick = tick;
}
//------------------------------------------------------------------------------
// Purpose: gets the client frame time
//------------------------------------------------------------------------------
float CClientState::GetFrameTime() const
{
if (IsPaused())
{
return 0.0f;
}
return m_flFrameTime;
}
//------------------------------------------------------------------------------
// 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)
{
if (pServerTick->m_NetTick.m_nCommandTick != -1)
{
return CClientState__ProcessServerTick(pClientState, pServerTick);
}
else // Statistics only.
{
char* pShifted = reinterpret_cast<char*>(pClientState) - 0x10; // Shifted due to compiler optimizations.
CClientState* pClient_Adj = reinterpret_cast<CClientState*>(pShifted);
CNetChan* pChan = pClient_Adj->m_NetChannel;
pChan->SetRemoteFramerate(pServerTick->m_NetTick.m_flHostFrameTime, pServerTick->m_NetTick.m_flHostFrameTimeStdDeviation);
pChan->SetRemoteCPUStatistics(pServerTick->m_NetTick.m_nServerCPU);
return true;
}
}
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);
}
/////////////////////////////////////////////////////////////////////////////////
CClientState* g_pClientState = nullptr;
CClientState** g_pClientState_Shifted = nullptr;