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 (Localize_IsLanguageSupported(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];
}
}
pConVarRef->SetValue(pNewString);

View File

@ -14,6 +14,7 @@
#include "engine/server/server.h"
#include "engine/client/client.h"
#ifndef CLIENT_DLL
#include "networksystem/hostmanager.h"
#include "jwt/include/decode.h"
#include "mbedtls/include/mbedtls/sha256.h"
#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",
(NucleusID_t)this->m_DataBlock.userData,
playerName,
g_MasterServer.GetHostIP().c_str());
g_ServerHostManager.GetHostIP().c_str());
if (idLen < 0)
ERROR_AND_RETURN("Session ID stitching failed");

View File

@ -10,6 +10,7 @@
#include "tier0/jobthread.h"
#include "tier0/commandline.h"
#include "tier0/fasttimer.h"
#include "tier0/frametask.h"
#include "tier1/cvar.h"
#include "tier1/NetAdr.h"
#include "tier2/socketcreator.h"
@ -45,6 +46,7 @@
#include "networksystem/pylon.h"
#ifndef CLIENT_DLL
#include "networksystem/bansystem.h"
#include "networksystem/hostmanager.h"
#endif // !CLIENT_DLL
#include "networksystem/listmanager.h"
#include "public/edict.h"
@ -53,58 +55,85 @@
#endif // !CLIENT_DLL
#include "game/shared/vscript_shared.h"
#ifdef DEDICATED
static ConVar hostdesc("hostdesc", "", FCVAR_RELEASE, "Host game server description.");
#endif // DEDICATED
#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_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).");
#endif // !CLIENT_DLL
#ifdef DEDICATED
static ConVar hostdesc("hostdesc", "", FCVAR_RELEASE, "Host game server description.");
//-----------------------------------------------------------------------------
// Purpose: Send keep alive request to Pylon Master Server.
// Input : &netGameServer -
// 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.
{
return false;
return;
}
string errorMsg;
string hostToken;
string hostIp;
const bool result = g_MasterServer.PostServerHost(errorMsg, hostToken, hostIp, netGameServer);
if (!result)
const NetGameServer_t gameServer
{
if (!errorMsg.empty() && g_MasterServer.GetCurrentError().compare(errorMsg) != NULL)
{
g_MasterServer.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_MasterServer.GetCurrentToken().compare(hostToken) != NULL)
{
g_MasterServer.SetCurrentToken(hostToken);
Msg(eDLL_T::SERVER, "Published server with token: %s'%s%s%s'\n",
g_svReset, g_svGreyB,
hostToken.c_str(), g_svReset);
}
}
hostname->GetString(),
hostdesc.GetString(),
sv_pylonVisibility.GetInt() == ServerVisibility_e::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()
};
if (hostIp.length() != 0)
g_MasterServer.SetHostIP(hostIp);
std::thread request([&, gameServer]
{
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
@ -336,32 +365,13 @@ void CHostState::Think(void) const
if (sv_globalBanlist.GetBool() &&
banListTimer.GetDurationInProgress().GetSeconds() > sv_banlistRefreshRate.GetFloat())
{
SV_CheckForBan();
SV_CheckClientsForBan();
banListTimer.Start();
}
#ifdef DEDICATED
if (pylonTimer.GetDurationInProgress().GetSeconds() > sv_pylonRefreshRate.GetFloat())
{
const NetGameServer_t netGameServer
{
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();
HostState_KeepAlive();
pylonTimer.Start();
}
#endif // DEDICATED

View File

@ -83,8 +83,10 @@ namespace VScriptCode
SQRESULT RefreshServerList(HSQUIRRELVM v)
{
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));
return SQ_OK;
@ -157,7 +159,7 @@ namespace VScriptCode
//-----------------------------------------------------------------------------
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);
if (!Script_CheckServerIndex(v, iServer))
@ -176,7 +178,7 @@ namespace VScriptCode
//-----------------------------------------------------------------------------
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);
if (!Script_CheckServerIndex(v, iServer))
@ -195,7 +197,7 @@ namespace VScriptCode
//-----------------------------------------------------------------------------
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);
if (!Script_CheckServerIndex(v, iServer))
@ -214,7 +216,7 @@ namespace VScriptCode
//-----------------------------------------------------------------------------
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);
if (!Script_CheckServerIndex(v, iServer))
@ -233,7 +235,7 @@ namespace VScriptCode
//-----------------------------------------------------------------------------
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);
if (!Script_CheckServerIndex(v, iServer))
@ -252,7 +254,7 @@ namespace VScriptCode
//-----------------------------------------------------------------------------
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);
if (!Script_CheckServerIndex(v, iServer))
@ -372,7 +374,7 @@ namespace VScriptCode
//-----------------------------------------------------------------------------
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);
if (!Script_CheckServerIndex(v, iServer))

View File

@ -16,7 +16,7 @@
#include "vscript_server.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* serverMapName = sq_getstring(v, 3);
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) ||
!VALID_CHARSTAR(serverMapName) ||
@ -61,16 +61,16 @@ namespace VScriptCode
}
// 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;
g_ServerListManager.m_Server.description = serverDescription;
g_ServerListManager.m_Server.map = serverMapName;
g_ServerListManager.m_Server.playlist = serverPlaylist;
g_ServerListManager.m_ServerVisibility = eServerVisibility;
details.name = serverName;
details.description = serverDescription;
details.map = serverMapName;
details.playlist = serverPlaylist;
// Launch server.
g_ServerListManager.LaunchServer(g_pServer->IsActive());
g_ServerHostManager.SetVisibility(serverVisibility);
g_ServerHostManager.LaunchServer(g_pServer->IsActive());
return SQ_OK;

View File

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

View File

@ -6,6 +6,8 @@ start_sources()
add_sources( SOURCE_GROUP "Private"
"bansystem.cpp"
"bansystem.h"
"hostmanager.cpp"
"hostmanager.h"
"listmanager.cpp"
"listmanager.h"
"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:
//-----------------------------------------------------------------------------
CServerListManager::CServerListManager(void)
: m_HostingStatus(EHostStatus_t::NOT_HOSTING)
, m_ServerVisibility(EServerVisibility_t::OFFLINE)
{
}
//-----------------------------------------------------------------------------
// Purpose: get server list from pylon
// Input : &svMessage -
// Output : amount of servers found
// Input : &outMessage -
// &numServers -
// Output : true on success, false otherwise
//-----------------------------------------------------------------------------
size_t CServerListManager::RefreshServerList(string& svMessage)
bool CServerListManager::RefreshServerList(string& outMessage, size_t& numServers)
{
ClearServerList();
vector<NetGameServer_t> vServerList = g_MasterServer.GetServerList(svMessage);
std::lock_guard<std::mutex> l(m_Mutex);
m_vServerList = vServerList;
vector<NetGameServer_t> serverList;
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)
{
std::lock_guard<std::mutex> l(m_Mutex);
AUTO_LOCK(m_Mutex);
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
// Input : &svIp -
@ -101,7 +78,9 @@ void CServerListManager::ConnectToServer(const string& svIp, const int nPort, co
{
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);
}
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);
}
//-----------------------------------------------------------------------------
// 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;
CServerListManager g_ServerListManager;

View File

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

View File

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

View File

@ -18,9 +18,9 @@ struct MSEulaData_t
class CPylon
{
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 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 QueryServer(const char* endpoint, const char* request, string& outResponse, string& outMessage, CURLINFO& outStatus) const;
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; };
inline void SetLanguage(const char* lang) { m_Language = lang; };
inline const string& GetLanguage() const { return m_Language; };
inline void SetLanguage(const char* lang)
{
AUTO_LOCK(m_StringMutex);
m_Language = lang;
};
inline const string& GetLanguage() const
{
AUTO_LOCK(m_StringMutex);
return m_Language;
};
private:
string m_Token;
string m_ErrorMsg;
string m_HostIP;
string m_Language;
mutable CThreadFastMutex m_StringMutex;
};
extern CPylon g_MasterServer;