NetworkSystem: fix numerous issues

This patch splits host logic from CServerListManager. CServerListManager is actually meant for the client to manage the server list to which the client could connect to. The hosting logic has been moved to the new CServerHostManager class.

Previously, we stored all the hosting details in CServerListManager, with connection criteria in CPylon, this data has been moved over to CServerHostManager as well.

Previously, we also needed a mutex to access the server host data, function HostState_KeepAlive() has been refactored to the point this mutex is no longer necessary as the only threaded process is the actual request, the rest is being applied in the main thread. We also now only construct a NetGameServer_t struct if we actually plan to host.

Access to CPylon::m_Language is now also protected by a mutex, as the change callback of cvar 'language' and the threaded method 'CPylon::QueryServer()' are competing for access.
This commit is contained in:
Kawe Mazidjatari 2024-03-10 01:57:04 +01:00
parent 1908efce96
commit 63237361e9
13 changed files with 295 additions and 215 deletions

View File

@ -268,8 +268,12 @@ void LanguageChanged_f(IConVar* pConVar, const char* pOldString)
// if new text isn't valid but the old value is, reset the value // if new text isn't valid but the old value is, reset the value
if (Localize_IsLanguageSupported(pOldString)) if (Localize_IsLanguageSupported(pOldString))
pNewString = pOldString; pNewString = pOldString;
else // this shouldn't really happen, but if neither the old nor new values are valid, set to english else
{
// this shouldn't really happen, but if neither the old nor new values are valid, set to english
Assert(0);
pNewString = g_LanguageNames[0]; pNewString = g_LanguageNames[0];
}
} }
pConVarRef->SetValue(pNewString); pConVarRef->SetValue(pNewString);

View File

@ -14,6 +14,7 @@
#include "engine/server/server.h" #include "engine/server/server.h"
#include "engine/client/client.h" #include "engine/client/client.h"
#ifndef CLIENT_DLL #ifndef CLIENT_DLL
#include "networksystem/hostmanager.h"
#include "jwt/include/decode.h" #include "jwt/include/decode.h"
#include "mbedtls/include/mbedtls/sha256.h" #include "mbedtls/include/mbedtls/sha256.h"
#endif #endif
@ -162,7 +163,7 @@ bool CClient::Authenticate(const char* const playerName, char* const reasonBuf,
const int idLen = snprintf(newId, sizeof(newId), "%llu-%s-%s", const int idLen = snprintf(newId, sizeof(newId), "%llu-%s-%s",
(NucleusID_t)this->m_DataBlock.userData, (NucleusID_t)this->m_DataBlock.userData,
playerName, playerName,
g_MasterServer.GetHostIP().c_str()); g_ServerHostManager.GetHostIP().c_str());
if (idLen < 0) if (idLen < 0)
ERROR_AND_RETURN("Session ID stitching failed"); ERROR_AND_RETURN("Session ID stitching failed");

View File

@ -10,6 +10,7 @@
#include "tier0/jobthread.h" #include "tier0/jobthread.h"
#include "tier0/commandline.h" #include "tier0/commandline.h"
#include "tier0/fasttimer.h" #include "tier0/fasttimer.h"
#include "tier0/frametask.h"
#include "tier1/cvar.h" #include "tier1/cvar.h"
#include "tier1/NetAdr.h" #include "tier1/NetAdr.h"
#include "tier2/socketcreator.h" #include "tier2/socketcreator.h"
@ -45,6 +46,7 @@
#include "networksystem/pylon.h" #include "networksystem/pylon.h"
#ifndef CLIENT_DLL #ifndef CLIENT_DLL
#include "networksystem/bansystem.h" #include "networksystem/bansystem.h"
#include "networksystem/hostmanager.h"
#endif // !CLIENT_DLL #endif // !CLIENT_DLL
#include "networksystem/listmanager.h" #include "networksystem/listmanager.h"
#include "public/edict.h" #include "public/edict.h"
@ -53,58 +55,85 @@
#endif // !CLIENT_DLL #endif // !CLIENT_DLL
#include "game/shared/vscript_shared.h" #include "game/shared/vscript_shared.h"
#ifdef DEDICATED
static ConVar hostdesc("hostdesc", "", FCVAR_RELEASE, "Host game server description.");
#endif // DEDICATED
#ifndef CLIENT_DLL #ifndef CLIENT_DLL
static ConVar sv_pylonVisibility("sv_pylonVisibility", "0", FCVAR_RELEASE, "Determines the visibility to the Pylon master server.", "0 = Offline, 1 = Hidden, 2 = Public."); static ConVar sv_pylonVisibility("sv_pylonVisibility", "0", FCVAR_RELEASE, "Determines the visibility to the Pylon master server.", "0 = Offline, 1 = Hidden, 2 = Public.");
static ConVar sv_pylonRefreshRate("sv_pylonRefreshRate", "5.0", FCVAR_DEVELOPMENTONLY, "Pylon host refresh rate (seconds)."); static ConVar sv_pylonRefreshRate("sv_pylonRefreshRate", "5.0", FCVAR_DEVELOPMENTONLY, "Pylon host refresh rate (seconds).");
static ConVar sv_autoReloadRate("sv_autoReloadRate", "0", FCVAR_RELEASE, "Time in seconds between each server auto-reload (disabled if null)."); static ConVar sv_autoReloadRate("sv_autoReloadRate", "0", FCVAR_RELEASE, "Time in seconds between each server auto-reload (disabled if null).");
#endif // !CLIENT_DLL
#ifdef DEDICATED
static ConVar hostdesc("hostdesc", "", FCVAR_RELEASE, "Host game server description.");
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// Purpose: Send keep alive request to Pylon Master Server. // Purpose: Send keep alive request to Pylon Master Server.
// Input : &netGameServer -
// Output : Returns true on success, false otherwise. // Output : Returns true on success, false otherwise.
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
bool HostState_KeepAlive(const NetGameServer_t& netGameServer) static void HostState_KeepAlive()
{ {
if (!g_pServer->IsActive() || !sv_pylonVisibility.GetBool()) // Check for active game. if (!g_pServer->IsActive() || !sv_pylonVisibility.GetBool()) // Check for active game.
{ {
return false; return;
} }
string errorMsg; const NetGameServer_t gameServer
string hostToken;
string hostIp;
const bool result = g_MasterServer.PostServerHost(errorMsg, hostToken, hostIp, netGameServer);
if (!result)
{ {
if (!errorMsg.empty() && g_MasterServer.GetCurrentError().compare(errorMsg) != NULL) hostname->GetString(),
{ hostdesc.GetString(),
g_MasterServer.SetCurrentError(errorMsg); sv_pylonVisibility.GetInt() == ServerVisibility_e::HIDDEN,
Error(eDLL_T::SERVER, NO_ERROR, "%s\n", errorMsg.c_str()); g_pHostState->m_levelName,
} v_Playlists_GetCurrent(),
} hostip->GetString(),
else // Attempt to log the token, if there is one. hostport->GetInt(),
{ g_pNetKey->GetBase64NetKey(),
if (!hostToken.empty() && g_MasterServer.GetCurrentToken().compare(hostToken) != NULL) *g_nServerRemoteChecksum,
{ SDK_VERSION,
g_MasterServer.SetCurrentToken(hostToken); g_pServer->GetNumClients(),
Msg(eDLL_T::SERVER, "Published server with token: %s'%s%s%s'\n", g_ServerGlobalVariables->m_nMaxClients,
g_svReset, g_svGreyB, std::chrono::duration_cast<std::chrono::milliseconds>(
hostToken.c_str(), g_svReset); std::chrono::system_clock::now().time_since_epoch()
} ).count()
} };
if (hostIp.length() != 0) std::thread request([&, gameServer]
g_MasterServer.SetHostIP(hostIp); {
string errorMsg;
string hostToken;
string hostIp;
return result; const bool result = g_MasterServer.PostServerHost(errorMsg, hostToken, hostIp, gameServer);
// Apply the data the next frame
g_TaskQueue.Dispatch([result, errorMsg, hostToken, hostIp]
{
if (!result)
{
if (!errorMsg.empty() && g_ServerHostManager.GetCurrentError().compare(errorMsg) != NULL)
{
g_ServerHostManager.SetCurrentError(errorMsg);
Error(eDLL_T::SERVER, NO_ERROR, "%s\n", errorMsg.c_str());
}
}
else // Attempt to log the token, if there is one.
{
if (!hostToken.empty() && g_ServerHostManager.GetCurrentToken().compare(hostToken) != NULL)
{
g_ServerHostManager.SetCurrentToken(hostToken);
Msg(eDLL_T::SERVER, "Published server with token: %s'%s%s%s'\n",
g_svReset, g_svGreyB,
hostToken.c_str(), g_svReset);
}
}
if (hostIp.length() != 0)
g_ServerHostManager.SetHostIP(hostIp);
}, 0);
}
);
request.detach();
} }
#endif // !CLIENT_DLL #endif // DEDICATED
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// Purpose: state machine's main processing loop // Purpose: state machine's main processing loop
@ -336,32 +365,13 @@ void CHostState::Think(void) const
if (sv_globalBanlist.GetBool() && if (sv_globalBanlist.GetBool() &&
banListTimer.GetDurationInProgress().GetSeconds() > sv_banlistRefreshRate.GetFloat()) banListTimer.GetDurationInProgress().GetSeconds() > sv_banlistRefreshRate.GetFloat())
{ {
SV_CheckForBan(); SV_CheckClientsForBan();
banListTimer.Start(); banListTimer.Start();
} }
#ifdef DEDICATED #ifdef DEDICATED
if (pylonTimer.GetDurationInProgress().GetSeconds() > sv_pylonRefreshRate.GetFloat()) if (pylonTimer.GetDurationInProgress().GetSeconds() > sv_pylonRefreshRate.GetFloat())
{ {
const NetGameServer_t netGameServer HostState_KeepAlive();
{
hostname->GetString(),
hostdesc.GetString(),
sv_pylonVisibility.GetInt() == EServerVisibility_t::HIDDEN,
g_pHostState->m_levelName,
v_Playlists_GetCurrent(),
hostip->GetString(),
hostport->GetInt(),
g_pNetKey->GetBase64NetKey(),
*g_nServerRemoteChecksum,
SDK_VERSION,
g_pServer->GetNumClients(),
g_ServerGlobalVariables->m_nMaxClients,
std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::system_clock::now().time_since_epoch()
).count()
};
std::thread(&HostState_KeepAlive, netGameServer).detach();
pylonTimer.Start(); pylonTimer.Start();
} }
#endif // DEDICATED #endif // DEDICATED

View File

@ -83,8 +83,10 @@ namespace VScriptCode
SQRESULT RefreshServerList(HSQUIRRELVM v) SQRESULT RefreshServerList(HSQUIRRELVM v)
{ {
string serverMessage; // Refresh list. string serverMessage; // Refresh list.
size_t iCount = g_ServerListManager.RefreshServerList(serverMessage); size_t iCount;
// TODO: return error string on failure?
g_ServerListManager.RefreshServerList(serverMessage, iCount);
sq_pushinteger(v, static_cast<SQInteger>(iCount)); sq_pushinteger(v, static_cast<SQInteger>(iCount));
return SQ_OK; return SQ_OK;
@ -157,7 +159,7 @@ namespace VScriptCode
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
SQRESULT GetServerName(HSQUIRRELVM v) SQRESULT GetServerName(HSQUIRRELVM v)
{ {
std::lock_guard<std::mutex> l(g_ServerListManager.m_Mutex); AUTO_LOCK(g_ServerListManager.m_Mutex);
SQInteger iServer = sq_getinteger(v, 1); SQInteger iServer = sq_getinteger(v, 1);
if (!Script_CheckServerIndex(v, iServer)) if (!Script_CheckServerIndex(v, iServer))
@ -176,7 +178,7 @@ namespace VScriptCode
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
SQRESULT GetServerDescription(HSQUIRRELVM v) SQRESULT GetServerDescription(HSQUIRRELVM v)
{ {
std::lock_guard<std::mutex> l(g_ServerListManager.m_Mutex); AUTO_LOCK(g_ServerListManager.m_Mutex);
SQInteger iServer = sq_getinteger(v, 1); SQInteger iServer = sq_getinteger(v, 1);
if (!Script_CheckServerIndex(v, iServer)) if (!Script_CheckServerIndex(v, iServer))
@ -195,7 +197,7 @@ namespace VScriptCode
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
SQRESULT GetServerMap(HSQUIRRELVM v) SQRESULT GetServerMap(HSQUIRRELVM v)
{ {
std::lock_guard<std::mutex> l(g_ServerListManager.m_Mutex); AUTO_LOCK(g_ServerListManager.m_Mutex);
SQInteger iServer = sq_getinteger(v, 1); SQInteger iServer = sq_getinteger(v, 1);
if (!Script_CheckServerIndex(v, iServer)) if (!Script_CheckServerIndex(v, iServer))
@ -214,7 +216,7 @@ namespace VScriptCode
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
SQRESULT GetServerPlaylist(HSQUIRRELVM v) SQRESULT GetServerPlaylist(HSQUIRRELVM v)
{ {
std::lock_guard<std::mutex> l(g_ServerListManager.m_Mutex); AUTO_LOCK(g_ServerListManager.m_Mutex);
SQInteger iServer = sq_getinteger(v, 1); SQInteger iServer = sq_getinteger(v, 1);
if (!Script_CheckServerIndex(v, iServer)) if (!Script_CheckServerIndex(v, iServer))
@ -233,7 +235,7 @@ namespace VScriptCode
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
SQRESULT GetServerCurrentPlayers(HSQUIRRELVM v) SQRESULT GetServerCurrentPlayers(HSQUIRRELVM v)
{ {
std::lock_guard<std::mutex> l(g_ServerListManager.m_Mutex); AUTO_LOCK(g_ServerListManager.m_Mutex);
SQInteger iServer = sq_getinteger(v, 1); SQInteger iServer = sq_getinteger(v, 1);
if (!Script_CheckServerIndex(v, iServer)) if (!Script_CheckServerIndex(v, iServer))
@ -252,7 +254,7 @@ namespace VScriptCode
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
SQRESULT GetServerMaxPlayers(HSQUIRRELVM v) SQRESULT GetServerMaxPlayers(HSQUIRRELVM v)
{ {
std::lock_guard<std::mutex> l(g_ServerListManager.m_Mutex); AUTO_LOCK(g_ServerListManager.m_Mutex);
SQInteger iServer = sq_getinteger(v, 1); SQInteger iServer = sq_getinteger(v, 1);
if (!Script_CheckServerIndex(v, iServer)) if (!Script_CheckServerIndex(v, iServer))
@ -372,7 +374,7 @@ namespace VScriptCode
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
SQRESULT ConnectToListedServer(HSQUIRRELVM v) SQRESULT ConnectToListedServer(HSQUIRRELVM v)
{ {
std::lock_guard<std::mutex> l(g_ServerListManager.m_Mutex); AUTO_LOCK(g_ServerListManager.m_Mutex);
SQInteger iServer = sq_getinteger(v, 1); SQInteger iServer = sq_getinteger(v, 1);
if (!Script_CheckServerIndex(v, iServer)) if (!Script_CheckServerIndex(v, iServer))

View File

@ -16,7 +16,7 @@
#include "vscript_server.h" #include "vscript_server.h"
#include <engine/host_state.h> #include <engine/host_state.h>
#include <networksystem/listmanager.h> #include <networksystem/hostmanager.h>
/* /*
===================== =====================
@ -51,7 +51,7 @@ namespace VScriptCode
SQChar* serverDescription = sq_getstring(v, 2); SQChar* serverDescription = sq_getstring(v, 2);
SQChar* serverMapName = sq_getstring(v, 3); SQChar* serverMapName = sq_getstring(v, 3);
SQChar* serverPlaylist = sq_getstring(v, 4); SQChar* serverPlaylist = sq_getstring(v, 4);
EServerVisibility_t eServerVisibility = static_cast<EServerVisibility_t>(sq_getinteger(v, 5)); ServerVisibility_e serverVisibility = static_cast<ServerVisibility_e>(sq_getinteger(v, 5));
if (!VALID_CHARSTAR(serverName) || if (!VALID_CHARSTAR(serverName) ||
!VALID_CHARSTAR(serverMapName) || !VALID_CHARSTAR(serverMapName) ||
@ -61,16 +61,16 @@ namespace VScriptCode
} }
// Adjust browser settings. // Adjust browser settings.
std::lock_guard<std::mutex> l(g_ServerListManager.m_Mutex); NetGameServer_t& details = g_ServerHostManager.GetDetails();
g_ServerListManager.m_Server.name = serverName; details.name = serverName;
g_ServerListManager.m_Server.description = serverDescription; details.description = serverDescription;
g_ServerListManager.m_Server.map = serverMapName; details.map = serverMapName;
g_ServerListManager.m_Server.playlist = serverPlaylist; details.playlist = serverPlaylist;
g_ServerListManager.m_ServerVisibility = eServerVisibility;
// Launch server. // Launch server.
g_ServerListManager.LaunchServer(g_pServer->IsActive()); g_ServerHostManager.SetVisibility(serverVisibility);
g_ServerHostManager.LaunchServer(g_pServer->IsActive());
return SQ_OK; return SQ_OK;

View File

@ -30,6 +30,7 @@ History:
#include "engine/client/clientstate.h" #include "engine/client/clientstate.h"
#include "networksystem/serverlisting.h" #include "networksystem/serverlisting.h"
#include "networksystem/pylon.h" #include "networksystem/pylon.h"
#include "networksystem/hostmanager.h"
#include "networksystem/listmanager.h" #include "networksystem/listmanager.h"
#include "rtech/playlists/playlists.h" #include "rtech/playlists/playlists.h"
#include "common/callback.h" #include "common/callback.h"
@ -256,7 +257,7 @@ void CBrowser::DrawBrowserPanel(void)
ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthStretch, 5); ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthStretch, 5);
ImGui::TableHeadersRow(); ImGui::TableHeadersRow();
g_ServerListManager.m_Mutex.lock(); g_ServerListManager.m_Mutex.Lock();
vector<const NetGameServer_t*> filteredServers; vector<const NetGameServer_t*> filteredServers;
// Filter the server list first before running it over the ImGui list // Filter the server list first before running it over the ImGui list
@ -321,7 +322,7 @@ void CBrowser::DrawBrowserPanel(void)
} }
filteredServers.clear(); filteredServers.clear();
g_ServerListManager.m_Mutex.unlock(); g_ServerListManager.m_Mutex.Unlock();
ImGui::EndTable(); ImGui::EndTable();
ImGui::PopStyleVar(frameStyleVars); ImGui::PopStyleVar(frameStyleVars);
@ -383,7 +384,8 @@ void CBrowser::RefreshServerList(void)
std::thread request([&] std::thread request([&]
{ {
std::string serverListMessage; std::string serverListMessage;
g_ServerListManager.RefreshServerList(serverListMessage); size_t numServers;
g_ServerListManager.RefreshServerList(serverListMessage, numServers);
g_TaskQueue.Dispatch([&, serverListMessage] g_TaskQueue.Dispatch([&, serverListMessage]
{ {
@ -518,20 +520,20 @@ void CBrowser::HiddenServersModal(void)
void CBrowser::DrawHostPanel(void) void CBrowser::DrawHostPanel(void)
{ {
#ifndef CLIENT_DLL #ifndef CLIENT_DLL
std::lock_guard<std::mutex> l(g_ServerListManager.m_Mutex); NetGameServer_t& details = g_ServerHostManager.GetDetails();
ImGui::InputTextWithHint("##ServerHost_ServerName", "Server name (required)", &g_ServerListManager.m_Server.name); ImGui::InputTextWithHint("##ServerHost_ServerName", "Server name (required)", &details.name);
ImGui::InputTextWithHint("##ServerHost_ServerDesc", "Server description (optional)", &g_ServerListManager.m_Server.description); ImGui::InputTextWithHint("##ServerHost_ServerDesc", "Server description (optional)", &details.description);
ImGui::Spacing(); ImGui::Spacing();
if (ImGui::BeginCombo("Mode", g_ServerListManager.m_Server.playlist.c_str())) if (ImGui::BeginCombo("Mode", details.playlist.c_str()))
{ {
g_PlaylistsVecMutex.lock(); g_PlaylistsVecMutex.lock();
for (const string& svPlaylist : g_vAllPlaylists) for (const string& svPlaylist : g_vAllPlaylists)
{ {
if (ImGui::Selectable(svPlaylist.c_str(), svPlaylist == g_ServerListManager.m_Server.playlist)) if (ImGui::Selectable(svPlaylist.c_str(), svPlaylist == details.playlist))
{ {
g_ServerListManager.m_Server.playlist = svPlaylist; details.playlist = svPlaylist;
} }
} }
@ -539,7 +541,7 @@ void CBrowser::DrawHostPanel(void)
ImGui::EndCombo(); ImGui::EndCombo();
} }
if (ImGui::BeginCombo("Map", g_ServerListManager.m_Server.map.c_str())) if (ImGui::BeginCombo("Map", details.map.c_str()))
{ {
g_InstalledMapsMutex.lock(); g_InstalledMapsMutex.lock();
@ -548,9 +550,9 @@ void CBrowser::DrawHostPanel(void)
const CUtlString& mapName = g_InstalledMaps[i]; const CUtlString& mapName = g_InstalledMaps[i];
if (ImGui::Selectable(mapName.String(), if (ImGui::Selectable(mapName.String(),
mapName.IsEqual_CaseInsensitive(g_ServerListManager.m_Server.map.c_str()))) mapName.IsEqual_CaseInsensitive(details.map.c_str())))
{ {
g_ServerListManager.m_Server.map = mapName.String(); details.map = mapName.String();
} }
} }
@ -566,17 +568,17 @@ void CBrowser::DrawHostPanel(void)
ImGui::Text("Server visibility"); ImGui::Text("Server visibility");
if (ImGui::SameLine(); ImGui::RadioButton("offline", g_ServerListManager.m_ServerVisibility == EServerVisibility_t::OFFLINE)) if (ImGui::SameLine(); ImGui::RadioButton("offline", g_ServerHostManager.GetVisibility() == ServerVisibility_e::OFFLINE))
{ {
g_ServerListManager.m_ServerVisibility = EServerVisibility_t::OFFLINE; g_ServerHostManager.SetVisibility(ServerVisibility_e::OFFLINE);
} }
if (ImGui::SameLine(); ImGui::RadioButton("hidden", g_ServerListManager.m_ServerVisibility == EServerVisibility_t::HIDDEN)) if (ImGui::SameLine(); ImGui::RadioButton("hidden", g_ServerHostManager.GetVisibility() == ServerVisibility_e::HIDDEN))
{ {
g_ServerListManager.m_ServerVisibility = EServerVisibility_t::HIDDEN; g_ServerHostManager.SetVisibility(ServerVisibility_e::HIDDEN);
} }
if (ImGui::SameLine(); ImGui::RadioButton("public", g_ServerListManager.m_ServerVisibility == EServerVisibility_t::PUBLIC)) if (ImGui::SameLine(); ImGui::RadioButton("public", g_ServerHostManager.GetVisibility() == ServerVisibility_e::PUBLIC))
{ {
g_ServerListManager.m_ServerVisibility = EServerVisibility_t::PUBLIC; g_ServerHostManager.SetVisibility(ServerVisibility_e::PUBLIC);
} }
ImGui::TextColored(m_hostMessageColor, "%s", m_hostMessage.c_str()); ImGui::TextColored(m_hostMessageColor, "%s", m_hostMessage.c_str());
@ -596,27 +598,27 @@ void CBrowser::DrawHostPanel(void)
{ {
m_hostMessage.clear(); m_hostMessage.clear();
const bool enforceField = g_ServerListManager.m_ServerVisibility == EServerVisibility_t::OFFLINE const bool enforceField = g_ServerHostManager.GetVisibility() == ServerVisibility_e::OFFLINE
? true ? true
: !g_ServerListManager.m_Server.name.empty(); : !details.name.empty();
if (enforceField && !g_ServerListManager.m_Server.playlist.empty() && !g_ServerListManager.m_Server.map.empty()) if (enforceField && !details.playlist.empty() && !details.map.empty())
{ {
g_ServerListManager.LaunchServer(serverActive); // Launch server. g_ServerHostManager.LaunchServer(serverActive); // Launch server.
} }
else else
{ {
if (g_ServerListManager.m_Server.name.empty()) if (details.name.empty())
{ {
m_hostMessage = "Server name is required."; m_hostMessage = "Server name is required.";
m_hostMessageColor = ImVec4(1.00f, 0.00f, 0.00f, 1.00f); m_hostMessageColor = ImVec4(1.00f, 0.00f, 0.00f, 1.00f);
} }
else if (g_ServerListManager.m_Server.playlist.empty()) else if (details.playlist.empty())
{ {
m_hostMessage = "Playlist is required."; m_hostMessage = "Playlist is required.";
m_hostMessageColor = ImVec4(1.00f, 0.00f, 0.00f, 1.00f); m_hostMessageColor = ImVec4(1.00f, 0.00f, 0.00f, 1.00f);
} }
else if (g_ServerListManager.m_Server.map.empty()) else if (details.map.empty())
{ {
m_hostMessage = "Level name is required."; m_hostMessage = "Level name is required.";
m_hostMessageColor = ImVec4(1.00f, 0.00f, 0.00f, 1.00f); m_hostMessageColor = ImVec4(1.00f, 0.00f, 0.00f, 1.00f);
@ -645,9 +647,9 @@ void CBrowser::DrawHostPanel(void)
if (ImGui::Button("Change level", ImVec2(contentRegionMax.x, 32))) if (ImGui::Button("Change level", ImVec2(contentRegionMax.x, 32)))
{ {
if (!g_ServerListManager.m_Server.map.empty()) if (!details.map.empty())
{ {
g_ServerListManager.LaunchServer(serverActive); g_ServerHostManager.LaunchServer(serverActive);
} }
else else
{ {
@ -705,12 +707,15 @@ void CBrowser::UpdateHostingStatus(void)
#ifndef CLIENT_DLL #ifndef CLIENT_DLL
assert(g_pHostState && g_pCVar); assert(g_pHostState && g_pCVar);
std::lock_guard<std::mutex> l(g_ServerListManager.m_Mutex); const HostStatus_e hostStatus = g_pServer->IsActive()
g_ServerListManager.m_HostingStatus = g_pServer->IsActive() ? EHostStatus_t::HOSTING : EHostStatus_t::NOT_HOSTING; // Are we hosting a server? ? HostStatus_e::HOSTING
: HostStatus_e::NOT_HOSTING;
switch (g_ServerListManager.m_HostingStatus) g_ServerHostManager.SetHostStatus(hostStatus); // Are we hosting a server?
switch (hostStatus)
{ {
case EHostStatus_t::NOT_HOSTING: case HostStatus_e::NOT_HOSTING:
{ {
if (!m_hostToken.empty()) if (!m_hostToken.empty())
{ {
@ -726,9 +731,12 @@ void CBrowser::UpdateHostingStatus(void)
break; break;
} }
case EHostStatus_t::HOSTING: case HostStatus_e::HOSTING:
{ {
if (g_ServerListManager.m_ServerVisibility == EServerVisibility_t::OFFLINE) const ServerVisibility_e serverVisibility = g_ServerHostManager.GetVisibility();
NetGameServer_t& details = g_ServerHostManager.GetDetails();
if (serverVisibility == ServerVisibility_e::OFFLINE)
{ {
break; break;
} }
@ -738,14 +746,14 @@ void CBrowser::UpdateHostingStatus(void)
break; break;
} }
switch (g_ServerListManager.m_ServerVisibility) switch (serverVisibility)
{ {
case EServerVisibility_t::HIDDEN: case ServerVisibility_e::HIDDEN:
g_ServerListManager.m_Server.hidden = true; details.hidden = true;
break; break;
case EServerVisibility_t::PUBLIC: case ServerVisibility_e::PUBLIC:
g_ServerListManager.m_Server.hidden = false; details.hidden = false;
break; break;
default: default:
break; break;
@ -753,9 +761,9 @@ void CBrowser::UpdateHostingStatus(void)
const NetGameServer_t netGameServer const NetGameServer_t netGameServer
{ {
g_ServerListManager.m_Server.name, details.name,
g_ServerListManager.m_Server.description, details.description,
g_ServerListManager.m_Server.hidden, details.hidden,
g_pHostState->m_levelName, g_pHostState->m_levelName,
v_Playlists_GetCurrent(), v_Playlists_GetCurrent(),
hostip->GetString(), hostip->GetString(),
@ -820,7 +828,7 @@ void CBrowser::InstallHostingDetails(const bool postFailed, const char* const ho
if (!hostIp.empty()) if (!hostIp.empty())
{ {
g_MasterServer.SetHostIP(hostIp); g_ServerHostManager.SetHostIP(hostIp);
} }
if (postFailed) if (postFailed)

View File

@ -6,6 +6,8 @@ start_sources()
add_sources( SOURCE_GROUP "Private" add_sources( SOURCE_GROUP "Private"
"bansystem.cpp" "bansystem.cpp"
"bansystem.h" "bansystem.h"
"hostmanager.cpp"
"hostmanager.h"
"listmanager.cpp" "listmanager.cpp"
"listmanager.h" "listmanager.h"
"pylon.cpp" "pylon.cpp"

View File

@ -0,0 +1,51 @@
//=============================================================================//
//
// Purpose: server host manager
//
//-----------------------------------------------------------------------------
//
//=============================================================================//
#include "tier0/frametask.h"
#include "rtech/playlists/playlists.h"
#include "engine/cmd.h"
#include "hostmanager.h"
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CServerHostManager::CServerHostManager(void)
: m_HostingStatus(HostStatus_e::NOT_HOSTING)
, m_ServerVisibility(ServerVisibility_e::OFFLINE)
{
}
//-----------------------------------------------------------------------------
// Purpose: Launch server with given parameters
//-----------------------------------------------------------------------------
void CServerHostManager::LaunchServer(const bool changeLevel) const
{
if (!ThreadInMainThread())
{
g_TaskQueue.Dispatch([this, changeLevel]()
{
this->LaunchServer(changeLevel);
}, 0);
return;
}
Msg(eDLL_T::ENGINE, "Starting server with name: \"%s\" map: \"%s\" playlist: \"%s\"\n",
m_Server.name.c_str(), m_Server.map.c_str(), m_Server.playlist.c_str());
/*
* Playlist gets parsed in two instances, first in Playlists_Parse() with all the necessary
* values. Then when you would normally call launchplaylist which calls StartPlaylist it would cmd
* call mp_gamemode which parses the gamemode specific part of the playlist..
*/
v_Playlists_Parse(m_Server.playlist.c_str());
mp_gamemode->SetValue(m_Server.playlist.c_str());
const string command = Format("%s \"%s\"", changeLevel ? "changelevel" : "map", m_Server.map.c_str());
Cbuf_AddText(Cbuf_GetCurrentPlayer(), command.c_str(), cmd_source_t::kCommandSrcCode);
}
CServerHostManager g_ServerHostManager;

View File

@ -0,0 +1,55 @@
#ifndef HOSTMANAGER_H
#define HOSTMANAGER_H
#include <networksystem/serverlisting.h>
enum HostStatus_e
{
NOT_HOSTING,
HOSTING
};
enum ServerVisibility_e
{
OFFLINE,
HIDDEN,
PUBLIC
};
class CServerHostManager
{
public:
CServerHostManager();
void LaunchServer(const bool changeLevel) const;
inline HostStatus_e GetHostStatus(void) const { return m_HostingStatus; }
inline void SetHostStatus(const HostStatus_e hostStatus) { m_HostingStatus = hostStatus; }
inline ServerVisibility_e GetVisibility(void) const { return m_ServerVisibility; }
inline void SetVisibility(const ServerVisibility_e visibility) { m_ServerVisibility = visibility; }
inline NetGameServer_t& GetDetails() { return m_Server; }
inline void SetCurrentToken(const string& token) { m_Token = token; }
inline const string& GetCurrentToken() const { return m_Token; }
inline void SetCurrentError(const string& error) { m_ErrorMsg = error; }
inline const string& GetCurrentError() const { return m_ErrorMsg; }
inline void SetHostIP(const string& ip) { m_HostIP = ip; };
inline const string& GetHostIP() const { return m_HostIP; };
private:
HostStatus_e m_HostingStatus;
ServerVisibility_e m_ServerVisibility;
NetGameServer_t m_Server;
string m_Token;
string m_ErrorMsg;
string m_HostIP;
};
extern CServerHostManager g_ServerHostManager;
#endif // HOSTMANAGER_H

View File

@ -1,6 +1,6 @@
//=============================================================================// //=============================================================================//
// //
// Purpose: // Purpose: server list manager
// //
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// //
@ -22,25 +22,30 @@
// Purpose: // Purpose:
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
CServerListManager::CServerListManager(void) CServerListManager::CServerListManager(void)
: m_HostingStatus(EHostStatus_t::NOT_HOSTING)
, m_ServerVisibility(EServerVisibility_t::OFFLINE)
{ {
} }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// Purpose: get server list from pylon // Purpose: get server list from pylon
// Input : &svMessage - // Input : &outMessage -
// Output : amount of servers found // &numServers -
// Output : true on success, false otherwise
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
size_t CServerListManager::RefreshServerList(string& svMessage) bool CServerListManager::RefreshServerList(string& outMessage, size_t& numServers)
{ {
ClearServerList(); ClearServerList();
vector<NetGameServer_t> vServerList = g_MasterServer.GetServerList(svMessage);
std::lock_guard<std::mutex> l(m_Mutex); vector<NetGameServer_t> serverList;
m_vServerList = vServerList; const bool success = g_MasterServer.GetServerList(serverList, outMessage);
return m_vServerList.size(); if (!success)
return false;
AUTO_LOCK(m_Mutex);
m_vServerList = serverList;
numServers = m_vServerList.size();
return true;
} }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
@ -48,38 +53,10 @@ size_t CServerListManager::RefreshServerList(string& svMessage)
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
void CServerListManager::ClearServerList(void) void CServerListManager::ClearServerList(void)
{ {
std::lock_guard<std::mutex> l(m_Mutex); AUTO_LOCK(m_Mutex);
m_vServerList.clear(); m_vServerList.clear();
} }
//-----------------------------------------------------------------------------
// Purpose: Launch server with given parameters
//-----------------------------------------------------------------------------
void CServerListManager::LaunchServer(const bool bChangeLevel) const
{
if (!ThreadInMainThread())
{
g_TaskQueue.Dispatch([this, bChangeLevel]()
{
this->LaunchServer(bChangeLevel);
}, 0);
return;
}
Msg(eDLL_T::ENGINE, "Starting server with name: \"%s\" map: \"%s\" playlist: \"%s\"\n",
m_Server.name.c_str(), m_Server.map.c_str(), m_Server.playlist.c_str());
/*
* Playlist gets parsed in two instances, first in Playlists_Parse() with all the necessary
* values. Then when you would normally call launchplaylist which calls StartPlaylist it would cmd
* call mp_gamemode which parses the gamemode specific part of the playlist..
*/
v_Playlists_Parse(m_Server.playlist.c_str());
mp_gamemode->SetValue(m_Server.playlist.c_str());
ProcessCommand(Format("%s \"%s\"", bChangeLevel ? "changelevel" : "map", m_Server.map.c_str()).c_str());
}
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// Purpose: connects to specified server // Purpose: connects to specified server
// Input : &svIp - // Input : &svIp -
@ -101,7 +78,9 @@ void CServerListManager::ConnectToServer(const string& svIp, const int nPort, co
{ {
NET_SetKey(svNetKey); NET_SetKey(svNetKey);
} }
ProcessCommand(Format("%s \"[%s]:%i\"", "connect", svIp.c_str(), nPort).c_str());
const string command = Format("%s \"[%s]:%i\"", "connect", svIp.c_str(), nPort);
Cbuf_AddText(Cbuf_GetCurrentPlayer(), command.c_str(), cmd_source_t::kCommandSrcCode);
} }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
@ -124,16 +103,9 @@ void CServerListManager::ConnectToServer(const string& svServer, const string& s
{ {
NET_SetKey(svNetKey); NET_SetKey(svNetKey);
} }
ProcessCommand(Format("%s \"%s\"", "connect", svServer.c_str()).c_str());
const string command = Format("%s \"%s\"", "connect", svServer.c_str()).c_str();
Cbuf_AddText(Cbuf_GetCurrentPlayer(), command.c_str(), cmd_source_t::kCommandSrcCode);
} }
//----------------------------------------------------------------------------- CServerListManager g_ServerListManager;
// Purpose: executes submitted commands in a separate thread
// Input : *pszCommand -
//-----------------------------------------------------------------------------
void CServerListManager::ProcessCommand(const char* pszCommand) const
{
Cbuf_AddText(Cbuf_GetCurrentPlayer(), pszCommand, cmd_source_t::kCommandSrcCode);
}
CServerListManager g_ServerListManager;

View File

@ -2,40 +2,20 @@
#define LISTMANAGER_H #define LISTMANAGER_H
#include <networksystem/serverlisting.h> #include <networksystem/serverlisting.h>
enum EHostStatus_t
{
NOT_HOSTING,
HOSTING
};
enum EServerVisibility_t
{
OFFLINE,
HIDDEN,
PUBLIC
};
class CServerListManager class CServerListManager
{ {
public: public:
CServerListManager(); CServerListManager();
size_t RefreshServerList(string& svMessage); bool RefreshServerList(string& outMessage, size_t& numServers);
void ClearServerList(void); void ClearServerList(void);
void LaunchServer(const bool bChangeLevel) const;
void ConnectToServer(const string& svIp, const int nPort, const string& svNetKey) const; void ConnectToServer(const string& svIp, const int nPort, const string& svNetKey) const;
void ConnectToServer(const string& svServer, const string& svNetKey) const; void ConnectToServer(const string& svServer, const string& svNetKey) const;
void ProcessCommand(const char* pszCommand) const; // TODO: make private!
EHostStatus_t m_HostingStatus;
EServerVisibility_t m_ServerVisibility;
NetGameServer_t m_Server;
vector<NetGameServer_t> m_vServerList; vector<NetGameServer_t> m_vServerList;
mutable CThreadFastMutex m_Mutex;
mutable std::mutex m_Mutex;
}; };
extern CServerListManager g_ServerListManager; extern CServerListManager g_ServerListManager;

View File

@ -46,12 +46,10 @@ static bool IsServerListingValid(const rapidjson::Value& value)
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// Purpose: gets a vector of hosted servers. // Purpose: gets a vector of hosted servers.
// Input : &outMessage - // Input : &outMessage -
// Output : vector<NetGameServer_t> // Output : true on success, false on failure.
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
vector<NetGameServer_t> CPylon::GetServerList(string& outMessage) const bool CPylon::GetServerList(vector<NetGameServer_t>& outServerList, string& outMessage) const
{ {
vector<NetGameServer_t> vecServers;
rapidjson::Document requestJson; rapidjson::Document requestJson;
requestJson.SetObject(); requestJson.SetObject();
requestJson.AddMember("version", SDK_VERSION, requestJson.GetAllocator()); requestJson.AddMember("version", SDK_VERSION, requestJson.GetAllocator());
@ -65,13 +63,13 @@ vector<NetGameServer_t> CPylon::GetServerList(string& outMessage) const
if (!SendRequest("/servers", requestJson, responseJson, if (!SendRequest("/servers", requestJson, responseJson,
outMessage, status, "server list error")) outMessage, status, "server list error"))
{ {
return vecServers; return false;
} }
if (!responseJson.HasMember("servers")) if (!responseJson.HasMember("servers"))
{ {
outMessage = Format("Invalid response with status: %d", int(status)); outMessage = Format("Invalid response with status: %d", int(status));
return vecServers; return false;
} }
const rapidjson::Value& servers = responseJson["servers"]; const rapidjson::Value& servers = responseJson["servers"];
@ -87,7 +85,7 @@ vector<NetGameServer_t> CPylon::GetServerList(string& outMessage) const
continue; continue;
} }
vecServers.push_back( outServerList.push_back(
NetGameServer_t NetGameServer_t
{ {
obj["name"].GetString(), obj["name"].GetString(),
@ -107,7 +105,7 @@ vector<NetGameServer_t> CPylon::GetServerList(string& outMessage) const
); );
} }
return vecServers; return true;
} }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
@ -522,7 +520,7 @@ bool CPylon::QueryServer(const char* endpoint, const char* request,
string finalUrl; string finalUrl;
CURLFormatUrl(finalUrl, hostName, endpoint); CURLFormatUrl(finalUrl, hostName, endpoint);
finalUrl += Format("?language=%s", this->m_Language.c_str()); finalUrl += Format("?language=%s", this->GetLanguage().c_str());
CURLParams params; CURLParams params;

View File

@ -18,9 +18,9 @@ struct MSEulaData_t
class CPylon class CPylon
{ {
public: public:
CPylon() { m_Language = g_LanguageNames[0]; } CPylon() { SetLanguage(g_LanguageNames[0]); }
vector<NetGameServer_t> GetServerList(string& outMessage) const; bool GetServerList(vector<NetGameServer_t>& outServerList, string& outMessage) const;
bool GetServerByToken(NetGameServer_t& slOutServer, string& outMessage, const string& svToken) const; bool GetServerByToken(NetGameServer_t& slOutServer, string& outMessage, const string& svToken) const;
bool PostServerHost(string& outMessage, string& svOutToken, string& outHostIp, const NetGameServer_t& netGameServer) const; bool PostServerHost(string& outMessage, string& svOutToken, string& outHostIp, const NetGameServer_t& netGameServer) const;
@ -38,22 +38,19 @@ public:
bool SendRequest(const char* endpoint, const rapidjson::Document& requestJson, rapidjson::Document& responseJson, string& outMessage, CURLINFO& status, const char* errorText = nullptr, const bool checkEula = true) const; bool SendRequest(const char* endpoint, const rapidjson::Document& requestJson, rapidjson::Document& responseJson, string& outMessage, CURLINFO& status, const char* errorText = nullptr, const bool checkEula = true) const;
bool QueryServer(const char* endpoint, const char* request, string& outResponse, string& outMessage, CURLINFO& outStatus) const; bool QueryServer(const char* endpoint, const char* request, string& outResponse, string& outMessage, CURLINFO& outStatus) const;
inline void SetCurrentToken(const string& token) { m_Token = token; } inline void SetLanguage(const char* lang)
inline const string& GetCurrentToken() const { return m_Token; } {
AUTO_LOCK(m_StringMutex);
inline void SetCurrentError(const string& error) { m_ErrorMsg = error; } m_Language = lang;
inline const string& GetCurrentError() const { return m_ErrorMsg; } };
inline const string& GetLanguage() const
inline void SetHostIP(const string& ip) { m_HostIP = ip; }; {
inline const string& GetHostIP() const { return m_HostIP; }; AUTO_LOCK(m_StringMutex);
return m_Language;
inline void SetLanguage(const char* lang) { m_Language = lang; }; };
inline const string& GetLanguage() const { return m_Language; };
private: private:
string m_Token;
string m_ErrorMsg;
string m_HostIP;
string m_Language; string m_Language;
mutable CThreadFastMutex m_StringMutex;
}; };
extern CPylon g_MasterServer; extern CPylon g_MasterServer;