diff --git a/r5dev/common/callback.cpp b/r5dev/common/callback.cpp index aef8e5e7..1a0ef548 100644 --- a/r5dev/common/callback.cpp +++ b/r5dev/common/callback.cpp @@ -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); diff --git a/r5dev/engine/client/client.cpp b/r5dev/engine/client/client.cpp index f53c2b28..496e7afe 100644 --- a/r5dev/engine/client/client.cpp +++ b/r5dev/engine/client/client.cpp @@ -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"); diff --git a/r5dev/engine/host_state.cpp b/r5dev/engine/host_state.cpp index 12f580c5..579c1918 100644 --- a/r5dev/engine/host_state.cpp +++ b/r5dev/engine/host_state.cpp @@ -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::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::system_clock::now().time_since_epoch() - ).count() - }; - - std::thread(&HostState_KeepAlive, netGameServer).detach(); + HostState_KeepAlive(); pylonTimer.Start(); } #endif // DEDICATED diff --git a/r5dev/game/client/vscript_client.cpp b/r5dev/game/client/vscript_client.cpp index 38fba9a3..bf420316 100644 --- a/r5dev/game/client/vscript_client.cpp +++ b/r5dev/game/client/vscript_client.cpp @@ -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(iCount)); return SQ_OK; @@ -157,7 +159,7 @@ namespace VScriptCode //----------------------------------------------------------------------------- SQRESULT GetServerName(HSQUIRRELVM v) { - std::lock_guard 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 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 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 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 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 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 l(g_ServerListManager.m_Mutex); + AUTO_LOCK(g_ServerListManager.m_Mutex); SQInteger iServer = sq_getinteger(v, 1); if (!Script_CheckServerIndex(v, iServer)) diff --git a/r5dev/game/server/vscript_server.cpp b/r5dev/game/server/vscript_server.cpp index e82d815b..43ad588b 100644 --- a/r5dev/game/server/vscript_server.cpp +++ b/r5dev/game/server/vscript_server.cpp @@ -16,7 +16,7 @@ #include "vscript_server.h" #include -#include +#include /* ===================== @@ -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(sq_getinteger(v, 5)); + ServerVisibility_e serverVisibility = static_cast(sq_getinteger(v, 5)); if (!VALID_CHARSTAR(serverName) || !VALID_CHARSTAR(serverMapName) || @@ -61,16 +61,16 @@ namespace VScriptCode } // Adjust browser settings. - std::lock_guard 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; diff --git a/r5dev/gameui/IBrowser.cpp b/r5dev/gameui/IBrowser.cpp index cbf80c3d..91cc775a 100644 --- a/r5dev/gameui/IBrowser.cpp +++ b/r5dev/gameui/IBrowser.cpp @@ -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 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 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 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) diff --git a/r5dev/networksystem/CMakeLists.txt b/r5dev/networksystem/CMakeLists.txt index c379fbe6..70e4a4f4 100644 --- a/r5dev/networksystem/CMakeLists.txt +++ b/r5dev/networksystem/CMakeLists.txt @@ -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" diff --git a/r5dev/networksystem/hostmanager.cpp b/r5dev/networksystem/hostmanager.cpp new file mode 100644 index 00000000..94125933 --- /dev/null +++ b/r5dev/networksystem/hostmanager.cpp @@ -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; diff --git a/r5dev/networksystem/hostmanager.h b/r5dev/networksystem/hostmanager.h new file mode 100644 index 00000000..330cad61 --- /dev/null +++ b/r5dev/networksystem/hostmanager.h @@ -0,0 +1,55 @@ +#ifndef HOSTMANAGER_H +#define HOSTMANAGER_H +#include + +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 diff --git a/r5dev/networksystem/listmanager.cpp b/r5dev/networksystem/listmanager.cpp index 06949f85..14f4dcba 100644 --- a/r5dev/networksystem/listmanager.cpp +++ b/r5dev/networksystem/listmanager.cpp @@ -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 vServerList = g_MasterServer.GetServerList(svMessage); - std::lock_guard l(m_Mutex); - m_vServerList = vServerList; + vector 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 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; \ No newline at end of file +CServerListManager g_ServerListManager; diff --git a/r5dev/networksystem/listmanager.h b/r5dev/networksystem/listmanager.h index dc2b0870..843564fb 100644 --- a/r5dev/networksystem/listmanager.h +++ b/r5dev/networksystem/listmanager.h @@ -2,40 +2,20 @@ #define LISTMANAGER_H #include -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 m_vServerList; - - mutable std::mutex m_Mutex; + mutable CThreadFastMutex m_Mutex; }; extern CServerListManager g_ServerListManager; diff --git a/r5dev/networksystem/pylon.cpp b/r5dev/networksystem/pylon.cpp index 184c015d..f23088dd 100644 --- a/r5dev/networksystem/pylon.cpp +++ b/r5dev/networksystem/pylon.cpp @@ -46,12 +46,10 @@ static bool IsServerListingValid(const rapidjson::Value& value) //----------------------------------------------------------------------------- // Purpose: gets a vector of hosted servers. // Input : &outMessage - -// Output : vector +// Output : true on success, false on failure. //----------------------------------------------------------------------------- -vector CPylon::GetServerList(string& outMessage) const +bool CPylon::GetServerList(vector& outServerList, string& outMessage) const { - vector vecServers; - rapidjson::Document requestJson; requestJson.SetObject(); requestJson.AddMember("version", SDK_VERSION, requestJson.GetAllocator()); @@ -65,13 +63,13 @@ vector 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 CPylon::GetServerList(string& outMessage) const continue; } - vecServers.push_back( + outServerList.push_back( NetGameServer_t { obj["name"].GetString(), @@ -107,7 +105,7 @@ vector 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; diff --git a/r5dev/networksystem/pylon.h b/r5dev/networksystem/pylon.h index 4fce78e5..e208bf75 100644 --- a/r5dev/networksystem/pylon.h +++ b/r5dev/networksystem/pylon.h @@ -18,9 +18,9 @@ struct MSEulaData_t class CPylon { public: - CPylon() { m_Language = g_LanguageNames[0]; } + CPylon() { SetLanguage(g_LanguageNames[0]); } - vector GetServerList(string& outMessage) const; + bool GetServerList(vector& 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;