Server: sync client's clock with server's

This should in theory fix the server and client desync that takes place after multiple hours of uptime without disconnecting or reloading. Note that in this version of the engine the server tick is only send on connect (Titanfall 2 does seem to send it every tick, as this netmsg is also responsible for updating the server percentage on the 'cl_showfps' HUD, which does work as designed). The patched code still writes a 'statistics only' server tick update every tick, but only sends full if the interval time has been reached for that particular client instance.
This commit is contained in:
Kawe Mazidjatari 2023-12-30 01:17:39 +01:00
parent 0c56af38fc
commit caba6a7aba
4 changed files with 40 additions and 3 deletions

View File

@ -129,6 +129,8 @@ ConVar* sv_maxunlag = nullptr;
ConVar* sv_unlag_clamp = nullptr;
ConVar* sv_clockcorrection_msecs = nullptr;
ConVar* sv_clockSyncInterval = nullptr;
ConVar* sv_updaterate_sp = nullptr;
ConVar* sv_updaterate_mp = nullptr;
@ -377,6 +379,8 @@ void ConVar_StaticInit(void)
sv_unlag_clamp = ConVar::StaticCreate("sv_unlag_clamp", "0", FCVAR_RELEASE, "Clamp the difference between the current time and received command time to sv_maxunlag + sv_clockcorrection_msecs.", false, 0.f, false, 0.f, nullptr, nullptr);
sv_clockSyncInterval = ConVar::StaticCreate("sv_clockSyncInterval", "5.0", FCVAR_RELEASE, "Interval in seconds in which the clients's clock will be synced with that of the server's.", false, 0.f, false, 0.f, nullptr, nullptr);
sv_showconnecting = ConVar::StaticCreate("sv_showconnecting" , "1", FCVAR_RELEASE, "Logs information about the connecting client to the console.", false, 0.f, false, 0.f, nullptr, nullptr);
sv_globalBanlist = ConVar::StaticCreate("sv_globalBanlist" , "1", FCVAR_RELEASE, "Determines whether or not to use the global banned list.", false, 0.f, false, 0.f, nullptr, "0 = Disable, 1 = Enable.");
sv_pylonVisibility = ConVar::StaticCreate("sv_pylonVisibility", "0", FCVAR_RELEASE, "Determines the visibility to the Pylon master server.", false, 0.f, false, 0.f, nullptr, "0 = Offline, 1 = Hidden, 2 = Public.");

View File

@ -118,6 +118,8 @@ extern ConVar* sv_maxunlag;
extern ConVar* sv_unlag_clamp;
extern ConVar* sv_clockcorrection_msecs;
extern ConVar* sv_clockSyncInterval;
extern ConVar* sv_updaterate_sp;
extern ConVar* sv_updaterate_mp;

View File

@ -93,20 +93,47 @@ bool CNetworkStringTable::Lock(bool bLock)
void CNetworkStringTableContainer::WriteUpdateMessage(CNetworkStringTableContainer* thisp, CClient* pClient, unsigned int nTickAck, bf_write* pMsg)
{
#ifndef CLIENT_DLL
if (sv_stats->GetBool())
const double currentTime = Plat_FloatTime();
ServerPlayer_t& clientExtended = g_ServerPlayer[pClient->GetUserID()];
int commandTick = -1; // -1 means we update statistics only; see 'CClientState::VProcessServerTick()'.
// NOTE: if we send this message each tick, the client will start to
// falter. Unlike other source games, we have to have some delay in
// between each server tick message for this to work correctly.
if (clientExtended.m_bRetryClockSync ||
(currentTime - clientExtended.m_flLastClockSyncTime) > sv_clockSyncInterval->GetFloat())
{
uint8_t nCPUPercentage = static_cast<uint8_t>(g_pServer->GetCPUUsage() * 100.0f);
// Sync the clocks on the client with that of the server's.
commandTick = pClient->GetCommandTick();
clientExtended.m_flLastClockSyncTime = currentTime;
clientExtended.m_bRetryClockSync = false;
}
// If commandTick == statistics only while server opted out, do not
// send the message.
const bool shouldSend = (commandTick == -1 && !sv_stats->GetBool()) ? false : true;
if (shouldSend)
{
const uint8_t nCPUPercentage = static_cast<uint8_t>(g_pServer->GetCPUUsage() * 100.0f);
SVC_ServerTick serverTick(g_pServer->GetTick(), *host_frametime_unbounded, *host_frametime_stddeviation, nCPUPercentage);
serverTick.m_nGroup = 0;
serverTick.m_bReliable = true;
serverTick.m_NetTick.m_nCommandTick = -1; // Statistics only, see 'CClientState::VProcessServerTick'.
serverTick.m_NetTick.m_nCommandTick = commandTick;
pMsg->WriteUBitLong(serverTick.GetType(), NETMSG_TYPE_BITS);
if (!pMsg->IsOverflowed())
{
serverTick.WriteToBuffer(pMsg);
}
else
{
Assert(0, "Snapshot buffer overflowed before string table update!");
clientExtended.m_bRetryClockSync = true; // Retry on next snapshot for this client.
}
}
#endif // !CLIENT_DLL
v_CNetworkStringTableContainer__WriteUpdateMessage(thisp, pClient, nTickAck, pMsg);

View File

@ -20,15 +20,19 @@ struct ServerPlayer_t
{
m_flCurrentNetProcessTime = 0.0;
m_flLastNetProcessTime = 0.0;
m_flLastClockSyncTime = 0.0;
m_flStringCommandQuotaTimeStart = 0.0;
m_nStringCommandQuotaCount = NULL;
m_bRetryClockSync = false;
m_bInitialConVarsSet = false;
}
double m_flCurrentNetProcessTime;
double m_flLastNetProcessTime;
double m_flLastClockSyncTime;
double m_flStringCommandQuotaTimeStart;
int m_nStringCommandQuotaCount;
bool m_bRetryClockSync;
bool m_bInitialConVarsSet;
};