Merge branch 'bulk_check' into indev

This commit is contained in:
Kawe Mazidjatari 2023-04-29 21:01:59 +02:00
commit 55aa254e6b
16 changed files with 630 additions and 600 deletions

View File

@ -250,6 +250,7 @@ void Winsock_Shutdown()
}
void QuerySystemInfo()
{
#ifndef DEDICATED
for (int i = 0; ; i++)
{
DISPLAY_DEVICE dd = { sizeof(dd), {0} };
@ -266,6 +267,7 @@ void QuerySystemInfo()
spdlog::info("{:25s}: '{:s}'\n", "GPU model identifier", szDeviceName);
}
}
#endif // !DEDICATED
const CPUInformation& pi = GetCPUInformation();

View File

@ -10,7 +10,7 @@ void HEbisuSDK_Init()
#ifdef DEDICATED
*g_EbisuSDKInit = true; // <- 1st EbisuSDK
*g_EbisuProfileInit = true; // <- 2nd EbisuSDK
*g_NucleusID = true; // <- 3rd EbisuSDK
*g_NucleusID = 9990000; // <- 3rd EbisuSDK
#endif // DEDICATED
}

View File

@ -268,9 +268,10 @@ void CHostState::Think(void) const
statsTimer.Start();
}
if (banListTimer.GetDurationInProgress().GetSeconds() > sv_banlistRefreshRate->GetDouble())
if (sv_globalBanlist->GetBool() &&
banListTimer.GetDurationInProgress().GetSeconds() > sv_banlistRefreshRate->GetDouble())
{
g_pBanSystem->BanListCheck();
SV_CheckForBan();
banListTimer.Start();
}
#ifdef DEDICATED

View File

@ -80,12 +80,17 @@ int CServer::GetNumClients(void) const
}
//---------------------------------------------------------------------------------
// Purpose: client to server authentication
// Input : *pChallenge -
// Output : true if user isn't banned, false otherwise
// Purpose: Initializes a CSVClient for a new net connection. This will only be called
// once for a player each game, not once for each level change.
// Input : *pServer -
// *pInpacket -
// Output : pointer to client instance on success, nullptr on failure
//---------------------------------------------------------------------------------
bool CServer::AuthClient(user_creds_s* pChallenge)
CClient* CServer::ConnectClient(CServer* pServer, user_creds_s* pChallenge)
{
if (pServer->m_State < server_state_t::ss_active)
return nullptr;
char* pszPersonaName = pChallenge->personaName;
uint64_t nNucleusID = pChallenge->personaId;
@ -99,46 +104,25 @@ bool CServer::AuthClient(user_creds_s* pChallenge)
// Only proceed connection if the client's name is valid and UTF-8 encoded.
if (!VALID_CHARSTAR(pszPersonaName) || !IsValidUTF8(pszPersonaName) || !IsValidPersonaName(pszPersonaName))
{
RejectConnection(m_Socket, &pChallenge->netAdr, "#Valve_Reject_Invalid_Name");
pServer->RejectConnection(pServer->m_Socket, &pChallenge->netAdr, "#Valve_Reject_Invalid_Name");
if (bEnableLogging)
Warning(eDLL_T::SERVER, "Connection rejected for '%s' ('%llu' has an invalid name!)\n", pszAddresBuffer, nNucleusID);
return false;
return nullptr;
}
if (g_pBanSystem->IsBanListValid())
{
if (g_pBanSystem->IsBanned(pszAddresBuffer, nNucleusID))
{
RejectConnection(m_Socket, &pChallenge->netAdr, "#Valve_Reject_Banned");
pServer->RejectConnection(pServer->m_Socket, &pChallenge->netAdr, "#Valve_Reject_Banned");
if (bEnableLogging)
Warning(eDLL_T::SERVER, "Connection rejected for '%s' ('%llu' is banned from this server!)\n", pszAddresBuffer, nNucleusID);
return false;
return nullptr;
}
}
if (sv_globalBanlist->GetBool())
{
std::thread th(SV_IsClientBanned, string(pszAddresBuffer), nNucleusID);
th.detach();
}
return true;
}
//---------------------------------------------------------------------------------
// Purpose: Initializes a CSVClient for a new net connection. This will only be called
// once for a player each game, not once for each level change.
// Input : *pServer -
// *pInpacket -
// Output : pointer to client instance on success, nullptr on failure
//---------------------------------------------------------------------------------
CClient* CServer::ConnectClient(CServer* pServer, user_creds_s* pChallenge)
{
if (pServer->m_State < server_state_t::ss_active || !pServer->AuthClient(pChallenge))
return nullptr;
CClient* pClient = v_CServer_ConnectClient(pServer, pChallenge);
for (auto& callback : !g_pPluginSystem->GetConnectClientCallbacks())
@ -147,6 +131,12 @@ CClient* CServer::ConnectClient(CServer* pServer, user_creds_s* pChallenge)
return nullptr;
}
if (pClient && sv_globalBanlist->GetBool())
{
std::thread th(SV_IsClientBanned, pClient, string(pszAddresBuffer), nNucleusID, string(pszPersonaName));
th.detach();
}
return pClient;
}

View File

@ -42,7 +42,6 @@ public:
bool IsActive(void) const { return m_State >= server_state_t::ss_active; }
bool IsLoading(void) const { return m_State == server_state_t::ss_loading; }
bool IsDedicated(void) const { return m_bIsDedicated; }
bool AuthClient(user_creds_s* pChallenge);
void RejectConnection(int iSocket, netadr_t* pNetAdr, const char* szMessage);
static CClient* ConnectClient(CServer* pServer, user_creds_s* pChallenge);
static void RunFrame(CServer* pServer);

View File

@ -1,7 +1,14 @@
//===========================================================================//
//
// Purpose:
//
//===========================================================================//
#include "core/stdafx.h"
#include "tier0/threadtools.h"
#include "tier0/frametask.h"
#include "tier1/cvar.h"
#include "engine/server/sv_main.h"
#include "engine/client/client.h"
#include "networksystem/pylon.h"
#include "networksystem/bansystem.h"
#include "engine/client/client.h"
@ -11,24 +18,124 @@
//-----------------------------------------------------------------------------
// Purpose: checks if particular client is banned on the comp server
//-----------------------------------------------------------------------------
void SV_IsClientBanned(const string& svIPAddr, const uint64_t nNucleusID)
void SV_IsClientBanned(CClient* pClient, const string& svIPAddr, const uint64_t nNucleusID, const string& svPersonaName)
{
string svError;
Assert(pClient != nullptr);
string svError;
bool bCompBanned = g_pMasterServer->CheckForBan(svIPAddr, nNucleusID, svPersonaName, svError);
bool bCompBanned = g_pMasterServer->CheckForBan(svIPAddr, nNucleusID, svError);
if (bCompBanned)
{
if (!ThreadInMainThread())
{
g_TaskScheduler->Dispatch([svError, nNucleusID]
g_TaskScheduler->Dispatch([pClient, svError, svIPAddr, nNucleusID]
{
g_pBanSystem->AddConnectionRefuse(svError, nNucleusID); // Add to the vector.
// Make sure client isn't already disconnected,
// and that if there is a valid netchannel, that
// it hasn't been taken by a different client by
// the time this task is getting executed.
CNetChan* pChan = pClient->GetNetChan();
if (pChan && pClient->GetNucleusID() == nNucleusID)
{
pClient->Disconnect(Reputation_t::REP_MARK_BAD, svError.c_str());
Warning(eDLL_T::SERVER, "Removed client '%s' ('%llu' is banned globally!)\n",
svIPAddr.c_str(), nNucleusID);
}
}, 0);
}
Warning(eDLL_T::SERVER, "Added '%s' to refused list ('%llu' is banned from the master server!)\n", svIPAddr.c_str(), nNucleusID);
}
}
//-----------------------------------------------------------------------------
// Purpose: checks if particular client is banned on the master server
//-----------------------------------------------------------------------------
void SV_ProcessBulkCheck(const BannedVec_t& bannedVec)
{
BannedVec_t outBannedVec;
g_pMasterServer->GetBannedList(bannedVec, outBannedVec);
if (!ThreadInMainThread())
{
g_TaskScheduler->Dispatch([outBannedVec]
{
SV_CheckForBan(&outBannedVec);
}, 0);
}
}
//-----------------------------------------------------------------------------
// Purpose: creates a snapshot of the currently connected clients
// Input : *pBannedVec - if passed, will check for bans and kick the clients
//-----------------------------------------------------------------------------
void SV_CheckForBan(const BannedVec_t* pBannedVec /*= nullptr*/)
{
Assert(ThreadInMainThread());
BannedVec_t bannedVec;
for (int c = 0; c < MAX_PLAYERS; c++) // Loop through all possible client instances.
{
CClient* pClient = g_pClient->GetClient(c);
if (!pClient)
continue;
CNetChan* pNetChan = pClient->GetNetChan();
if (!pNetChan)
continue;
if (!pClient->IsConnected())
continue;
const char* szIPAddr = pNetChan->GetAddress();
const uint64_t nNucleusID = pClient->GetNucleusID();
if (!pBannedVec)
bannedVec.push_back(std::make_pair(string(szIPAddr), nNucleusID));
else
{
for (auto& it : *pBannedVec)
{
if (it.second == pClient->GetNucleusID())
{
pClient->Disconnect(Reputation_t::REP_MARK_BAD, "%s", it.first.c_str());
Warning(eDLL_T::SERVER, "Removed client '%s' from slot '%i' ('%llu' is banned globally!)\n",
szIPAddr, c, nNucleusID);
}
}
}
}
if (!pBannedVec && !bannedVec.empty())
{
std::thread(&SV_ProcessBulkCheck, bannedVec).detach();
}
}
//-----------------------------------------------------------------------------
// Purpose: loads the game .dll
//-----------------------------------------------------------------------------
void SV_InitGameDLL()
{
v_SV_InitGameDLL();
}
//-----------------------------------------------------------------------------
// Purpose: release resources associated with extension DLLs.
//-----------------------------------------------------------------------------
void SV_ShutdownGameDLL()
{
v_SV_ShutdownGameDLL();
}
//-----------------------------------------------------------------------------
// Purpose: activates the server
// Output : true on success, false on failure
//-----------------------------------------------------------------------------
bool SV_ActivateServer()
{
return v_SV_ActivateServer();
}
void SV_BroadcastVoiceData(CClient* cl, int nBytes, char* data)
{
if (!sv_voiceenable->GetBool())
@ -73,15 +180,18 @@ void SV_BroadcastVoiceData(CClient* cl, int nBytes, char* data)
}
///////////////////////////////////////////////////////////////////////////////
void HSV_Main::Attach(void) const
{
//DetourAttach(&v_SV_InitGameDLL, SV_InitGameDLL);
//DetourAttach(&v_SV_ShutdownGameDLL, SV_ShutdownGameDLL);
//DetourAttach(&v_SV_ActivateServer, SV_ActivateServer);
DetourAttach(&v_SV_BroadcastVoiceData, SV_BroadcastVoiceData);
}
void HSV_Main::Detach(void) const
{
//DetourDetach(&v_SV_InitGameDLL, SV_InitGameDLL);
//DetourDetach(&v_SV_ShutdownGameDLL, SV_ShutdownGameDLL);
//DetourDetach(&v_SV_ActivateServer, SV_ActivateServer);
DetourDetach(&v_SV_BroadcastVoiceData, SV_BroadcastVoiceData);
}
///////////////////////////////////////////////////////////////////////////////

View File

@ -3,18 +3,22 @@
#include "networksystem/bansystem.h"
///////////////////////////////////////////////////////////////////////////////
class CClient;
class CClient;
/* ==== SV_MAIN ======================================================================================================================================================= */
inline CMemory p_SV_InitGameDLL;
inline auto SV_InitGameDLL = p_SV_InitGameDLL.RCast<void(*)(void)>();
inline auto v_SV_InitGameDLL = p_SV_InitGameDLL.RCast<void(*)(void)>();
inline CMemory p_SV_ShutdownGameDLL;
inline auto SV_ShutdownGameDLL = p_SV_ShutdownGameDLL.RCast<void(*)(void)>();
inline auto v_SV_ShutdownGameDLL = p_SV_ShutdownGameDLL.RCast<void(*)(void)>();
inline CMemory p_SV_ActivateServer;
inline auto v_SV_ActivateServer = p_SV_ActivateServer.RCast<bool(*)(void)>();
inline CMemory p_SV_CreateBaseline;
inline auto SV_CreateBaseline = p_SV_CreateBaseline.RCast<bool(*)(void)>();
inline auto v_SV_CreateBaseline = p_SV_CreateBaseline.RCast<bool(*)(void)>();
inline CMemory p_CGameServer__SpawnServer;
inline auto CGameServer__SpawnServer = p_CGameServer__SpawnServer.RCast<bool(*)(void* thisptr, const char* pszMapName, const char* pszMapGroupName)>();
@ -26,7 +30,11 @@ inline bool* s_bIsDedicated = nullptr;
///////////////////////////////////////////////////////////////////////////////
void SV_IsClientBanned(const string& svIPAddr, const uint64_t nNucleusID);
void SV_InitGameDLL();
void SV_ShutdownGameDLL();
bool SV_ActivateServer();
void SV_IsClientBanned(CClient* pClient, const string& svIPAddr, const uint64_t nNucleusID, const string& svPersonaName);
void SV_CheckForBan(const BannedVec_t* pBannedVec = nullptr);
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
@ -37,6 +45,7 @@ class HSV_Main : public IDetour
LogFunAdr("CGameServer::SpawnServer", p_CGameServer__SpawnServer.GetPtr());
LogFunAdr("SV_InitGameDLL", p_SV_InitGameDLL.GetPtr());
LogFunAdr("SV_ShutdownGameDLL", p_SV_ShutdownGameDLL.GetPtr());
LogFunAdr("SV_ActivateServer", p_SV_ActivateServer.GetPtr());
LogFunAdr("SV_CreateBaseline", p_SV_CreateBaseline.GetPtr());
LogVarAdr("s_bIsDedicated", reinterpret_cast<uintptr_t>(s_bIsDedicated));
}
@ -44,6 +53,7 @@ class HSV_Main : public IDetour
{
p_SV_InitGameDLL = g_GameDll.FindPatternSIMD("48 81 EC ?? ?? ?? ?? E8 ?? ?? ?? ?? 80 3D ?? ?? ?? ?? ?? 0F 85 ?? ?? ?? ??");
p_SV_ShutdownGameDLL = g_GameDll.FindPatternSIMD("48 83 EC 28 80 3D ?? ?? ?? ?? ?? 0F 84 ?? ?? ?? ?? 48 8B 0D ?? ?? ?? ?? 48");
p_SV_ActivateServer = g_GameDll.FindPatternSIMD("48 8B C4 56 48 81 EC ?? ?? ?? ?? 48 89 ?? ?? 48 8D");
p_SV_CreateBaseline = g_GameDll.FindPatternSIMD("48 83 EC 28 48 8B 0D ?? ?? ?? ?? 48 85 C9 75 07");
#if defined (GAMEDLL_S0) || defined (GAMEDLL_S1)
p_CGameServer__SpawnServer = g_GameDll.FindPatternSIMD("40 53 55 56 57 41 55 41 56 41 57 48 81 EC ?? ?? ?? ??");
@ -53,11 +63,13 @@ class HSV_Main : public IDetour
#endif
p_SV_BroadcastVoiceData = g_GameDll.FindPatternSIMD("4C 8B DC 56 48 81 EC ? ? ? ? 80 3D ? ? ? ? ? ");
SV_InitGameDLL = p_SV_InitGameDLL.RCast<void(*)(void)>();
SV_ShutdownGameDLL = p_SV_ShutdownGameDLL.RCast<void(*)(void)>();
SV_CreateBaseline = p_SV_CreateBaseline.RCast<bool(*)(void)>();
CGameServer__SpawnServer = p_CGameServer__SpawnServer.RCast<bool(*)(void*, const char*, const char*)>();
v_SV_InitGameDLL = p_SV_InitGameDLL.RCast<void(*)(void)>();
v_SV_ShutdownGameDLL = p_SV_ShutdownGameDLL.RCast<void(*)(void)>();
v_SV_ActivateServer = p_SV_ActivateServer.RCast<bool(*)(void)>();
v_SV_CreateBaseline = p_SV_CreateBaseline.RCast<bool(*)(void)>();
v_SV_BroadcastVoiceData = p_SV_BroadcastVoiceData.RCast<void(__fastcall*)(CClient* cl, int nBytes, char* data)>();
CGameServer__SpawnServer = p_CGameServer__SpawnServer.RCast<bool(*)(void*, const char*, const char*)>();
}
virtual void GetVar(void) const
{

View File

@ -98,25 +98,27 @@ void CBanSystem::Save(void) const
//-----------------------------------------------------------------------------
// Purpose: adds a banned player entry to the banned list
// Input : &svIpAddress -
// nNucleusID -
// Input : *ipAddress -
// nucleusId -
//-----------------------------------------------------------------------------
bool CBanSystem::AddEntry(const string& svIpAddress, const uint64_t nNucleusID)
bool CBanSystem::AddEntry(const char* ipAddress, const uint64_t nucleusId)
{
Assert(!svIpAddress.empty());
Assert(VALID_CHARSTAR(ipAddress));
const auto idPair = std::make_pair(string(ipAddress), nucleusId);
if (IsBanListValid())
{
auto it = std::find(m_vBanList.begin(), m_vBanList.end(), std::make_pair(svIpAddress, nNucleusID));
auto it = std::find(m_vBanList.begin(), m_vBanList.end(), idPair);
if (it == m_vBanList.end())
{
m_vBanList.push_back(std::make_pair(svIpAddress, nNucleusID));
m_vBanList.push_back(idPair);
return true;
}
}
else
{
m_vBanList.push_back(std::make_pair(svIpAddress, nNucleusID));
m_vBanList.push_back(idPair);
return true;
}
@ -125,24 +127,25 @@ bool CBanSystem::AddEntry(const string& svIpAddress, const uint64_t nNucleusID)
//-----------------------------------------------------------------------------
// Purpose: deletes an entry in the banned list
// Input : &svIpAddress -
// nNucleusID -
// Input : *ipAddress -
// nucleusId -
//-----------------------------------------------------------------------------
bool CBanSystem::DeleteEntry(const string& svIpAddress, const uint64_t nNucleusID)
bool CBanSystem::DeleteEntry(const char* ipAddress, const uint64_t nucleusId)
{
Assert(!svIpAddress.empty());
Assert(VALID_CHARSTAR(ipAddress));
if (IsBanListValid())
{
auto it = std::find_if(m_vBanList.begin(), m_vBanList.end(),
[&](const pair<const string, const uint64_t>& element)
{ return (svIpAddress.compare(element.first) == NULL || element.second == nNucleusID); });
{
return (strcmp(ipAddress, element.first.c_str()) == NULL
|| element.second == nucleusId);
});
if (it != m_vBanList.end())
{
DeleteConnectionRefuse(it->second);
m_vBanList.erase(it);
return true;
}
}
@ -150,109 +153,27 @@ bool CBanSystem::DeleteEntry(const string& svIpAddress, const uint64_t nNucleusI
return false;
}
//-----------------------------------------------------------------------------
// Purpose: adds a connect refuse entry to the refused list
// Input : &svError -
// nNucleusID -
//-----------------------------------------------------------------------------
bool CBanSystem::AddConnectionRefuse(const string& svError, const uint64_t nNucleusID)
{
if (IsRefuseListValid())
{
auto it = std::find_if(m_vRefuseList.begin(), m_vRefuseList.end(),
[&](const pair<const string, const uint64_t>& element) { return element.second == nNucleusID; });
if (it == m_vRefuseList.end())
{
m_vRefuseList.push_back(std::make_pair(svError, nNucleusID));
return true;
}
}
else
{
m_vRefuseList.push_back(std::make_pair(svError, nNucleusID));
return true;
}
return false;
}
//-----------------------------------------------------------------------------
// Purpose: deletes an entry in the refused list
// Input : nNucleusID -
//-----------------------------------------------------------------------------
bool CBanSystem::DeleteConnectionRefuse(const uint64_t nNucleusID)
{
if (IsRefuseListValid())
{
auto it = std::find_if(m_vRefuseList.begin(), m_vRefuseList.end(),
[&](const pair<const string, const uint64_t>& element) { return element.second == nNucleusID; });
if (it != m_vRefuseList.end())
{
m_vRefuseList.erase(it);
return true;
}
}
return false;
}
//-----------------------------------------------------------------------------
// Purpose: Check refuse list and kill netchan connection.
//-----------------------------------------------------------------------------
void CBanSystem::BanListCheck(void)
{
if (!IsRefuseListValid())
return;
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(c);
if (!pClient)
continue;
CNetChan* pNetChan = pClient->GetNetChan();
if (!pNetChan)
continue;
if (!pClient->IsConnected())
continue;
if (pClient->GetNucleusID() != m_vRefuseList[i].second)
continue;
string svIpAddress = pNetChan->GetAddress();
Warning(eDLL_T::SERVER, "Removing client '%s' from slot '%i' ('%llu' is banned from this server!)\n", svIpAddress.c_str(), c, pClient->GetNucleusID());
pClient->Disconnect(Reputation_t::REP_MARK_BAD, "%s", m_vRefuseList[i].first.c_str());
}
}
}
//-----------------------------------------------------------------------------
// Purpose: checks if specified ip address or nucleus id is banned
// Input : &svIpAddress -
// nNucleusID -
// Input : *ipAddress -
// nucleusId -
// Output : true if banned, false if not banned
//-----------------------------------------------------------------------------
bool CBanSystem::IsBanned(const string& svIpAddress, const uint64_t nNucleusID) const
bool CBanSystem::IsBanned(const char* ipAddress, const uint64_t nucleusId) const
{
for (size_t i = 0; i < m_vBanList.size(); i++)
{
const string& ipAddress = m_vBanList[i].first;
const uint64_t nucleusID = m_vBanList[i].second;
const string& bannedIpAddress = m_vBanList[i].first;
const uint64_t bannedNucleusID = m_vBanList[i].second;
if (ipAddress.empty() ||
!nucleusID) // Cannot be null.
if (bannedIpAddress.empty()
|| !bannedNucleusID) // Cannot be null.
{
continue;
}
if (ipAddress.compare(svIpAddress) == NULL ||
nNucleusID == nucleusID)
if (bannedIpAddress.compare(ipAddress) == NULL
|| nucleusId == bannedNucleusID)
{
return true;
}
@ -261,14 +182,6 @@ bool CBanSystem::IsBanned(const string& svIpAddress, const uint64_t nNucleusID)
return false;
}
//-----------------------------------------------------------------------------
// Purpose: checks if refused list is valid
//-----------------------------------------------------------------------------
bool CBanSystem::IsRefuseListValid(void) const
{
return !m_vRefuseList.empty();
}
//-----------------------------------------------------------------------------
// Purpose: checks if banned list is valid
//-----------------------------------------------------------------------------
@ -279,71 +192,75 @@ bool CBanSystem::IsBanListValid(void) const
//-----------------------------------------------------------------------------
// Purpose: kicks a player by given name
// Input : &svPlayerName -
// Input : *playerName -
// *reason -
//-----------------------------------------------------------------------------
void CBanSystem::KickPlayerByName(const string& svPlayerName)
void CBanSystem::KickPlayerByName(const char* playerName, const char* reason)
{
if (svPlayerName.empty())
if (!VALID_CHARSTAR(playerName))
return;
AuthorPlayerByName(svPlayerName, false);
AuthorPlayerByName(playerName, false);
}
//-----------------------------------------------------------------------------
// Purpose: kicks a player by given handle or id
// Input : &svHandle -
// Input : *playerHandle -
// *reason -
//-----------------------------------------------------------------------------
void CBanSystem::KickPlayerById(const string& svHandle)
void CBanSystem::KickPlayerById(const char* playerHandle, const char* reason)
{
if (svHandle.empty())
if (!VALID_CHARSTAR(playerHandle))
return;
AuthorPlayerById(svHandle, false);
AuthorPlayerById(playerHandle, false);
}
//-----------------------------------------------------------------------------
// Purpose: bans a player by given name
// Input : &svPlayerName -
// Input : *playerName -
// *reason -
//-----------------------------------------------------------------------------
void CBanSystem::BanPlayerByName(const string& svPlayerName)
void CBanSystem::BanPlayerByName(const char* playerName, const char* reason)
{
if (svPlayerName.empty())
if (!VALID_CHARSTAR(playerName))
return;
AuthorPlayerByName(svPlayerName, true);
AuthorPlayerByName(playerName, true);
}
//-----------------------------------------------------------------------------
// Purpose: bans a player by given handle or id
// Input : &svHandle -
// Input : *playerHandle -
// *reason -
//-----------------------------------------------------------------------------
void CBanSystem::BanPlayerById(const string& svHandle)
void CBanSystem::BanPlayerById(const char* playerHandle, const char* reason)
{
if (svHandle.empty())
if (!VALID_CHARSTAR(playerHandle))
return;
AuthorPlayerById(svHandle, true);
AuthorPlayerById(playerHandle, true);
}
//-----------------------------------------------------------------------------
// Purpose: unbans a player by given nucleus id or ip address
// Input : &svCriteria -
// Input : *criteria -
//-----------------------------------------------------------------------------
void CBanSystem::UnbanPlayer(const string& svCriteria)
void CBanSystem::UnbanPlayer(const char* criteria)
{
try
{
bool bSave = false;
if (StringIsDigit(svCriteria)) // Check if we have an ip address or nucleus id.
if (StringIsDigit(criteria)) // Check if we have an ip address or nucleus id.
{
if (DeleteEntry("<<invalid>>", std::stoll(svCriteria))) // Delete ban entry.
if (DeleteEntry("<<invalid>>", std::stoll(criteria))) // Delete ban entry.
{
bSave = true;
}
}
else
{
if (DeleteEntry(svCriteria, 0)) // Delete ban entry.
if (DeleteEntry(criteria, 0)) // Delete ban entry.
{
bSave = true;
}
@ -352,7 +269,7 @@ void CBanSystem::UnbanPlayer(const string& svCriteria)
if (bSave)
{
Save(); // Save modified vector to file.
DevMsg(eDLL_T::SERVER, "Removed '%s' from banned list\n", svCriteria.c_str());
DevMsg(eDLL_T::SERVER, "Removed '%s' from banned list\n", criteria);
}
}
catch (const std::exception& e)
@ -364,15 +281,19 @@ void CBanSystem::UnbanPlayer(const string& svCriteria)
//-----------------------------------------------------------------------------
// Purpose: authors player by given name
// Input : &svPlayerName -
// bBan - (only kicks if false)
// Input : *playerName -
// shouldBan - (only kicks if false)
// *reason -
//-----------------------------------------------------------------------------
void CBanSystem::AuthorPlayerByName(const string& svPlayerName, const bool bBan)
void CBanSystem::AuthorPlayerByName(const char* playerName, const bool shouldBan, const char* reason)
{
Assert(!svPlayerName.empty());
Assert(VALID_CHARSTAR(playerName));
bool bDisconnect = false;
bool bSave = false;
if (!reason)
reason = shouldBan ? "Banned from server" : "Kicked from server";
for (int i = 0; i < MAX_PLAYERS; i++)
{
CClient* pClient = g_pClient->GetClient(i);
@ -385,12 +306,12 @@ void CBanSystem::AuthorPlayerByName(const string& svPlayerName, const bool bBan)
if (strlen(pNetChan->GetName()) > 0)
{
if (svPlayerName.compare(pNetChan->GetName()) == NULL) // Our wanted name?
if (strcmp(playerName, pNetChan->GetName()) == NULL) // Our wanted name?
{
if (bBan && AddEntry(pNetChan->GetAddress(), pClient->GetNucleusID()) && !bSave)
if (shouldBan && AddEntry(pNetChan->GetAddress(), pClient->GetNucleusID()) && !bSave)
bSave = true;
pClient->Disconnect(REP_MARK_BAD, bBan ? "Banned from server" : "Kicked from server");
pClient->Disconnect(REP_MARK_BAD, reason);
bDisconnect = true;
}
}
@ -399,29 +320,33 @@ void CBanSystem::AuthorPlayerByName(const string& svPlayerName, const bool bBan)
if (bSave)
{
Save();
DevMsg(eDLL_T::SERVER, "Added '%s' to banned list\n", svPlayerName.c_str());
DevMsg(eDLL_T::SERVER, "Added '%s' to banned list\n", playerName);
}
else if (bDisconnect)
{
DevMsg(eDLL_T::SERVER, "Kicked '%s' from server\n", svPlayerName.c_str());
DevMsg(eDLL_T::SERVER, "Kicked '%s' from server\n", playerName);
}
}
//-----------------------------------------------------------------------------
// Purpose: authors player by given nucleus id or ip address
// Input : &svHandle -
// bBan - (only kicks if false)
// Input : *playerHandle -
// shouldBan - (only kicks if false)
// *reason -
//-----------------------------------------------------------------------------
void CBanSystem::AuthorPlayerById(const string& svHandle, const bool bBan)
void CBanSystem::AuthorPlayerById(const char* playerHandle, const bool shouldBan, const char* reason)
{
Assert(!svHandle.empty());
Assert(VALID_CHARSTAR(playerHandle));
try
{
bool bOnlyDigits = StringIsDigit(svHandle);
bool bOnlyDigits = StringIsDigit(playerHandle);
bool bDisconnect = false;
bool bSave = false;
if (!reason)
reason = shouldBan ? "Banned from server" : "Kicked from server";
for (int i = 0; i < MAX_PLAYERS; i++)
{
CClient* pClient = g_pClient->GetClient(i);
@ -434,7 +359,7 @@ void CBanSystem::AuthorPlayerById(const string& svHandle, const bool bBan)
if (bOnlyDigits)
{
uint64_t nTargetID = static_cast<uint64_t>(std::stoll(svHandle));
uint64_t nTargetID = static_cast<uint64_t>(std::stoll(playerHandle));
if (nTargetID > static_cast<uint64_t>(MAX_PLAYERS)) // Is it a possible nucleusID?
{
uint64_t nNucleusID = pClient->GetNucleusID();
@ -448,21 +373,21 @@ void CBanSystem::AuthorPlayerById(const string& svHandle, const bool bBan)
continue;
}
if (bBan && AddEntry(pNetChan->GetAddress(), pClient->GetNucleusID()) && !bSave)
if (shouldBan && AddEntry(pNetChan->GetAddress(), pClient->GetNucleusID()) && !bSave)
bSave = true;
pClient->Disconnect(REP_MARK_BAD, bBan ? "Banned from server" : "Kicked from server");
pClient->Disconnect(REP_MARK_BAD, reason);
bDisconnect = true;
}
else
{
if (svHandle.compare(pNetChan->GetAddress()) != NULL)
if (strcmp(playerHandle, pNetChan->GetAddress()) != NULL)
continue;
if (bBan && AddEntry(pNetChan->GetAddress(), pClient->GetNucleusID()) && !bSave)
if (shouldBan && AddEntry(pNetChan->GetAddress(), pClient->GetNucleusID()) && !bSave)
bSave = true;
pClient->Disconnect(REP_MARK_BAD, bBan ? "Banned from server" : "Kicked from server");
pClient->Disconnect(REP_MARK_BAD, reason);
bDisconnect = true;
}
}
@ -470,11 +395,11 @@ void CBanSystem::AuthorPlayerById(const string& svHandle, const bool bBan)
if (bSave)
{
Save();
DevMsg(eDLL_T::SERVER, "Added '%s' to banned list\n", svHandle.c_str());
DevMsg(eDLL_T::SERVER, "Added '%s' to banned list\n", playerHandle);
}
else if (bDisconnect)
{
DevMsg(eDLL_T::SERVER, "Kicked '%s' from server\n", svHandle.c_str());
DevMsg(eDLL_T::SERVER, "Kicked '%s' from server\n", playerHandle);
}
}
catch (const std::exception& e)

View File

@ -1,37 +1,32 @@
#pragma once
typedef vector<std::pair<string, uint64_t>> BannedVec_t;
class CBanSystem
{
public:
void Load(void);
void Save(void) const;
bool AddEntry(const string& svIpAddress, const uint64_t nNucleusID);
bool DeleteEntry(const string& svIpAddress, const uint64_t nNucleusID);
bool AddEntry(const char* ipAddress, const uint64_t nucleusId);
bool DeleteEntry(const char* ipAddress, const uint64_t nucleusId);
bool AddConnectionRefuse(const string& svError, const uint64_t nNucleusID);
bool DeleteConnectionRefuse(const uint64_t nNucleusID);
void BanListCheck(void);
bool IsBanned(const string& svIpAddress, const uint64_t nNucleusID) const;
bool IsRefuseListValid(void) const;
bool IsBanned(const char* ipAddress, const uint64_t nucleusId) const;
bool IsBanListValid(void) const;
void KickPlayerByName(const string& svPlayerName);
void KickPlayerById(const string& svHandle);
void KickPlayerByName(const char* playerName, const char* reason = nullptr);
void KickPlayerById(const char* playerHandle, const char* reason = nullptr);
void BanPlayerByName(const string& svPlayerName);
void BanPlayerById(const string& svHandle);
void BanPlayerByName(const char* playerName, const char* reason = nullptr);
void BanPlayerById(const char* playerHandle, const char* reason = nullptr);
void UnbanPlayer(const string& svCriteria);
void UnbanPlayer(const char* criteria);
private:
void AuthorPlayerByName(const string& svPlayerName, const bool bBan);
void AuthorPlayerById(const string& svHandle, const bool bBan);
void AuthorPlayerByName(const char* playerName, const bool bBan, const char* reason = nullptr);
void AuthorPlayerById(const char* playerHandle, const bool bBan, const char* reason = nullptr);
vector<std::pair<string, uint64_t>> m_vRefuseList;
vector<std::pair<string, uint64_t>> m_vBanList;
BannedVec_t m_vBanList;
};
extern CBanSystem* g_pBanSystem;

View File

@ -14,222 +14,122 @@
#endif // !CLIENT_DLL
//-----------------------------------------------------------------------------
// Purpose: returns a vector of hosted servers.
// Input : &svOutMessage -
// Purpose: gets a vector of hosted servers.
// Input : &outMessage -
// Output : vector<NetGameServer_t>
//-----------------------------------------------------------------------------
vector<NetGameServer_t> CPylon::GetServerList(string& svOutMessage) const
vector<NetGameServer_t> CPylon::GetServerList(string& outMessage) const
{
vector<NetGameServer_t> vslList;
vector<NetGameServer_t> vecServers;
nlohmann::json jsRequestBody = nlohmann::json::object();
jsRequestBody["version"] = SDK_VERSION;
nlohmann::json requestJson = nlohmann::json::object();
requestJson["version"] = SDK_VERSION;
string svRequestBody = jsRequestBody.dump(4);
string svResponse;
nlohmann::json responseJson;
CURLINFO status;
if (pylon_showdebuginfo->GetBool())
if (!SendRequest("/servers", requestJson, responseJson,
outMessage, status, "server list error"))
{
DevMsg(eDLL_T::ENGINE, "%s - Sending server list request to comp-server:\n%s\n", __FUNCTION__, svRequestBody.c_str());
return vecServers;
}
CURLINFO status;
if (!QueryMasterServer(pylon_matchmaking_hostname->GetString(), "/servers", svRequestBody, svResponse, svOutMessage, status))
if (!responseJson.contains("servers"))
{
return vslList;
outMessage = Format("Invalid response with status: %d", int(status));
return vecServers;
}
try
{
if (status == 200) // STATUS_OK
for (auto& obj : responseJson["servers"])
{
nlohmann::json jsResultBody = nlohmann::json::parse(svResponse);
if (jsResultBody["success"].is_boolean() && jsResultBody["success"].get<bool>())
{
for (auto& obj : jsResultBody["servers"])
vecServers.push_back(
NetGameServer_t
{
vslList.push_back(
NetGameServer_t
{
obj.value("name",""),
obj.value("description",""),
obj.value("hidden","false") == "true",
obj.value("map",""),
obj.value("playlist",""),
obj.value("ip",""),
obj.value("port", ""),
obj.value("key",""),
obj.value("checksum",""),
obj.value("version", SDK_VERSION),
obj.value("playerCount", ""),
obj.value("maxPlayers", ""),
obj.value("timeStamp", 0),
obj.value("publicRef", ""),
obj.value("cachedId", ""),
}
);
obj.value("name",""),
obj.value("description",""),
obj.value("hidden","false") == "true",
obj.value("map",""),
obj.value("playlist",""),
obj.value("ip",""),
obj.value("port", ""),
obj.value("key",""),
obj.value("checksum",""),
obj.value("version", SDK_VERSION),
obj.value("playerCount", ""),
obj.value("maxPlayers", ""),
obj.value("timeStamp", 0),
obj.value("publicRef", ""),
obj.value("cachedId", ""),
}
}
else
{
if (jsResultBody["error"].is_string())
{
svOutMessage = jsResultBody["error"].get<string>();
}
else
{
svOutMessage = Format("Unknown error with status: %d", static_cast<int>(status));
}
}
}
else
{
if (status)
{
if (!svResponse.empty())
{
nlohmann::json jsResultBody = nlohmann::json::parse(svResponse);
if (jsResultBody["error"].is_string())
{
svOutMessage = jsResultBody["error"].get<string>();
}
else
{
svOutMessage = Format("Server list error: %d", static_cast<int>(status));
}
return vslList;
}
svOutMessage = Format("Failed comp-server query: %d", static_cast<int>(status));
return vslList;
}
svOutMessage = Format("Failed to reach comp-server: %s", "connection timed-out");
return vslList;
);
}
}
catch (const std::exception& ex)
{
Warning(eDLL_T::ENGINE, "%s - Exception while parsing comp-server response:\n%s\n", __FUNCTION__, ex.what());
Warning(eDLL_T::ENGINE, "%s - %s\n", __FUNCTION__, ex.what());
vecServers.clear(); // Clear as the vector may be partially filled.
}
return vslList;
return vecServers;
}
//-----------------------------------------------------------------------------
// Purpose: Gets the server by token string.
// Input : &slOutServer -
// &svOutMessage -
// &svToken -
// Input : &outGameServer -
// &outMessage -
// &token -
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CPylon::GetServerByToken(NetGameServer_t& slOutServer, string& svOutMessage, const string& svToken) const
bool CPylon::GetServerByToken(NetGameServer_t& outGameServer,
string& outMessage, const string& token) const
{
nlohmann::json jsRequestBody = nlohmann::json::object();
jsRequestBody["token"] = svToken;
string svRequestBody = jsRequestBody.dump(4);
string svResponseBuf;
const bool bDebugLog = pylon_showdebuginfo->GetBool();
if (bDebugLog)
{
DevMsg(eDLL_T::ENGINE, "%s - Sending token connect request to comp-server:\n%s\n", __FUNCTION__, svRequestBody.c_str());
}
nlohmann::json requestJson = nlohmann::json::object();
requestJson["token"] = token;
nlohmann::json responseJson;
CURLINFO status;
if (!QueryMasterServer(pylon_matchmaking_hostname->GetString(), "/server/byToken", svRequestBody, svResponseBuf, svOutMessage, status))
if (!SendRequest("/server/byToken", requestJson, responseJson,
outMessage, status, "server not found"))
{
return false;
}
if (bDebugLog)
if (!responseJson.contains("server"))
{
DevMsg(eDLL_T::ENGINE, "%s - Comp-server replied with status: '%d'\n", __FUNCTION__, status);
outMessage = Format("Invalid response with status: %d", int(status));
return false;
}
try
{
if (status == 200) // STATUS_OK
nlohmann::json& serverJson = responseJson["server"];
outGameServer = NetGameServer_t
{
nlohmann::json jsResultBody = nlohmann::json::parse(svResponseBuf);
serverJson.value("name",""),
serverJson.value("description",""),
serverJson.value("hidden","false") == "true",
serverJson.value("map",""),
serverJson.value("playlist",""),
serverJson.value("ip",""),
serverJson.value("port", ""),
serverJson.value("key",""),
serverJson.value("checksum",""),
serverJson.value("version", SDK_VERSION),
serverJson.value("playerCount", ""),
serverJson.value("maxPlayers", ""),
serverJson.value("timeStamp", 0),
serverJson.value("publicRef", ""),
serverJson.value("cachedId", ""),
};
if (bDebugLog)
{
string svResultBody = jsResultBody.dump(4);
DevMsg(eDLL_T::ENGINE, "%s - Comp-server response body:\n%s\n", __FUNCTION__, svResultBody.c_str());
}
if (jsResultBody["success"].is_boolean() && jsResultBody["success"])
{
slOutServer = NetGameServer_t
{
jsResultBody["server"].value("name",""),
jsResultBody["server"].value("description",""),
jsResultBody["server"].value("hidden","false") == "true",
jsResultBody["server"].value("map",""),
jsResultBody["server"].value("playlist",""),
jsResultBody["server"].value("ip",""),
jsResultBody["server"].value("port", ""),
jsResultBody["server"].value("key",""),
jsResultBody["server"].value("checksum",""),
jsResultBody["server"].value("version", SDK_VERSION),
jsResultBody["server"].value("playerCount", ""),
jsResultBody["server"].value("maxPlayers", ""),
jsResultBody["server"].value("timeStamp", 0),
jsResultBody["server"].value("publicRef", ""),
jsResultBody["server"].value("cachedId", ""),
};
return true;
}
else
{
if (jsResultBody["error"].is_string())
{
svOutMessage = jsResultBody["error"].get<string>();
}
else
{
svOutMessage = Format("Unknown error with status: %d", static_cast<int>(status));
}
return false;
}
}
else
{
if (status)
{
if (!svResponseBuf.empty())
{
nlohmann::json jsResultBody = nlohmann::json::parse(svResponseBuf);
if (jsResultBody["error"].is_string())
{
svOutMessage = jsResultBody["error"].get<string>();
}
else
{
svOutMessage = Format("Server not found: %d", static_cast<int>(status));
}
return false;
}
svOutMessage = Format("Failed comp-server query: %d", static_cast<int>(status));
return false;
}
svOutMessage = Format("Failed to reach comp-server: %s", "connection timed-out");
return false;
}
return true;
}
catch (const std::exception& ex)
{
Warning(eDLL_T::ENGINE, "%s - Exception while parsing comp-server response:\n%s\n", __FUNCTION__, ex.what());
Warning(eDLL_T::ENGINE, "%s - %s\n", __FUNCTION__, ex.what());
}
return false;
@ -237,126 +137,56 @@ bool CPylon::GetServerByToken(NetGameServer_t& slOutServer, string& svOutMessage
//-----------------------------------------------------------------------------
// Purpose: Sends host server POST request.
// Input : &svOutMessage -
// &svOutToken -
// Input : &outMessage -
// &outToken -
// &netGameServer -
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CPylon::PostServerHost(string& svOutMessage, string& svOutToken, const NetGameServer_t& netGameServer) const
bool CPylon::PostServerHost(string& outMessage, string& outToken,
const NetGameServer_t& netGameServer) const
{
nlohmann::json jsRequestBody = nlohmann::json::object();
jsRequestBody["name"] = netGameServer.m_svHostName;
jsRequestBody["description"] = netGameServer.m_svDescription;
jsRequestBody["hidden"] = netGameServer.m_bHidden;
jsRequestBody["map"] = netGameServer.m_svHostMap;
jsRequestBody["playlist"] = netGameServer.m_svPlaylist;
jsRequestBody["ip"] = netGameServer.m_svIpAddress;
jsRequestBody["port"] = netGameServer.m_svGamePort;
jsRequestBody["key"] = netGameServer.m_svEncryptionKey;
jsRequestBody["checksum"] = netGameServer.m_svRemoteChecksum;
jsRequestBody["version"] = netGameServer.m_svSDKVersion;
jsRequestBody["playerCount"] = netGameServer.m_svPlayerCount;
jsRequestBody["maxPlayers"] = netGameServer.m_svMaxPlayers;
jsRequestBody["timeStamp"] = netGameServer.m_nTimeStamp;
jsRequestBody["publicRef"] = netGameServer.m_svPublicRef;
jsRequestBody["cachedId"] = netGameServer.m_svCachedId;
string svRequestBody = jsRequestBody.dump(4);
string svResponseBuf;
const bool bDebugLog = pylon_showdebuginfo->GetBool();
if (bDebugLog)
{
DevMsg(eDLL_T::ENGINE, "%s - Sending post host request to comp-server:\n%s\n", __FUNCTION__, svRequestBody.c_str());
}
nlohmann::json requestJson = nlohmann::json::object();
requestJson["name"] = netGameServer.m_svHostName;
requestJson["description"] = netGameServer.m_svDescription;
requestJson["hidden"] = netGameServer.m_bHidden;
requestJson["map"] = netGameServer.m_svHostMap;
requestJson["playlist"] = netGameServer.m_svPlaylist;
requestJson["ip"] = netGameServer.m_svIpAddress;
requestJson["port"] = netGameServer.m_svGamePort;
requestJson["key"] = netGameServer.m_svEncryptionKey;
requestJson["checksum"] = netGameServer.m_svRemoteChecksum;
requestJson["version"] = netGameServer.m_svSDKVersion;
requestJson["playerCount"] = netGameServer.m_svPlayerCount;
requestJson["maxPlayers"] = netGameServer.m_svMaxPlayers;
requestJson["timeStamp"] = netGameServer.m_nTimeStamp;
requestJson["publicRef"] = netGameServer.m_svPublicRef;
requestJson["cachedId"] = netGameServer.m_svCachedId;
nlohmann::json responseJson;
CURLINFO status;
if (!QueryMasterServer(pylon_matchmaking_hostname->GetString(), "/servers/add", svRequestBody, svResponseBuf, svOutMessage, status))
if (!SendRequest("/servers/add", requestJson, responseJson,
outMessage, status, "server host error"))
{
return false;
}
if (bDebugLog)
if (netGameServer.m_bHidden)
{
DevMsg(eDLL_T::ENGINE, "%s - Comp-server replied with status: '%d'\n", __FUNCTION__, status);
}
nlohmann::json& tokenJson = responseJson["token"];
try
{
if (status == 200) // STATUS_OK
if (!tokenJson.is_string())
{
nlohmann::json jsResultBody = nlohmann::json::parse(svResponseBuf);
outMessage = Format("Invalid response with status: %d", int(status));
outToken.clear();
if (bDebugLog)
{
string svResultBody = jsResultBody.dump(4);
DevMsg(eDLL_T::ENGINE, "%s - Comp-server response body:\n%s\n", __FUNCTION__, svResultBody.c_str());
}
if (jsResultBody["success"].is_boolean() && jsResultBody["success"].get<bool>())
{
if (jsResultBody["token"].is_string())
{
svOutToken = jsResultBody["token"].get<string>();
}
else
{
svOutMessage = Format("Invalid response with status: %d", static_cast<int>(status));
svOutToken.clear();
}
return true;
}
else
{
if (jsResultBody["error"].is_string())
{
svOutMessage = jsResultBody["error"].get<string>();
}
else
{
svOutMessage = Format("Unknown error with status: %d", static_cast<int>(status));
}
return false;
}
}
else
{
if (status)
{
if (!svResponseBuf.empty())
{
nlohmann::json jsResultBody = nlohmann::json::parse(svResponseBuf);
if (jsResultBody["error"].is_string())
{
svOutMessage = jsResultBody["error"].get<string>();
}
else
{
svOutMessage = Format("Server host error: %d", static_cast<int>(status));
}
svOutToken.clear();
return false;
}
svOutMessage = Format("Failed comp-server query: %d", static_cast<int>(status));
svOutToken.clear();
return false;
}
svOutMessage = Format("Failed to reach comp-server: %s", "connection timed-out");
svOutToken.clear();
return false;
}
}
catch (const std::exception& ex)
{
Warning(eDLL_T::ENGINE, "%s - Exception while parsing comp-server response:\n%s\n", __FUNCTION__, ex.what());
outToken = tokenJson.get<string>();
}
return false;
return true;
}
#ifdef DEDICATED
@ -372,26 +202,26 @@ bool CPylon::KeepAlive(const NetGameServer_t& netGameServer)
return false;
}
string svHostToken;
string svErrorMsg;
string errorMsg;
string hostToken;
bool result = PostServerHost(svErrorMsg, svHostToken, netGameServer);
const bool result = PostServerHost(errorMsg, hostToken, netGameServer);
if (!result)
{
if (!svErrorMsg.empty() && m_ErrorMsg.compare(svErrorMsg) != NULL)
if (!errorMsg.empty() && m_ErrorMsg.compare(errorMsg) != NULL)
{
m_ErrorMsg = svErrorMsg;
Error(eDLL_T::SERVER, NO_ERROR, "%s\n", svErrorMsg.c_str());
m_ErrorMsg = errorMsg;
Error(eDLL_T::SERVER, NO_ERROR, "%s\n", errorMsg.c_str());
}
}
else // Attempt to log the token, if there is one.
{
if (!svHostToken.empty() && m_Token.compare(svHostToken) != NULL)
if (!hostToken.empty() && m_Token.compare(hostToken) != NULL)
{
m_Token = svHostToken;
m_Token = hostToken;
DevMsg(eDLL_T::SERVER, "Published server with token: %s'%s%s%s'\n",
g_svReset.c_str(), g_svGreyB.c_str(),
svHostToken.c_str(), g_svReset.c_str());
hostToken.c_str(), g_svReset.c_str());
}
}
@ -400,105 +230,267 @@ bool CPylon::KeepAlive(const NetGameServer_t& netGameServer)
#endif // DEDICATED
//-----------------------------------------------------------------------------
// Purpose: Checks if client is banned on the comp server.
// Input : &svIpAddress -
// nNucleusID -
// &svOutReason -
// Output : Returns true if banned, false if not banned.
// Purpose: Checks a list of clients for their banned status.
// Input : &inBannedVec -
// &outBannedVec -
// Output : True on success, false otherwise.
//-----------------------------------------------------------------------------
bool CPylon::CheckForBan(const string& svIpAddress, const uint64_t nNucleusID, string& svOutReason) const
bool CPylon::GetBannedList(const BannedVec_t& inBannedVec, BannedVec_t& outBannedVec) const
{
nlohmann::json jsRequestBody = nlohmann::json::object();
jsRequestBody["id"] = nNucleusID;
jsRequestBody["ip"] = svIpAddress;
nlohmann::json arrayJson = nlohmann::json::array();
string svRequestBody = jsRequestBody.dump(4);
string svResponseBuf;
string svOutMessage;
CURLINFO status;
const bool bDebugLog = pylon_showdebuginfo->GetBool();
if (bDebugLog)
for (const auto& bannedPair : inBannedVec)
{
DevMsg(eDLL_T::ENGINE, "%s - Sending ban check request to comp-server:\n%s\n", __FUNCTION__, svRequestBody.c_str());
nlohmann::json player;
player["id"] = bannedPair.second;
player["ip"] = bannedPair.first;
arrayJson.push_back(player);
}
if (!QueryMasterServer(pylon_matchmaking_hostname->GetString(), "/banlist/isBanned", svRequestBody, svResponseBuf, svOutMessage, status))
nlohmann::json playerArray;
playerArray["players"] = arrayJson;
string outMessage;
CURLINFO status;
if (!SendRequest("/banlist/bulkCheck", playerArray,
arrayJson, outMessage, status, "banned bulk check error"))
{
return false;
}
if (bDebugLog)
if (!arrayJson.contains("bannedPlayers"))
{
DevMsg(eDLL_T::ENGINE, "%s - Comp-server replied with status: '%d'\n", __FUNCTION__, status);
}
if (status != 200)
{
Error(eDLL_T::ENGINE, NO_ERROR, "%s - Failed to query comp-server: status code = %d\n", __FUNCTION__, status);
outMessage = Format("Invalid response with status: %d", int(status));
return false;
}
try
{
nlohmann::json jsResultBody = nlohmann::json::parse(svResponseBuf);
if (bDebugLog)
for (auto& obj : arrayJson["bannedPlayers"])
{
string svResultBody = jsResultBody.dump(4);
DevMsg(eDLL_T::ENGINE, "%s - Comp-server response body:\n%s\n", __FUNCTION__, svResultBody.c_str());
outBannedVec.push_back(
std::make_pair(
obj.value("reason", "#DISCONNECT_BANNED"),
obj.value("id", uint64_t(0))
)
);
}
if (jsResultBody["success"].is_boolean() && jsResultBody["success"].get<bool>())
{
if (jsResultBody["banned"].is_boolean() && jsResultBody["banned"].get<bool>())
{
svOutReason = jsResultBody.value("reason", "#DISCONNECT_BANNED");
return true;
}
}
return true;
}
catch (const std::exception& ex)
{
Warning(eDLL_T::ENGINE, "%s - Exception while parsing comp-server response:\n%s\n", __FUNCTION__, ex.what());
Warning(eDLL_T::ENGINE, "%s - %s\n", __FUNCTION__, ex.what());
}
return false;
}
//-----------------------------------------------------------------------------
// Purpose: Sends query to master server.
// Input : &svHostName -
// &svApi -
// &svInRequest -
// &svResponse -
// &svOutMessage -
// &outStatus -
// Output : Returns true if successful, false otherwise.
// Purpose: Checks if client is banned on the comp server.
// Input : &ipAddress -
// nucleusId -
// &outReason - <- contains banned reason if any.
// Output : True if banned, false if not banned.
//-----------------------------------------------------------------------------
bool CPylon::QueryMasterServer(const string& svHostName, const string& svApi, const string& svInRequest,
string& svOutResponse, string& svOutMessage, CURLINFO& outStatus) const
bool CPylon::CheckForBan(const string& ipAddress, const uint64_t nucleusId,
const string& personaName, string& outReason) const
{
string svUrl;
CURLFormatUrl(svUrl, svHostName, svApi);
nlohmann::json requestJson = nlohmann::json::object();
requestJson["name"] = personaName;
requestJson["id"] = nucleusId;
requestJson["ip"] = ipAddress;
nlohmann::json responseJson;
string outMessage;
CURLINFO status;
if (!SendRequest("/banlist/isBanned", requestJson,
responseJson, outMessage, status, "banned check error"))
{
return false;
}
try
{
if (responseJson["banned"].is_boolean() &&
responseJson["banned"].get<bool>())
{
outReason = responseJson.value("reason", "#DISCONNECT_BANNED");
return true;
}
}
catch (const std::exception& ex)
{
Warning(eDLL_T::ENGINE, "%s - %s\n", __FUNCTION__, ex.what());
}
return false;
}
//-----------------------------------------------------------------------------
// Purpose: Sends request to Pylon Master Server.
// Input : *endpoint -
// &requestJson -
// &responseJson -
// &outMessage -
// &status -
// Output : True on success, false on failure.
//-----------------------------------------------------------------------------
bool CPylon::SendRequest(const char* endpoint, const nlohmann::json& requestJson,
nlohmann::json& responseJson, string& outMessage, CURLINFO& status, const char* errorText) const
{
string responseBody;
if (!QueryServer(endpoint, requestJson.dump(4).c_str(), responseBody, outMessage, status))
{
return false;
}
try
{
if (status == 200) // STATUS_OK
{
responseJson = nlohmann::json::parse(responseBody);
LogBody(responseJson);
if (responseJson["success"].is_boolean() &&
responseJson["success"].get<bool>())
{
return true;
}
else
{
ExtractError(responseJson, outMessage, status);
return false;
}
}
else
{
ExtractError(responseBody, outMessage, status, errorText);
return false;
}
}
catch (const std::exception& ex)
{
Warning(eDLL_T::ENGINE, "%s - Exception while parsing response:\n%s\n", __FUNCTION__, ex.what());
return false;
}
}
//-----------------------------------------------------------------------------
// Purpose: Sends query to master server.
// Input : *endpoint -
// *request -
// &outResponse -
// &outMessage - <- contains an error message on failure.
// &outStatus -
// Output : True on success, false on failure.
//-----------------------------------------------------------------------------
bool CPylon::QueryServer(const char* endpoint, const char* request,
string& outResponse, string& outMessage, CURLINFO& outStatus) const
{
const bool showDebug = pylon_showdebuginfo->GetBool();
const char* hostName = pylon_matchmaking_hostname->GetString();
if (showDebug)
{
DevMsg(eDLL_T::ENGINE, "Sending request to '%s' with endpoint '%s':\n%s\n",
hostName, endpoint, request);
}
string finalUrl;
CURLFormatUrl(finalUrl, hostName, endpoint);
curl_slist* sList = nullptr;
CURL* curl = CURLInitRequest(svUrl, svInRequest, svOutResponse, sList);
CURL* curl = CURLInitRequest(finalUrl.c_str(), request, outResponse, sList);
if (!curl)
{
return false;
}
CURLcode res = CURLSubmitRequest(curl, sList);
if (!CURLHandleError(curl, res, svOutMessage))
if (!CURLHandleError(curl, res, outMessage))
{
return false;
}
outStatus = CURLRetrieveInfo(curl);
if (showDebug)
{
DevMsg(eDLL_T::ENGINE, "Host '%s' replied with status: '%d'\n",
hostName, outStatus);
}
return true;
}
//-----------------------------------------------------------------------------
// Purpose: Extracts the error from the result json.
// Input : &resultJson -
// &outMessage -
// status -
// *errorText -
//-----------------------------------------------------------------------------
void CPylon::ExtractError(const nlohmann::json& resultJson, string& outMessage,
CURLINFO status, const char* errorText) const
{
if (resultJson["error"].is_string())
{
outMessage = resultJson["error"].get<string>();
}
else
{
if (!errorText)
{
errorText = "unknown error";
}
outMessage = Format("Failed with status: %d (%s)",
int(status), errorText);
}
}
//-----------------------------------------------------------------------------
// Purpose: Extracts the error from the response buffer.
// Input : &response -
// &outMessage -
// status -
// *errorText -
//-----------------------------------------------------------------------------
void CPylon::ExtractError(const string& response, string& outMessage,
CURLINFO status, const char* errorText) const
{
if (!response.empty())
{
nlohmann::json resultBody = nlohmann::json::parse(response);
ExtractError(resultBody, outMessage, status, errorText);
}
else if (status)
{
outMessage = Format("Failed server query: %d", int(status));
}
else
{
outMessage = Format("Failed to reach server: %s",
"connection timed out");
}
}
//-----------------------------------------------------------------------------
// Purpose: Logs the response body if debug is enabled.
// Input : &responseJson -
//-----------------------------------------------------------------------------
void CPylon::LogBody(const nlohmann::json& responseJson) const
{
if (pylon_showdebuginfo->GetBool())
{
const string responseBody = responseJson.dump(4);
DevMsg(eDLL_T::ENGINE, "\n%s\n", responseBody.c_str());
}
}
///////////////////////////////////////////////////////////////////////////////
CPylon* g_pMasterServer(new CPylon());

View File

@ -1,15 +1,24 @@
#pragma once
#include "thirdparty/curl/include/curl/curl.h"
#include "bansystem.h"
#include "serverlisting.h"
class CPylon
{
public:
vector<NetGameServer_t> GetServerList(string& svOutMessage) const;
bool GetServerByToken(NetGameServer_t& slOutServer, string& svOutMessage, const string& svToken) const;
bool PostServerHost(string& svOutMessage, string& svOutToken, const NetGameServer_t& netGameServer) const;
bool CheckForBan(const string& svIpAddress, const uint64_t nNucleusID, string& svOutReason) const;
bool QueryMasterServer(const string& svHostName, const string& svApi, const string& svRequest, string& svResponse, string& svOutMessage, CURLINFO& status) const;
vector<NetGameServer_t> GetServerList(string& outMessage) const;
bool GetServerByToken(NetGameServer_t& slOutServer, string& outMessage, const string& svToken) const;
bool PostServerHost(string& outMessage, string& svOutToken, const NetGameServer_t& netGameServer) const;
bool GetBannedList(const BannedVec_t& inBannedVec, BannedVec_t& outBannedVec) const;
bool CheckForBan(const string& ipAddress, const uint64_t nucleusId, const string& personaName, string& outReason) const;
void ExtractError(const nlohmann::json& resultBody, string& outMessage, CURLINFO status, const char* errorText = nullptr) const;
void ExtractError(const string& response, string& outMessage, CURLINFO status, const char* messageText = nullptr) const;
void LogBody(const nlohmann::json& responseJson) const;
bool SendRequest(const char* endpoint, const nlohmann::json& requestJson, nlohmann::json& responseJson, string& outMessage, CURLINFO& status, const char* errorText = nullptr) const;
bool QueryServer(const char* endpoint, const char* request, string& outResponse, string& outMessage, CURLINFO& outStatus) const;
#ifdef DEDICATED
bool KeepAlive(const NetGameServer_t& netGameServer);

View File

@ -1,13 +1,13 @@
#ifndef TIER2_CURLUTILS_H
#define TIER2_CURLUTILS_H
size_t CURLWriteStringCallback(char* contents, size_t size, size_t nmemb, void* userp);
size_t CURLWriteStringCallback(char* contents, const size_t size, const size_t nmemb, void* userp);
CURL* CURLInitRequest(const string& remote, const string& request, string& response, curl_slist*& slist);
CURL* CURLInitRequest(const char* remote, const char* request, string& outResponse, curl_slist*& slist);
CURLcode CURLSubmitRequest(CURL* curl, curl_slist*& slist);
CURLINFO CURLRetrieveInfo(CURL* curl);
bool CURLHandleError(CURL* curl, CURLcode res, string& outMessage);
void CURLFormatUrl(string& url, const string& host, const string& api);
void CURLFormatUrl(string& outUrl, const char* host, const char* api);
#endif // !TIER2_CURLUTILS_H

View File

@ -347,13 +347,13 @@ void CSurface::Init()
this->m_NetRandomKeyToggle->SetAnchor(Forms::AnchorStyles::Top | Forms::AnchorStyles::Left);
this->m_EngineNetworkGroup->AddControl(this->m_NetRandomKeyToggle);
this->m_NoQueuedPacketThread = new UIX::UIXCheckBox();
this->m_NoQueuedPacketThread->SetSize({ 125, 18 });
this->m_NoQueuedPacketThread->SetLocation({ 15, 30 });
this->m_NoQueuedPacketThread->SetTabIndex(2);
this->m_NoQueuedPacketThread->SetText("No queued packets");
this->m_NoQueuedPacketThread->SetAnchor(Forms::AnchorStyles::Top | Forms::AnchorStyles::Left);
this->m_EngineNetworkGroup->AddControl(this->m_NoQueuedPacketThread);
this->m_QueuedPacketThread = new UIX::UIXCheckBox();
this->m_QueuedPacketThread->SetSize({ 125, 18 });
this->m_QueuedPacketThread->SetLocation({ 15, 30 });
this->m_QueuedPacketThread->SetTabIndex(2);
this->m_QueuedPacketThread->SetText("Queued packets");
this->m_QueuedPacketThread->SetAnchor(Forms::AnchorStyles::Top | Forms::AnchorStyles::Left);
this->m_EngineNetworkGroup->AddControl(this->m_QueuedPacketThread);
this->m_NoTimeOutToggle = new UIX::UIXCheckBox();
this->m_NoTimeOutToggle->SetSize({ 125, 18 });
@ -566,7 +566,7 @@ void CSurface::LoadSettings()
this->m_NetRandomKeyToggle->SetChecked(attributeView != "0");
attributeView = vRoot.attribs["noQueuedPackets"];
this->m_NoQueuedPacketThread->SetChecked(attributeView != "0");
this->m_QueuedPacketThread->SetChecked(attributeView != "0");
attributeView = vRoot.attribs["noTimeOut"];
this->m_NoTimeOutToggle->SetChecked(attributeView != "0");
@ -635,7 +635,7 @@ void CSurface::SaveSettings()
// Network.
vRoot.add_attribute("encryptionEnable", GetControlValue(this->m_NetEncryptionToggle));
vRoot.add_attribute("randomNetKey", GetControlValue(this->m_NetRandomKeyToggle));
vRoot.add_attribute("noQueuedPackets", GetControlValue(this->m_NoQueuedPacketThread));
vRoot.add_attribute("noQueuedPackets", GetControlValue(this->m_QueuedPacketThread));
vRoot.add_attribute("noTimeOut", GetControlValue(this->m_NoTimeOutToggle));
// Video.
@ -1031,14 +1031,9 @@ void CSurface::AppendHostParameters(string& svParameters)
//-----------------------------------------------------------------------------
void CSurface::AppendNetParameters(string& svParameters)
{
if (this->m_NetEncryptionToggle->Checked())
AppendParameterInternal(svParameters, "+net_encryptionEnable", "1");
if (this->m_NetRandomKeyToggle->Checked())
AppendParameterInternal(svParameters, "+net_useRandomKey", "1");
if (this->m_NoQueuedPacketThread->Checked())
AppendParameterInternal(svParameters, "+net_queued_packet_thread", "0");
AppendParameterInternal(svParameters, "+net_encryptionEnable", this->m_NetEncryptionToggle->Checked() ? "1" : "0");
AppendParameterInternal(svParameters, "+net_useRandomKey", this->m_NetRandomKeyToggle->Checked() ? "1" : "0");
AppendParameterInternal(svParameters, "+net_queued_packet_thread", this->m_QueuedPacketThread->Checked() ? "1" : "0");
if (this->m_NoTimeOutToggle->Checked())
AppendParameterInternal(svParameters, "-notimeout");

View File

@ -106,7 +106,7 @@ private:
UIX::UIXCheckBox* m_NoAsyncJobsToggle;
UIX::UIXCheckBox* m_NetEncryptionToggle;
UIX::UIXCheckBox* m_NetRandomKeyToggle;
UIX::UIXCheckBox* m_NoQueuedPacketThread;
UIX::UIXCheckBox* m_QueuedPacketThread;
UIX::UIXCheckBox* m_NoTimeOutToggle;
UIX::UIXCheckBox* m_WindowedToggle;
UIX::UIXCheckBox* m_NoBorderToggle;

View File

@ -342,8 +342,8 @@ void ConVar::StaticInit(void)
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.");
sv_pylonRefreshRate = ConVar::StaticCreate("sv_pylonRefreshRate" , "5.0", FCVAR_RELEASE, "Pylon host refresh rate (seconds).", true, 2.f, true, 8.f, nullptr, nullptr);
sv_banlistRefreshRate = ConVar::StaticCreate("sv_banlistRefreshRate", "1.0", FCVAR_RELEASE, "Banned list refresh rate (seconds).", true, 1.f, false, 0.f, nullptr, nullptr);
sv_pylonRefreshRate = ConVar::StaticCreate("sv_pylonRefreshRate" , "5.0" , FCVAR_DEVELOPMENTONLY, "Pylon host refresh rate (seconds).", true, 2.f, true, 8.f, nullptr, nullptr);
sv_banlistRefreshRate = ConVar::StaticCreate("sv_banlistRefreshRate", "30.0", FCVAR_DEVELOPMENTONLY, "Banned list refresh rate (seconds).", true, 1.f, false, 0.f, nullptr, nullptr);
sv_statusRefreshRate = ConVar::StaticCreate("sv_statusRefreshRate" , "0.5", FCVAR_RELEASE, "Server status refresh rate (seconds).", true, 0.f, false, 0.f, nullptr, nullptr);
sv_autoReloadRate = ConVar::StaticCreate("sv_autoReloadRate" , "0" , FCVAR_RELEASE, "Time in seconds between each server auto-reload (disabled if null).", true, 0.f, false, 0.f, nullptr, nullptr);
sv_simulateBots = ConVar::StaticCreate("sv_simulateBots", "1", FCVAR_RELEASE, "Simulate user commands for bots on the server.", true, 0.f, false, 0.f, nullptr, nullptr);
@ -1245,13 +1245,13 @@ static void PrintCommand(const ConCommand* cmd, bool logging, FileHandle_t& f)
Q_strncat(emptyflags, csvf, len);
}
// Names staring with +/- need to be wrapped in single quotes
char name[256];
Q_snprintf(name, sizeof(name), "%s", cmd->GetName());
if (name[0] == '+' || name[0] == '-')
char nameBuf[256];
Q_snprintf(nameBuf, sizeof(nameBuf), "%s", cmd->GetName());
if (nameBuf[0] == '+' || nameBuf[0] == '-')
{
Q_snprintf(name, sizeof(name), "'%s'", cmd->GetName());
Q_snprintf(nameBuf, sizeof(nameBuf), "'%s'", cmd->GetName());
}
FileSystem()->FPrintf(f, "\"%s\",\"%s\",%s,\"%s\"\n", name, "cmd", emptyflags, StripQuotes(cmd->GetHelpText(), tempbuff, sizeof(tempbuff)));
FileSystem()->FPrintf(f, "\"%s\",\"%s\",%s,\"%s\"\n", nameBuf, "cmd", emptyflags, StripQuotes(cmd->GetHelpText(), tempbuff, sizeof(tempbuff)));
}
}

View File

@ -7,13 +7,13 @@
#include "tier1/cvar.h"
#include "tier2/curlutils.h"
size_t CURLWriteStringCallback(char* contents, size_t size, size_t nmemb, void* userp)
size_t CURLWriteStringCallback(char* contents, const size_t size, const size_t nmemb, void* userp)
{
reinterpret_cast<string*>(userp)->append(contents, size * nmemb);
return size * nmemb;
}
CURL* CURLInitRequest(const string& remote, const string& request, string& response, curl_slist*& slist)
CURL* CURLInitRequest(const char* remote, const char* request, string& outResponse, curl_slist*& slist)
{
std::function<void(const char*)> fnError = [&](const char* errorMsg)
{
@ -36,14 +36,14 @@ CURL* CURLInitRequest(const string& remote, const string& request, string& respo
}
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, slist);
curl_easy_setopt(curl, CURLOPT_URL, remote.c_str());
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, request.c_str());
curl_easy_setopt(curl, CURLOPT_URL, remote);
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, request);
curl_easy_setopt(curl, CURLOPT_POST, 1L);
curl_easy_setopt(curl, CURLOPT_TIMEOUT, curl_timeout->GetInt());
curl_easy_setopt(curl, CURLOPT_USE_SSL, CURLUSESSL_ALL);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, CURLWriteStringCallback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &outResponse);
curl_easy_setopt(curl, CURLOPT_VERBOSE, curl_debug->GetBool());
curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1L);
@ -89,7 +89,7 @@ bool CURLHandleError(CURL* curl, CURLcode res, string& outMessage)
return false;
}
void CURLFormatUrl(string& url, const string& host, const string& api)
void CURLFormatUrl(string& outUrl, const char* host, const char* api)
{
url = Format("%s%s%s", "https://", host.c_str(), api.c_str());
outUrl = Format("%s%s%s", "https://", host, api);
}