More thread safety improvements

* Run 'CHostState::Think()' in the main thread.
* Construct 'NetGameServer_t' objects in main thread before dispatching (TODO: browser).
* Dispatch the call to 'CBanSystem::AddConnectionRefuse' from 'SV_IsClientBanned' to the main thread if we aren't main.
* Return bool for CBanSystem::AddEntry and CBanSystem::DeleteEntry for future optimizations (next commit).
This commit is contained in:
Kawe Mazidjatari 2022-08-27 23:45:58 +02:00
parent 656b0be3ec
commit 532b4125d5
12 changed files with 125 additions and 93 deletions

View File

@ -42,10 +42,12 @@
#endif // DEDICATED
#include "networksystem/pylon.h"
#include "networksystem/bansystem.h"
#include "networksystem/listmanager.h"
#include "public/edict.h"
#ifndef CLIENT_DLL
#include "game/server/gameinterface.h"
#endif // !CLIENT_DLL
#include "squirrel/sqinit.h"
//-----------------------------------------------------------------------------
// Purpose: state machine's main processing loop
@ -58,6 +60,8 @@ FORCEINLINE void CHostState::FrameUpdate(CHostState* pHostState, double flCurren
g_pHostState->Setup();
bInitialized = true;
}
g_pHostState->Think();
#ifdef DEDICATED
RCONServer()->RunFrame();
#else //
@ -162,11 +166,11 @@ FORCEINLINE void CHostState::Init(void)
m_iNextState = HostStates_t::HS_RUN;
}
}
m_flShortFrameTime = 1.0;
m_flShortFrameTime = 1.0f;
m_levelName[0] = 0;
m_landMarkName[0] = 0;
m_mapGroupName[0] = 0;
m_bSplitScreenConnect = 256;
m_bSplitScreenConnect = 256; // Is this actually 'm_bSplitScreenConnect'? (assembly sets this value, other 3 bytes are padded which makes this operation valid still).
m_vecLocation.Init();
m_angLocation.Init();
m_iCurrentState = HostStates_t::HS_NEW_GAME;
@ -180,9 +184,6 @@ FORCEINLINE void CHostState::Setup(void)
g_pHostState->LoadConfig();
g_pConVar->PurgeHostNames();
std::thread think(&CHostState::Think, this);
think.detach();
net_usesocketsforloopback->SetValue(1);
if (net_useRandomKey->GetBool())
{
@ -201,46 +202,61 @@ FORCEINLINE void CHostState::Think(void) const
static CFastTimer pylonTimer;
static CFastTimer statsTimer;
for (;;) // Loop running at 20-tps.
if (!bInitialized) // Initialize clocks.
{
if (!bInitialized) // Initialize clocks.
{
#ifndef CLIENT_DLL
banListTimer.Start();
banListTimer.Start();
#ifdef DEDICATED
pylonTimer.Start();
pylonTimer.Start();
#endif // DEDICATED
statsTimer.Start();
#endif // !CLIENT_DLL
bInitialized = true;
}
#ifndef CLIENT_DLL
if (banListTimer.GetDurationInProgress().GetSeconds() > sv_banlistRefreshInterval->GetDouble())
{
g_pBanSystem->BanListCheck();
banListTimer.Start();
}
#endif // !CLIENT_DLL
#ifdef DEDICATED
if (pylonTimer.GetDurationInProgress().GetSeconds() > sv_pylonRefreshInterval->GetDouble())
{
KeepAliveToPylon();
pylonTimer.Start();
}
#endif // DEDICATED
#ifndef CLIENT_DLL
if (statsTimer.GetDurationInProgress().GetSeconds() > sv_statusRefreshInterval->GetDouble())
{
string svCurrentPlaylist = KeyValues_GetCurrentPlaylist();
int32_t nPlayerCount = g_pServer->GetNumHumanPlayers();
SetConsoleTitleA(fmt::format("{:s} - {:d}/{:d} Players ({:s} on {:s})",
hostname->GetString(), nPlayerCount, g_ServerGlobalVariables->m_nMaxClients, svCurrentPlaylist, m_levelName).c_str());
statsTimer.Start();
}
std::this_thread::sleep_for(std::chrono::milliseconds(50));
statsTimer.Start();
#endif // !CLIENT_DLL
bInitialized = true;
}
#ifndef CLIENT_DLL
if (banListTimer.GetDurationInProgress().GetSeconds() > sv_banlistRefreshInterval->GetDouble())
{
g_pBanSystem->BanListCheck();
banListTimer.Start();
}
#endif // !CLIENT_DLL
#ifdef DEDICATED
if (pylonTimer.GetDurationInProgress().GetSeconds() > sv_pylonRefreshInterval->GetDouble())
{
const NetGameServer_t netGameServer
{
hostname->GetString(),
hostdesc->GetString(),
sv_pylonVisibility->GetInt() == EServerVisibility_t::HIDDEN,
g_pHostState->m_levelName,
mp_gamemode->GetString(),
hostip->GetString(),
hostport->GetString(),
g_svNetKey,
std::to_string(*g_nServerRemoteChecksum),
SDK_VERSION,
std::to_string(g_pServer->GetNumHumanPlayers() + g_pServer->GetNumFakeClients()),
std::to_string(g_ServerGlobalVariables->m_nMaxClients),
std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::system_clock::now().time_since_epoch()
).count()
};
std::thread(KeepAliveToPylon, netGameServer).detach();
pylonTimer.Start();
}
#endif // DEDICATED
#ifndef CLIENT_DLL
if (statsTimer.GetDurationInProgress().GetSeconds() > sv_statusRefreshInterval->GetDouble())
{
string svCurrentPlaylist = KeyValues_GetCurrentPlaylist();
int32_t nPlayerCount = g_pServer->GetNumHumanPlayers();
SetConsoleTitleA(fmt::format("{:s} - {:d}/{:d} Players ({:s} on {:s})",
hostname->GetString(), nPlayerCount, g_ServerGlobalVariables->m_nMaxClients, svCurrentPlaylist, m_levelName).c_str());
statsTimer.Start();
}
#endif // !CLIENT_DLL
}
//-----------------------------------------------------------------------------

View File

@ -71,7 +71,7 @@ CClient* CServer::Authenticate(CServer* pServer, user_creds_s* pInpacket)
DevMsg(eDLL_T::SERVER, "\n");
DevMsg(eDLL_T::SERVER, "______________________________________________________________\n");
DevMsg(eDLL_T::SERVER, "] AUTHENTICATION ---------------------------------------------\n");
DevMsg(eDLL_T::SERVER, "] UID : | '%s'\n", pInpacket->m_nUserID);
DevMsg(eDLL_T::SERVER, "] UID : | '%s'\n", pInpacket->m_pUserID);
DevMsg(eDLL_T::SERVER, "] OID : | '%llu'\n", pInpacket->m_nNucleusID);
DevMsg(eDLL_T::SERVER, "] ADR : | '%s'\n", svIpAddress.c_str());
DevMsg(eDLL_T::SERVER, "--------------------------------------------------------------\n");
@ -82,7 +82,6 @@ CClient* CServer::Authenticate(CServer* pServer, user_creds_s* pInpacket)
if (g_pBanSystem->IsBanned(svIpAddress, pInpacket->m_nNucleusID)) // Is the client trying to connect banned?
{
v_CServer_RejectConnection(pServer, pServer->m_Socket, pInpacket, "You have been banned from this server."); // RejectConnection for the client.
if (sv_showconnecting->GetBool())
{
Warning(eDLL_T::SERVER, "Connection rejected for '%s' ('%llu' is banned from this server!)\n", svIpAddress.c_str(), pInpacket->m_nNucleusID);

View File

@ -23,7 +23,7 @@ struct user_creds_s
int32_t m_nchallenge;
uint8_t gap2[8];
uint64_t m_nNucleusID;
uint8_t* m_nUserID;
uint8_t* m_pUserID;
};
class CServer : public IServer

View File

@ -1,4 +1,6 @@
#include "core/stdafx.h"
#include "tier0/threadtools.h"
#include "tier0/frametask.h"
#include "engine/server/sv_main.h"
#include "networksystem/pylon.h"
#include "networksystem/bansystem.h"
@ -6,15 +8,22 @@
//-----------------------------------------------------------------------------
// Purpose: checks if particular client is banned on the comp server
//-----------------------------------------------------------------------------
void SV_IsClientBanned(CPylon* pPylon, const string svIPAddr, uint64_t nNucleusID)
void SV_IsClientBanned(CPylon* pPylon, const string& svIPAddr, const uint64_t nNucleusID)
{
string svError = string();
string svError;
bool bCompBanned = pPylon->GetClientIsBanned(svIPAddr, nNucleusID, svError);
if (bCompBanned)
{
DevMsg(eDLL_T::SERVER, "Connection rejected for '%s' ('%llu' is banned from the master server!)\n", svIPAddr.c_str(), nNucleusID);
g_pBanSystem->AddConnectionRefuse(svError, nNucleusID); // Add to the vector.
if (!ThreadInMainThread())
{
g_TaskScheduler->Dispatch([svError, nNucleusID]
{
g_pBanSystem->AddConnectionRefuse(svError, nNucleusID); // Add to the vector.
}, 0);
}
}
}

View File

@ -21,7 +21,7 @@ inline bool* s_bDedicated = nullptr;
///////////////////////////////////////////////////////////////////////////////
void SV_IsClientBanned(CPylon* pPylon, const string svIPAddr, uint64_t nNucleusID);
void SV_IsClientBanned(CPylon* pPylon, const string& svIPAddr, const uint64_t nNucleusID);
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////

View File

@ -592,7 +592,7 @@ void CBrowser::UpdateHostingStatus(void)
break;
}
NetGameServer_t gameServer // !FIXME: create from main thread.
NetGameServer_t netGameServer // !FIXME: create from main thread.
{
g_pServerListManager->m_Server.m_svHostName,
g_pServerListManager->m_Server.m_svDescription,
@ -611,7 +611,7 @@ void CBrowser::UpdateHostingStatus(void)
).count()
};
std::thread post(&CBrowser::SendHostingPostRequest, this, gameServer);
std::thread post(&CBrowser::SendHostingPostRequest, this, netGameServer);
post.detach();
break;

View File

@ -32,7 +32,7 @@ void CBanSystem::operator[](std::pair<const string&, const uint64_t> pair)
//-----------------------------------------------------------------------------
void CBanSystem::Load(void)
{
fs::path path = std::filesystem::current_path() /= "platform\\banlist.json";
fs::path path = std::filesystem::current_path() /= "platform\\banlist.json"; // !TODO: Use FS "PLATFORM"
nlohmann::json jsIn;
ifstream banFile(path, std::ios::in);
@ -82,7 +82,7 @@ void CBanSystem::Save(void) const
jsOut[std::to_string(i)]["originID"] = m_vBanList[i].second;
}
fs::path path = std::filesystem::current_path() /= "platform\\banlist.json";
fs::path path = std::filesystem::current_path() /= "platform\\banlist.json"; // !TODO: Use FS "PLATFORM".
ofstream outFile(path, std::ios::out | std::ios::trunc); // Write config file..
outFile << jsOut.dump(4);
@ -93,7 +93,7 @@ void CBanSystem::Save(void) const
// Input : &svIpAddress -
// nOriginID -
//-----------------------------------------------------------------------------
void CBanSystem::AddEntry(const string& svIpAddress, const uint64_t nOriginID)
bool CBanSystem::AddEntry(const string& svIpAddress, const uint64_t nOriginID)
{
if (!svIpAddress.empty())
{
@ -101,8 +101,10 @@ void CBanSystem::AddEntry(const string& svIpAddress, const uint64_t nOriginID)
if (it == m_vBanList.end())
{
m_vBanList.push_back(std::make_pair(svIpAddress, nOriginID));
return true;
}
}
return false;
}
//-----------------------------------------------------------------------------
@ -110,15 +112,19 @@ void CBanSystem::AddEntry(const string& svIpAddress, const uint64_t nOriginID)
// Input : &svIpAddress -
// nOriginID -
//-----------------------------------------------------------------------------
void CBanSystem::DeleteEntry(const string& svIpAddress, const uint64_t nOriginID)
bool CBanSystem::DeleteEntry(const string& svIpAddress, const uint64_t nOriginID)
{
bool result = false;
for (size_t i = 0; i < m_vBanList.size(); i++)
{
if (svIpAddress.compare(m_vBanList[i].first) == NULL || nOriginID == m_vBanList[i].second)
{
m_vBanList.erase(m_vBanList.begin() + i);
result = true;
}
}
return result;
}
//-----------------------------------------------------------------------------
@ -166,11 +172,12 @@ void CBanSystem::BanListCheck(void)
{
if (IsRefuseListValid())
{
bool bSave = false;
for (size_t i = 0; i < m_vRefuseList.size(); i++)
{
for (int c = 0; c < MAX_PLAYERS; c++) // Loop through all possible client instances.
{
CClient* pClient = g_pClient->GetClient(i);
CClient* pClient = g_pClient->GetClient(c);
if (!pClient)
continue;
@ -184,11 +191,15 @@ void CBanSystem::BanListCheck(void)
string svIpAddress = pNetChan->GetAddress();
Warning(eDLL_T::SERVER, "Connection rejected for '%s' ('%llu' is banned from this server!)\n", svIpAddress.c_str(), pClient->GetOriginID());
AddEntry(svIpAddress, pClient->GetOriginID());
Save(); // Save banlist to file.
NET_DisconnectClient(pClient, c, m_vRefuseList[i].first.c_str(), 0, true);
if (AddEntry(svIpAddress, pClient->GetOriginID() && !bSave))
bSave = true;
}
}
if (bSave)
Save(); // Save banlist to file.
}
}

View File

@ -9,8 +9,8 @@ public:
void Load(void);
void Save(void) const;
void AddEntry(const string& svIpAddress, const uint64_t nOriginID);
void DeleteEntry(const string& svIpAddress, const uint64_t nOriginID);
bool AddEntry(const string& svIpAddress, const uint64_t nOriginID);
bool DeleteEntry(const string& svIpAddress, const uint64_t nOriginID);
void AddConnectionRefuse(const string& svError, const uint64_t nOriginID);
void DeleteConnectionRefuse(const uint64_t nOriginID);

View File

@ -96,6 +96,15 @@ void CServerListManager::LaunchServer(void) const
//-----------------------------------------------------------------------------
void CServerListManager::ConnectToServer(const string& svIp, const string& svPort, const string& svNetKey) const
{
if (!ThreadInMainThread())
{
g_TaskScheduler->Dispatch([this, svIp, svPort, svNetKey]()
{
this->ConnectToServer(svIp, svPort, svNetKey);
}, 0);
return;
}
if (!svNetKey.empty())
{
NET_SetKey(svNetKey);
@ -110,6 +119,15 @@ void CServerListManager::ConnectToServer(const string& svIp, const string& svPor
//-----------------------------------------------------------------------------
void CServerListManager::ConnectToServer(const string& svServer, const string& svNetKey) const
{
if (!ThreadInMainThread())
{
g_TaskScheduler->Dispatch([this, svServer, svNetKey]()
{
this->ConnectToServer(svServer, svNetKey);
}, 0);
return;
}
if (!svNetKey.empty())
{
NET_SetKey(svNetKey);

View File

@ -7,50 +7,25 @@
#include <core/stdafx.h>
#include <tier1/cvar.h>
#include <engine/net.h>
#include <engine/host_state.h>
#include <engine/sys_utils.h>
#ifndef CLIENT_DLL
#include <engine/server/server.h>
#endif
#include <squirrel/sqinit.h>
#include <networksystem/pylon.h>
#include <networksystem/listmanager.h>
#include <public/edict.h>
//-----------------------------------------------------------------------------
// Purpose: Send keep alive request to Pylon Master Server.
// NOTE: When Pylon update reaches indev remove this and implement properly.
//-----------------------------------------------------------------------------
void KeepAliveToPylon()
bool KeepAliveToPylon(const NetGameServer_t& netGameServer)
{
#ifndef CLIENT_DLL
if (g_pHostState->m_bActiveGame && sv_pylonVisibility->GetBool()) // Check for active game.
{
std::string m_szHostToken = std::string();
std::string m_szHostRequestMessage = std::string();
string m_szHostToken;
string m_szHostRequestMessage;
bool result = g_pMasterServer->PostServerHost(m_szHostRequestMessage, m_szHostToken,
NetGameServer_t
{
hostname->GetString(),
hostdesc->GetString(),
sv_pylonVisibility->GetInt() == EServerVisibility_t::HIDDEN,
g_pHostState->m_levelName,
mp_gamemode->GetString(),
hostip->GetString(),
hostport->GetString(),
g_svNetKey,
std::to_string(*g_nServerRemoteChecksum),
SDK_VERSION,
std::to_string(g_pServer->GetNumHumanPlayers() + g_pServer->GetNumFakeClients()),
std::to_string(g_ServerGlobalVariables->m_nMaxClients),
std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::system_clock::now().time_since_epoch()
).count()
}
);
bool result = g_pMasterServer->PostServerHost(m_szHostRequestMessage, m_szHostToken, netGameServer);
return result;
}
return false;
#endif // !CLIENT_DLL
}

View File

@ -1,7 +1,7 @@
#pragma once
#include "serverlisting.h"
void KeepAliveToPylon();
bool KeepAliveToPylon(const NetGameServer_t& netGameServer);
class CPylon
{

View File

@ -307,13 +307,17 @@ void Host_Unban_f(const CCommand& args)
{
if (args.HasOnlyDigits(1)) // Check if we have an ip address or origin ID.
{
g_pBanSystem->DeleteEntry("noIP", std::stoll(args.Arg(1))); // Delete ban entry.
g_pBanSystem->Save(); // Save modified vector to file.
if (g_pBanSystem->DeleteEntry("noIP", std::stoll(args.Arg(1)))) // Delete ban entry.
{
g_pBanSystem->Save(); // Save modified vector to file.
}
}
else
{
g_pBanSystem->DeleteEntry(args.Arg(1), 0); // Delete ban entry.
g_pBanSystem->Save(); // Save modified vector to file.
if (g_pBanSystem->DeleteEntry(args.Arg(1), 0)) // Delete ban entry.
{
g_pBanSystem->Save(); // Save modified vector to file.
}
}
}
catch (std::exception& e)