2022-02-27 07:53:44 -05:00
|
|
|
#include <STDInclude.hpp>
|
2022-12-26 07:07:24 -05:00
|
|
|
#include "Discovery.hpp"
|
|
|
|
#include "ServerList.hpp"
|
|
|
|
#include "UIFeeder.hpp"
|
2017-01-16 11:42:50 -05:00
|
|
|
|
2022-11-26 12:38:34 -05:00
|
|
|
#include <version.hpp>
|
|
|
|
|
2017-01-16 11:42:50 -05:00
|
|
|
namespace Components
|
|
|
|
{
|
|
|
|
bool ServerList::SortAsc = true;
|
2022-12-29 06:34:43 -05:00
|
|
|
int ServerList::SortKey = static_cast<std::underlying_type_t<Column>>(Column::Ping);
|
2017-01-16 11:42:50 -05:00
|
|
|
|
|
|
|
unsigned int ServerList::CurrentServer = 0;
|
|
|
|
ServerList::Container ServerList::RefreshContainer;
|
|
|
|
|
|
|
|
std::vector<ServerList::ServerInfo> ServerList::OnlineList;
|
|
|
|
std::vector<ServerList::ServerInfo> ServerList::OfflineList;
|
|
|
|
std::vector<ServerList::ServerInfo> ServerList::FavouriteList;
|
|
|
|
|
|
|
|
std::vector<unsigned int> ServerList::VisibleList;
|
|
|
|
|
2022-04-12 17:15:50 -04:00
|
|
|
Dvar::Var ServerList::UIServerSelected;
|
|
|
|
Dvar::Var ServerList::UIServerSelectedMap;
|
|
|
|
Dvar::Var ServerList::NETServerQueryLimit;
|
|
|
|
Dvar::Var ServerList::NETServerFrames;
|
|
|
|
|
2022-10-15 16:31:16 -04:00
|
|
|
bool ServerList::UseMasterServer = true;
|
2022-05-03 01:17:31 -04:00
|
|
|
|
2017-01-16 11:42:50 -05:00
|
|
|
std::vector<ServerList::ServerInfo>* ServerList::GetList()
|
|
|
|
{
|
2022-12-29 06:34:43 -05:00
|
|
|
if (IsOnlineList())
|
2017-01-16 11:42:50 -05:00
|
|
|
{
|
2022-12-29 06:34:43 -05:00
|
|
|
return &OnlineList;
|
2017-01-16 11:42:50 -05:00
|
|
|
}
|
2022-12-29 06:34:43 -05:00
|
|
|
|
|
|
|
if (IsOfflineList())
|
2017-01-16 11:42:50 -05:00
|
|
|
{
|
2022-12-29 06:34:43 -05:00
|
|
|
return &OfflineList;
|
2017-01-16 11:42:50 -05:00
|
|
|
}
|
2022-12-29 06:34:43 -05:00
|
|
|
|
|
|
|
if (IsFavouriteList())
|
2017-01-16 11:42:50 -05:00
|
|
|
{
|
2022-12-29 06:34:43 -05:00
|
|
|
return &FavouriteList;
|
2017-01-16 11:42:50 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ServerList::IsFavouriteList()
|
|
|
|
{
|
2022-12-29 06:34:43 -05:00
|
|
|
return (*Game::ui_netSource)->current.integer == 2;
|
2017-01-16 11:42:50 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
bool ServerList::IsOfflineList()
|
|
|
|
{
|
2022-12-29 06:34:43 -05:00
|
|
|
return (*Game::ui_netSource)->current.integer == 0;
|
2017-01-16 11:42:50 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
bool ServerList::IsOnlineList()
|
|
|
|
{
|
2022-12-29 06:34:43 -05:00
|
|
|
return (*Game::ui_netSource)->current.integer == 1;
|
2017-01-16 11:42:50 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
unsigned int ServerList::GetServerCount()
|
|
|
|
{
|
2022-12-29 06:34:43 -05:00
|
|
|
return VisibleList.size();
|
2017-01-16 11:42:50 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
const char* ServerList::GetServerText(unsigned int index, int column)
|
|
|
|
{
|
2022-12-29 06:34:43 -05:00
|
|
|
auto* info = GetServer(index);
|
2017-01-16 11:42:50 -05:00
|
|
|
|
|
|
|
if (info)
|
|
|
|
{
|
2022-12-29 06:34:43 -05:00
|
|
|
return GetServerInfoText(info, column);
|
2017-01-16 11:42:50 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
return "";
|
|
|
|
}
|
|
|
|
|
2022-12-29 06:34:43 -05:00
|
|
|
const char* ServerList::GetServerInfoText(ServerInfo* server, int column, bool sorting)
|
2017-01-16 11:42:50 -05:00
|
|
|
{
|
|
|
|
if (!server) return "";
|
|
|
|
|
2022-12-29 06:34:43 -05:00
|
|
|
switch (static_cast<Column>(column))
|
2017-01-16 11:42:50 -05:00
|
|
|
{
|
2017-06-14 06:06:04 -04:00
|
|
|
case Column::Password:
|
|
|
|
{
|
2022-08-15 05:56:00 -04:00
|
|
|
return (server->password ? ":icon_locked:" : "");
|
2017-06-14 06:06:04 -04:00
|
|
|
}
|
2017-01-16 11:42:50 -05:00
|
|
|
|
2017-06-14 06:06:04 -04:00
|
|
|
case Column::Matchtype:
|
|
|
|
{
|
|
|
|
return ((server->matchType == 1) ? "P" : "M");
|
|
|
|
}
|
2017-01-16 11:42:50 -05:00
|
|
|
|
2022-08-14 15:39:53 -04:00
|
|
|
case Column::AimAssist:
|
|
|
|
{
|
2022-08-15 05:56:00 -04:00
|
|
|
return ((server->aimassist == 1) ? ":headshot:" : "");
|
2022-08-14 15:39:53 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
case Column::VoiceChat:
|
|
|
|
{
|
2022-08-15 05:56:00 -04:00
|
|
|
return ((server->voice == 1) ? ":voice_on:" : "");
|
2022-08-14 15:39:53 -04:00
|
|
|
}
|
|
|
|
|
2017-06-14 06:06:04 -04:00
|
|
|
case Column::Hostname:
|
|
|
|
{
|
|
|
|
return server->hostname.data();
|
|
|
|
}
|
2017-01-16 11:42:50 -05:00
|
|
|
|
2017-06-14 06:06:04 -04:00
|
|
|
case Column::Mapname:
|
|
|
|
{
|
|
|
|
if (server->svRunning)
|
2017-01-16 11:42:50 -05:00
|
|
|
{
|
2017-06-14 06:06:04 -04:00
|
|
|
if (!sorting && !Maps::CheckMapInstalled(server->mapname.data()))
|
2017-01-16 11:42:50 -05:00
|
|
|
{
|
2017-06-14 06:06:04 -04:00
|
|
|
return Utils::String::VA("^1%s", Game::UI_LocalizeMapName(server->mapname.data()));
|
2017-01-16 11:42:50 -05:00
|
|
|
}
|
2022-12-29 06:34:43 -05:00
|
|
|
|
2017-06-14 06:06:04 -04:00
|
|
|
return Game::UI_LocalizeMapName(server->mapname.data());
|
2017-01-16 11:42:50 -05:00
|
|
|
}
|
2022-12-29 06:34:43 -05:00
|
|
|
|
|
|
|
return Utils::String::VA("^3%s", Game::UI_LocalizeMapName(server->mapname.data()));
|
2017-06-14 06:06:04 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
case Column::Players:
|
|
|
|
{
|
|
|
|
return Utils::String::VA("%i/%i (%i)", server->clients, server->maxClients, server->bots);
|
|
|
|
}
|
|
|
|
|
|
|
|
case Column::Gametype:
|
|
|
|
{
|
|
|
|
return Game::UI_LocalizeGameType(server->gametype.data());
|
|
|
|
}
|
2017-01-16 11:42:50 -05:00
|
|
|
|
2017-06-14 06:06:04 -04:00
|
|
|
case Column::Mod:
|
|
|
|
{
|
2022-12-29 06:34:43 -05:00
|
|
|
if (Utils::String::StartsWith(server->mod, "mods/"))
|
2017-01-16 11:42:50 -05:00
|
|
|
{
|
2022-12-29 06:34:43 -05:00
|
|
|
// Can point to '\0' which is fine
|
2017-06-14 06:06:04 -04:00
|
|
|
return (server->mod.data() + 5);
|
2017-01-16 11:42:50 -05:00
|
|
|
}
|
|
|
|
|
2017-06-14 06:06:04 -04:00
|
|
|
return "";
|
|
|
|
}
|
2017-01-16 11:42:50 -05:00
|
|
|
|
2017-06-14 06:06:04 -04:00
|
|
|
case Column::Ping:
|
|
|
|
{
|
|
|
|
if (server->ping < 75) // Below this is a good ping
|
|
|
|
{
|
|
|
|
return Utils::String::VA("^2%i", server->ping);
|
2017-01-16 11:42:50 -05:00
|
|
|
}
|
2022-12-29 06:34:43 -05:00
|
|
|
|
|
|
|
if (server->ping < 150) // Below this is a medium ping
|
2017-01-16 11:42:50 -05:00
|
|
|
{
|
2017-06-14 06:06:04 -04:00
|
|
|
return Utils::String::VA("^3%i", server->ping);
|
2017-01-16 11:42:50 -05:00
|
|
|
}
|
2017-06-14 06:06:04 -04:00
|
|
|
|
2022-12-29 06:34:43 -05:00
|
|
|
return Utils::String::VA("^1%i", server->ping);
|
|
|
|
}
|
2017-06-14 06:06:04 -04:00
|
|
|
default:
|
|
|
|
{
|
|
|
|
break;
|
2022-12-29 06:34:43 -05:00
|
|
|
}
|
2017-01-16 11:42:50 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
return "";
|
|
|
|
}
|
|
|
|
|
|
|
|
void ServerList::SelectServer(unsigned int index)
|
|
|
|
{
|
2022-12-29 06:34:43 -05:00
|
|
|
CurrentServer = index;
|
2017-01-16 11:42:50 -05:00
|
|
|
|
2022-12-29 06:34:43 -05:00
|
|
|
auto* info = GetCurrentServer();
|
2017-01-16 11:42:50 -05:00
|
|
|
|
|
|
|
if (info)
|
|
|
|
{
|
2022-12-29 06:34:43 -05:00
|
|
|
UIServerSelected.set(true);
|
|
|
|
UIServerSelectedMap.set(info->mapname);
|
2017-01-16 11:42:50 -05:00
|
|
|
Dvar::Var("ui_serverSelectedGametype").set(info->gametype);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2022-12-29 06:34:43 -05:00
|
|
|
UIServerSelected.set(false);
|
2017-01-16 11:42:50 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-08-24 10:38:14 -04:00
|
|
|
void ServerList::UpdateVisibleList([[maybe_unused]] const UIScript::Token& token, [[maybe_unused]] const Game::uiInfo_s* info)
|
2017-01-16 11:42:50 -05:00
|
|
|
{
|
2022-12-29 06:34:43 -05:00
|
|
|
auto* list = GetList();
|
2017-01-16 11:42:50 -05:00
|
|
|
if (!list) return;
|
|
|
|
|
2022-12-29 06:34:43 -05:00
|
|
|
std::vector tempList(*list);
|
2017-01-16 11:42:50 -05:00
|
|
|
|
|
|
|
if (tempList.empty())
|
|
|
|
{
|
2022-12-29 06:34:43 -05:00
|
|
|
Refresh(UIScript::Token(), info);
|
2017-01-16 11:42:50 -05:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
list->clear();
|
|
|
|
|
2022-12-29 06:34:43 -05:00
|
|
|
std::lock_guard _(RefreshContainer.mutex);
|
2017-01-16 11:42:50 -05:00
|
|
|
|
2022-12-29 06:34:43 -05:00
|
|
|
RefreshContainer.sendCount = 0;
|
|
|
|
RefreshContainer.sentCount = 0;
|
2017-01-16 11:42:50 -05:00
|
|
|
|
2017-05-30 14:31:00 -04:00
|
|
|
for (auto& server : tempList)
|
2017-01-16 11:42:50 -05:00
|
|
|
{
|
2022-12-29 06:34:43 -05:00
|
|
|
InsertRequest(server.addr);
|
2017-01-16 11:42:50 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-08-24 10:38:14 -04:00
|
|
|
void ServerList::RefreshVisibleList([[maybe_unused]] const UIScript::Token& token, [[maybe_unused]] const Game::uiInfo_s* info)
|
2022-05-07 09:23:26 -04:00
|
|
|
{
|
2022-12-29 06:34:43 -05:00
|
|
|
RefreshVisibleListInternal(UIScript::Token(), info);
|
2022-05-07 09:23:26 -04:00
|
|
|
}
|
|
|
|
|
2022-08-24 10:38:14 -04:00
|
|
|
void ServerList::RefreshVisibleListInternal([[maybe_unused]] const UIScript::Token& token, [[maybe_unused]] const Game::uiInfo_s* info, bool refresh)
|
2017-01-16 11:42:50 -05:00
|
|
|
{
|
2022-12-29 06:34:43 -05:00
|
|
|
Game::Dvar_SetBoolByName("ui_serverSelected", false);
|
2017-01-16 11:42:50 -05:00
|
|
|
|
2022-12-29 06:34:43 -05:00
|
|
|
VisibleList.clear();
|
2017-01-16 11:42:50 -05:00
|
|
|
|
2022-12-29 06:34:43 -05:00
|
|
|
auto* list = GetList();
|
2017-01-16 11:42:50 -05:00
|
|
|
if (!list) return;
|
|
|
|
|
2022-05-07 09:23:26 -04:00
|
|
|
if (refresh)
|
|
|
|
{
|
2022-12-29 06:34:43 -05:00
|
|
|
Refresh(UIScript::Token(), info);
|
2022-05-07 09:23:26 -04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-12-29 06:34:43 -05:00
|
|
|
auto ui_browserShowFull = Dvar::Var("ui_browserShowFull").get<bool>();
|
|
|
|
auto ui_browserShowEmpty = Dvar::Var("ui_browserShowEmpty").get<bool>();
|
|
|
|
auto ui_browserShowHardcore = Dvar::Var("ui_browserKillcam").get<int>();
|
|
|
|
auto ui_browserShowPassword = Dvar::Var("ui_browserShowPassword").get<int>();
|
|
|
|
auto ui_browserMod = Dvar::Var("ui_browserMod").get<int>();
|
|
|
|
auto ui_joinGametype = (*Game::ui_joinGametype)->current.integer;
|
2017-01-16 11:42:50 -05:00
|
|
|
|
|
|
|
for (unsigned int i = 0; i < list->size(); ++i)
|
|
|
|
{
|
2022-08-24 10:38:14 -04:00
|
|
|
auto* serverInfo = &(*list)[i];
|
2017-01-16 11:42:50 -05:00
|
|
|
|
|
|
|
// Filter full servers
|
2022-08-24 10:38:14 -04:00
|
|
|
if (!ui_browserShowFull && serverInfo->clients >= serverInfo->maxClients) continue;
|
2017-01-16 11:42:50 -05:00
|
|
|
|
|
|
|
// Filter empty servers
|
2022-08-24 10:38:14 -04:00
|
|
|
if (!ui_browserShowEmpty && serverInfo->clients <= 0) continue;
|
2017-01-16 11:42:50 -05:00
|
|
|
|
|
|
|
// Filter hardcore servers
|
2022-08-24 10:38:14 -04:00
|
|
|
if ((ui_browserShowHardcore == 0 && serverInfo->hardcore) || (ui_browserShowHardcore == 1 && !serverInfo->hardcore)) continue;
|
2017-01-16 11:42:50 -05:00
|
|
|
|
|
|
|
// Filter servers with password
|
2022-08-24 10:38:14 -04:00
|
|
|
if ((ui_browserShowPassword == 0 && serverInfo->password) || (ui_browserShowPassword == 1 && !serverInfo->password)) continue;
|
2017-01-16 11:42:50 -05:00
|
|
|
|
|
|
|
// Don't show modded servers
|
2022-12-29 06:34:43 -05:00
|
|
|
if ((ui_browserMod == 0 && static_cast<int>(serverInfo->mod.size())) || (ui_browserMod == 1 && serverInfo->mod.empty())) continue;
|
2017-01-16 11:42:50 -05:00
|
|
|
|
|
|
|
// Filter by gametype
|
2022-08-24 10:38:14 -04:00
|
|
|
if (ui_joinGametype > 0 && (ui_joinGametype - 1) < *Game::gameTypeCount && Game::gameTypes[(ui_joinGametype - 1)].gameType != serverInfo->gametype) continue;
|
2017-01-16 11:42:50 -05:00
|
|
|
|
2022-12-29 06:34:43 -05:00
|
|
|
VisibleList.push_back(i);
|
2017-01-16 11:42:50 -05:00
|
|
|
}
|
|
|
|
|
2022-12-29 06:34:43 -05:00
|
|
|
SortList();
|
2017-01-16 11:42:50 -05:00
|
|
|
}
|
|
|
|
|
2022-08-24 10:38:14 -04:00
|
|
|
void ServerList::Refresh([[maybe_unused]] const UIScript::Token& token, [[maybe_unused]] const Game::uiInfo_s* info)
|
2017-01-16 11:42:50 -05:00
|
|
|
{
|
|
|
|
Dvar::Var("ui_serverSelected").set(false);
|
2017-03-10 20:11:32 -05:00
|
|
|
//Localization::Set("MPUI_SERVERQUERIED", "Sent requests: 0/0");
|
2017-01-16 11:42:50 -05:00
|
|
|
|
2022-12-29 06:34:43 -05:00
|
|
|
#if 0
|
|
|
|
OnlineList.clear();
|
|
|
|
OfflineList.clear();
|
|
|
|
FavouriteList.clear();
|
|
|
|
#endif
|
|
|
|
auto* list = GetList();
|
2017-01-16 11:42:50 -05:00
|
|
|
if (list) list->clear();
|
|
|
|
|
2022-12-29 06:34:43 -05:00
|
|
|
VisibleList.clear();
|
2017-05-30 14:31:00 -04:00
|
|
|
|
|
|
|
{
|
2022-12-29 06:34:43 -05:00
|
|
|
std::lock_guard _(RefreshContainer.mutex);
|
|
|
|
RefreshContainer.servers.clear();
|
|
|
|
RefreshContainer.sendCount = 0;
|
|
|
|
RefreshContainer.sentCount = 0;
|
2017-05-30 14:31:00 -04:00
|
|
|
}
|
2017-01-16 11:42:50 -05:00
|
|
|
|
2022-12-29 06:34:43 -05:00
|
|
|
if (IsOfflineList())
|
2017-01-16 11:42:50 -05:00
|
|
|
{
|
|
|
|
Discovery::Perform();
|
|
|
|
}
|
2022-12-29 06:34:43 -05:00
|
|
|
else if (IsOnlineList())
|
2017-01-16 11:42:50 -05:00
|
|
|
{
|
2022-05-03 01:17:31 -04:00
|
|
|
const auto masterPort = Dvar::Var("masterPort").get<int>();
|
|
|
|
const auto masterServerName = Dvar::Var("masterServerName").get<const char*>();
|
2017-01-16 11:42:50 -05:00
|
|
|
|
2022-05-03 12:35:53 -04:00
|
|
|
// Check if our dvars can properly convert to a address
|
2022-05-03 01:17:31 -04:00
|
|
|
Game::netadr_t masterServerAddr;
|
2022-12-29 06:34:43 -05:00
|
|
|
if (!GetMasterServer(masterServerName, masterPort, masterServerAddr))
|
2022-05-03 01:17:31 -04:00
|
|
|
{
|
2022-06-12 17:07:53 -04:00
|
|
|
Logger::Print("Could not resolve address for {}:{}", masterServerName, masterPort);
|
2022-12-29 06:34:43 -05:00
|
|
|
Toast::Show("cardicon_headshot", "^1Error", Utils::String::VA("Could not resolve address for %s:%i", masterServerName, masterPort), 5000);
|
2022-10-15 16:31:16 -04:00
|
|
|
UseMasterServer = false;
|
2022-05-03 12:35:53 -04:00
|
|
|
return;
|
|
|
|
}
|
2017-01-16 11:42:50 -05:00
|
|
|
|
2022-05-03 12:35:53 -04:00
|
|
|
Toast::Show("cardicon_headshot", "Server Browser", "Fetching servers...", 3000);
|
2017-01-16 11:42:50 -05:00
|
|
|
|
2022-10-15 16:31:16 -04:00
|
|
|
UseMasterServer = true;
|
2017-01-16 11:42:50 -05:00
|
|
|
|
2022-12-29 06:34:43 -05:00
|
|
|
RefreshContainer.awatingList = true;
|
|
|
|
RefreshContainer.awaitTime = Game::Sys_Milliseconds();
|
|
|
|
RefreshContainer.host = Network::Address(Utils::String::VA("%s:%u", masterServerName, masterPort));
|
2022-05-03 12:35:53 -04:00
|
|
|
|
|
|
|
Logger::Print("Sending serverlist request to master\n");
|
2022-12-29 06:34:43 -05:00
|
|
|
Network::SendCommand(RefreshContainer.host, "getservers", Utils::String::VA("IW4 %i full empty", PROTOCOL));
|
2017-01-16 11:42:50 -05:00
|
|
|
}
|
2022-12-29 06:34:43 -05:00
|
|
|
else if (IsFavouriteList())
|
2017-01-16 11:42:50 -05:00
|
|
|
{
|
2022-12-29 06:34:43 -05:00
|
|
|
LoadFavourties();
|
2017-01-16 11:42:50 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-12-17 08:29:18 -05:00
|
|
|
void ServerList::StoreFavourite(const std::string& server)
|
2017-01-16 11:42:50 -05:00
|
|
|
{
|
|
|
|
std::vector<std::string> servers;
|
2022-08-19 12:35:36 -04:00
|
|
|
|
|
|
|
const auto parseData = Utils::IO::ReadFile(FavouriteFile);
|
|
|
|
if (!parseData.empty())
|
2017-01-16 11:42:50 -05:00
|
|
|
{
|
2022-10-16 11:17:42 -04:00
|
|
|
nlohmann::json object;
|
|
|
|
try
|
|
|
|
{
|
|
|
|
object = nlohmann::json::parse(parseData);
|
|
|
|
}
|
|
|
|
catch (const nlohmann::json::parse_error& ex)
|
|
|
|
{
|
|
|
|
Logger::PrintError(Game::CON_CHANNEL_ERROR, "Json Parse Error: {}\n", ex.what());
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-01-16 11:42:50 -05:00
|
|
|
if (!object.is_array())
|
|
|
|
{
|
|
|
|
Logger::Print("Favourites storage file is invalid!\n");
|
2017-02-06 15:09:41 -05:00
|
|
|
Game::ShowMessageBox("Favourites storage file is invalid!", "Error");
|
2017-01-16 11:42:50 -05:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-08-19 12:35:36 -04:00
|
|
|
const nlohmann::json::array_t storedServers = object;
|
|
|
|
for (const auto& storedServer : storedServers)
|
2017-01-16 11:42:50 -05:00
|
|
|
{
|
2022-08-19 12:35:36 -04:00
|
|
|
if (!storedServer.is_string()) continue;
|
|
|
|
if (storedServer.get<std::string>() == server)
|
2017-01-16 11:42:50 -05:00
|
|
|
{
|
2017-02-06 15:09:41 -05:00
|
|
|
Game::ShowMessageBox("Server already marked as favourite.", "Error");
|
2017-01-16 11:42:50 -05:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-08-19 12:35:36 -04:00
|
|
|
servers.push_back(storedServer.get<std::string>());
|
2017-01-16 11:42:50 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
servers.push_back(server);
|
|
|
|
|
2022-08-19 12:35:36 -04:00
|
|
|
const auto data = nlohmann::json(servers);
|
|
|
|
Utils::IO::WriteFile(FavouriteFile, data.dump());
|
2017-02-06 15:09:41 -05:00
|
|
|
Game::ShowMessageBox("Server added to favourites.", "Success");
|
2017-01-16 11:42:50 -05:00
|
|
|
}
|
|
|
|
|
2018-12-17 08:29:18 -05:00
|
|
|
void ServerList::RemoveFavourite(const std::string& server)
|
2017-01-18 17:35:33 -05:00
|
|
|
{
|
|
|
|
std::vector<std::string> servers;
|
|
|
|
|
2022-08-19 12:35:36 -04:00
|
|
|
const auto parseData = Utils::IO::ReadFile(FavouriteFile);
|
|
|
|
if (!parseData.empty())
|
2017-01-18 17:35:33 -05:00
|
|
|
{
|
2022-10-16 11:17:42 -04:00
|
|
|
nlohmann::json object;
|
|
|
|
try
|
|
|
|
{
|
|
|
|
object = nlohmann::json::parse(parseData);
|
|
|
|
}
|
|
|
|
catch (const nlohmann::json::parse_error& ex)
|
|
|
|
{
|
|
|
|
Logger::PrintError(Game::CON_CHANNEL_ERROR, "Json Parse Error: {}\n", ex.what());
|
|
|
|
return;
|
|
|
|
}
|
2017-01-18 17:35:33 -05:00
|
|
|
|
|
|
|
if (!object.is_array())
|
|
|
|
{
|
|
|
|
Logger::Print("Favourites storage file is invalid!\n");
|
2017-02-06 15:09:41 -05:00
|
|
|
Game::ShowMessageBox("Favourites storage file is invalid!", "Error");
|
2017-01-18 17:35:33 -05:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-08-19 12:35:36 -04:00
|
|
|
const nlohmann::json::array_t arr = object;
|
2022-07-29 15:54:18 -04:00
|
|
|
for (auto& storedServer : arr)
|
2017-01-18 17:35:33 -05:00
|
|
|
{
|
2022-07-29 15:54:18 -04:00
|
|
|
if (storedServer.is_string() && storedServer.get<std::string>() != server)
|
2017-01-18 17:35:33 -05:00
|
|
|
{
|
2022-07-29 15:54:18 -04:00
|
|
|
servers.push_back(storedServer.get<std::string>());
|
2017-01-18 17:35:33 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-08-19 12:35:36 -04:00
|
|
|
const auto data = nlohmann::json(servers);
|
|
|
|
Utils::IO::WriteFile(FavouriteFile, data.dump());
|
2017-01-18 17:35:33 -05:00
|
|
|
|
2022-12-29 06:34:43 -05:00
|
|
|
auto* list = GetList();
|
2017-01-18 17:35:33 -05:00
|
|
|
if (list) list->clear();
|
|
|
|
|
2022-12-29 06:34:43 -05:00
|
|
|
RefreshVisibleListInternal(UIScript::Token(), nullptr);
|
2017-01-18 17:35:33 -05:00
|
|
|
|
2017-02-06 15:09:41 -05:00
|
|
|
Game::ShowMessageBox("Server removed from favourites.", "Success");
|
2017-01-18 17:35:33 -05:00
|
|
|
}
|
|
|
|
|
2017-01-16 11:42:50 -05:00
|
|
|
void ServerList::LoadFavourties()
|
|
|
|
{
|
2022-12-29 06:34:43 -05:00
|
|
|
if (!IsFavouriteList())
|
2017-01-16 11:42:50 -05:00
|
|
|
{
|
2022-08-19 12:35:36 -04:00
|
|
|
return;
|
|
|
|
}
|
2017-01-16 11:42:50 -05:00
|
|
|
|
2022-12-29 06:34:43 -05:00
|
|
|
auto* list = GetList();
|
2022-08-19 12:35:36 -04:00
|
|
|
if (list) list->clear();
|
2017-01-16 11:42:50 -05:00
|
|
|
|
2022-08-19 12:35:36 -04:00
|
|
|
const auto parseData = Utils::IO::ReadFile(FavouriteFile);
|
|
|
|
if (parseData.empty())
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
2017-01-16 11:42:50 -05:00
|
|
|
|
2022-10-16 11:17:42 -04:00
|
|
|
nlohmann::json object;
|
|
|
|
try
|
|
|
|
{
|
|
|
|
object = nlohmann::json::parse(parseData);
|
|
|
|
}
|
|
|
|
catch (const nlohmann::json::parse_error& ex)
|
|
|
|
{
|
|
|
|
Logger::PrintError(Game::CON_CHANNEL_ERROR, "Json Parse Error: {}\n", ex.what());
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-08-19 12:35:36 -04:00
|
|
|
if (!object.is_array())
|
|
|
|
{
|
|
|
|
Logger::Print("Favourites storage file is invalid!\n");
|
|
|
|
Game::ShowMessageBox("Favourites storage file is invalid!", "Error");
|
|
|
|
return;
|
|
|
|
}
|
2017-01-16 11:42:50 -05:00
|
|
|
|
2022-08-19 12:35:36 -04:00
|
|
|
const nlohmann::json::array_t servers = object;
|
|
|
|
for (const auto& server : servers)
|
|
|
|
{
|
|
|
|
if (!server.is_string()) continue;
|
2022-12-29 06:34:43 -05:00
|
|
|
InsertRequest(server.get<std::string>());
|
2017-01-16 11:42:50 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-02-28 13:58:03 -05:00
|
|
|
void ServerList::InsertRequest(Network::Address address)
|
2017-01-16 11:42:50 -05:00
|
|
|
{
|
2022-12-29 06:34:43 -05:00
|
|
|
std::lock_guard _(RefreshContainer.mutex);
|
2017-01-16 11:42:50 -05:00
|
|
|
|
2022-12-29 06:34:43 -05:00
|
|
|
Container::ServerContainer container;
|
2017-01-16 11:42:50 -05:00
|
|
|
container.sent = false;
|
|
|
|
container.target = address;
|
|
|
|
|
|
|
|
bool alreadyInserted = false;
|
2022-12-29 06:34:43 -05:00
|
|
|
for (auto &server : RefreshContainer.servers)
|
2017-01-16 11:42:50 -05:00
|
|
|
{
|
|
|
|
if (server.target == container.target)
|
|
|
|
{
|
|
|
|
alreadyInserted = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!alreadyInserted)
|
|
|
|
{
|
2022-12-29 06:34:43 -05:00
|
|
|
RefreshContainer.servers.push_back(container);
|
2017-01-16 11:42:50 -05:00
|
|
|
|
2022-12-29 06:34:43 -05:00
|
|
|
auto* list = GetList();
|
2017-01-16 11:42:50 -05:00
|
|
|
if (list)
|
|
|
|
{
|
2017-05-30 14:31:00 -04:00
|
|
|
for (auto& server : *list)
|
2017-01-16 11:42:50 -05:00
|
|
|
{
|
|
|
|
if (server.addr == container.target)
|
|
|
|
{
|
2022-12-29 06:34:43 -05:00
|
|
|
--RefreshContainer.sendCount;
|
|
|
|
--RefreshContainer.sentCount;
|
2017-01-16 11:42:50 -05:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-12-29 06:34:43 -05:00
|
|
|
++RefreshContainer.sendCount;
|
2017-01-16 11:42:50 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-06-04 04:56:14 -04:00
|
|
|
void ServerList::Insert(const Network::Address& address, const Utils::InfoString& info)
|
2017-01-16 11:42:50 -05:00
|
|
|
{
|
2022-12-29 06:34:43 -05:00
|
|
|
std::lock_guard _(RefreshContainer.mutex);
|
2017-01-16 11:42:50 -05:00
|
|
|
|
2022-12-29 06:34:43 -05:00
|
|
|
for (auto i = RefreshContainer.servers.begin(); i != RefreshContainer.servers.end();)
|
2017-01-16 11:42:50 -05:00
|
|
|
{
|
|
|
|
// Our desired server
|
2022-12-29 06:34:43 -05:00
|
|
|
if ((i->target == address) && i->sent)
|
2017-01-16 11:42:50 -05:00
|
|
|
{
|
|
|
|
// Challenge did not match
|
|
|
|
if (i->challenge != info.get("challenge"))
|
|
|
|
{
|
|
|
|
// Shall we remove the server from the queue?
|
|
|
|
// Better not, it might send a second response with the correct challenge.
|
|
|
|
// This might happen when users refresh twice (or more often) in a short period of time
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
ServerInfo server;
|
|
|
|
server.hostname = info.get("hostname");
|
|
|
|
server.mapname = info.get("mapname");
|
|
|
|
server.gametype = info.get("gametype");
|
|
|
|
server.shortversion = info.get("shortversion");
|
|
|
|
server.mod = info.get("fs_game");
|
2022-12-29 06:34:43 -05:00
|
|
|
server.matchType = std::strtol(info.get("matchtype").data(), nullptr, 10);
|
|
|
|
server.clients = std::strtol(info.get("clients").data(), nullptr, 10);
|
|
|
|
server.bots = std::strtol(info.get("bots").data(), nullptr, 10);
|
|
|
|
server.securityLevel = std::strtol(info.get("securityLevel").data(), nullptr, 10);
|
|
|
|
server.maxClients = std::strtol(info.get("sv_maxclients").data(), nullptr, 10);
|
|
|
|
server.password = info.get("isPrivate") == "1"s;
|
|
|
|
server.aimassist = info.get("aimAssist") == "1";
|
|
|
|
server.voice = info.get("voiceChat") == "1"s;
|
|
|
|
server.hardcore = info.get("hc") == "1"s;
|
|
|
|
server.svRunning = info.get("sv_running") == "1"s;
|
2017-01-16 11:42:50 -05:00
|
|
|
server.ping = (Game::Sys_Milliseconds() - i->sendTime);
|
|
|
|
server.addr = address;
|
|
|
|
|
2021-09-07 08:51:36 -04:00
|
|
|
server.hostname = TextRenderer::StripMaterialTextIcons(server.hostname);
|
|
|
|
server.mapname = TextRenderer::StripMaterialTextIcons(server.mapname);
|
|
|
|
server.gametype = TextRenderer::StripMaterialTextIcons(server.gametype);
|
|
|
|
server.mod = TextRenderer::StripMaterialTextIcons(server.mod);
|
2021-08-21 06:35:32 -04:00
|
|
|
|
2017-01-16 11:42:50 -05:00
|
|
|
// Remove server from queue
|
2022-12-29 06:34:43 -05:00
|
|
|
i = RefreshContainer.servers.erase(i);
|
2017-01-16 11:42:50 -05:00
|
|
|
|
2021-08-20 12:42:47 -04:00
|
|
|
// Servers with more than 18 players or less than 0 players are faking for sure
|
|
|
|
// So lets ignore those
|
|
|
|
if (server.clients > 18 || server.maxClients > 18 || server.clients < 0 || server.maxClients < 0)
|
2022-12-29 06:34:43 -05:00
|
|
|
{
|
2021-08-20 12:42:47 -04:00
|
|
|
return;
|
2022-12-29 06:34:43 -05:00
|
|
|
}
|
2021-08-20 12:42:47 -04:00
|
|
|
|
2017-01-16 11:42:50 -05:00
|
|
|
// Check if already inserted and remove
|
2022-12-29 06:34:43 -05:00
|
|
|
auto* list = GetList();
|
2017-01-16 11:42:50 -05:00
|
|
|
if (!list) return;
|
|
|
|
|
2022-12-29 06:34:43 -05:00
|
|
|
std::size_t k = 0;
|
2017-01-16 11:42:50 -05:00
|
|
|
for (auto j = list->begin(); j != list->end(); ++k)
|
|
|
|
{
|
|
|
|
if (j->addr == address)
|
|
|
|
{
|
|
|
|
j = list->erase(j);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
++j;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Also remove from visible list
|
2022-12-29 06:34:43 -05:00
|
|
|
for (auto j = VisibleList.begin(); j != VisibleList.end();)
|
2017-01-16 11:42:50 -05:00
|
|
|
{
|
|
|
|
if (*j == k)
|
|
|
|
{
|
2022-12-29 06:34:43 -05:00
|
|
|
j = VisibleList.erase(j);
|
2017-01-16 11:42:50 -05:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
++j;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-11-26 12:38:34 -05:00
|
|
|
if (info.get("gamename") == "IW4"s && server.matchType
|
2017-02-15 04:15:35 -05:00
|
|
|
#if !defined(DEBUG) && defined(VERSION_FILTER)
|
2022-12-29 06:34:43 -05:00
|
|
|
&& CompareVersion(server.shortversion, SHORTVERSION)
|
2017-01-16 11:42:50 -05:00
|
|
|
#endif
|
|
|
|
)
|
|
|
|
{
|
2022-12-29 06:34:43 -05:00
|
|
|
auto* lList = GetList();
|
2017-01-16 11:42:50 -05:00
|
|
|
if (lList)
|
|
|
|
{
|
|
|
|
lList->push_back(server);
|
2022-12-29 06:34:43 -05:00
|
|
|
RefreshVisibleListInternal(UIScript::Token(), nullptr);
|
2017-01-16 11:42:50 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
++i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-12-17 08:29:18 -05:00
|
|
|
bool ServerList::CompareVersion(const std::string& version1, const std::string& version2)
|
2017-02-15 08:11:36 -05:00
|
|
|
{
|
2022-02-26 18:02:04 -05:00
|
|
|
auto subVersions1 = Utils::String::Split(version1, '.');
|
|
|
|
auto subVersions2 = Utils::String::Split(version2, '.');
|
2017-02-15 08:11:36 -05:00
|
|
|
|
|
|
|
while (subVersions1.size() >= 3) subVersions1.pop_back();
|
|
|
|
while (subVersions2.size() >= 3) subVersions2.pop_back();
|
|
|
|
if (subVersions1.size() != subVersions2.size()) return false;
|
|
|
|
|
2022-12-29 06:34:43 -05:00
|
|
|
for (std::size_t i = 0; i < subVersions1.size(); ++i)
|
2017-02-15 08:11:36 -05:00
|
|
|
{
|
2022-12-29 06:34:43 -05:00
|
|
|
try
|
2017-02-15 08:11:36 -05:00
|
|
|
{
|
2022-12-29 06:34:43 -05:00
|
|
|
if (std::stoi(subVersions1[i]) != std::stoi(subVersions2[i]))
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
catch (const std::exception& ex)
|
|
|
|
{
|
|
|
|
Logger::Warning(Game::CON_CHANNEL_CONSOLEONLY, "{} while performing numeric comparison between {} and {}\n", ex.what(), subVersions1[i], subVersions2[i]);
|
2017-02-15 08:11:36 -05:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-01-16 11:42:50 -05:00
|
|
|
ServerList::ServerInfo* ServerList::GetCurrentServer()
|
|
|
|
{
|
2022-12-29 06:34:43 -05:00
|
|
|
return GetServer(CurrentServer);
|
2017-01-16 11:42:50 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
void ServerList::SortList()
|
|
|
|
{
|
2017-06-05 10:12:15 -04:00
|
|
|
// Only sort when the serverlist is open
|
2022-12-29 06:34:43 -05:00
|
|
|
if (!IsServerListOpen()) return;
|
2017-01-16 11:42:50 -05:00
|
|
|
|
2022-12-29 06:34:43 -05:00
|
|
|
std::ranges::stable_sort(VisibleList, [](const unsigned int& server1, const unsigned int& server2) -> bool
|
2017-06-05 10:12:15 -04:00
|
|
|
{
|
2017-01-16 11:42:50 -05:00
|
|
|
ServerInfo* info1 = nullptr;
|
|
|
|
ServerInfo* info2 = nullptr;
|
|
|
|
|
2022-12-29 06:34:43 -05:00
|
|
|
auto* list = GetList();
|
2017-06-08 11:55:18 -04:00
|
|
|
if (!list) return false;
|
2017-01-16 11:42:50 -05:00
|
|
|
|
|
|
|
if (list->size() > server1) info1 = &(*list)[server1];
|
|
|
|
if (list->size() > server2) info2 = &(*list)[server2];
|
|
|
|
|
2017-06-08 11:55:18 -04:00
|
|
|
if (!info1) return false;
|
|
|
|
if (!info2) return false;
|
2017-01-16 11:42:50 -05:00
|
|
|
|
|
|
|
// Numerical comparisons
|
2022-12-29 06:34:43 -05:00
|
|
|
if (SortKey == static_cast<std::underlying_type_t<Column>>(Column::Ping))
|
2017-01-16 11:42:50 -05:00
|
|
|
{
|
2017-06-08 11:55:18 -04:00
|
|
|
return info1->ping < info2->ping;
|
2017-01-16 11:42:50 -05:00
|
|
|
}
|
2022-12-29 06:34:43 -05:00
|
|
|
|
|
|
|
if (SortKey == static_cast<std::underlying_type_t<Column>>(Column::Players))
|
2017-01-16 11:42:50 -05:00
|
|
|
{
|
2017-06-08 11:55:18 -04:00
|
|
|
return info1->clients < info2->clients;
|
2017-01-16 11:42:50 -05:00
|
|
|
}
|
|
|
|
|
2022-12-29 06:34:43 -05:00
|
|
|
auto text1 = Utils::String::ToLower(TextRenderer::StripColors(GetServerInfoText(info1, SortKey, true)));
|
|
|
|
auto text2 = Utils::String::ToLower(TextRenderer::StripColors(GetServerInfoText(info2, SortKey, true)));
|
2017-01-16 11:42:50 -05:00
|
|
|
|
|
|
|
// ASCII-based comparison
|
2017-06-08 11:55:18 -04:00
|
|
|
return text1.compare(text2) < 0;
|
2017-01-16 11:42:50 -05:00
|
|
|
});
|
2017-06-08 11:55:18 -04:00
|
|
|
|
2022-12-29 06:34:43 -05:00
|
|
|
if (!SortAsc) std::ranges::reverse(VisibleList);
|
2017-01-16 11:42:50 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
ServerList::ServerInfo* ServerList::GetServer(unsigned int index)
|
|
|
|
{
|
2022-12-29 06:34:43 -05:00
|
|
|
if (VisibleList.size() > index)
|
2017-01-16 11:42:50 -05:00
|
|
|
{
|
2022-12-29 06:34:43 -05:00
|
|
|
auto* list = GetList();
|
2017-01-16 11:42:50 -05:00
|
|
|
if (!list) return nullptr;
|
|
|
|
|
2022-12-29 06:34:43 -05:00
|
|
|
if (list->size() > VisibleList[index])
|
2017-01-16 11:42:50 -05:00
|
|
|
{
|
2022-12-29 06:34:43 -05:00
|
|
|
return &(*list)[VisibleList[index]];
|
2017-01-16 11:42:50 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ServerList::Frame()
|
|
|
|
{
|
2017-07-12 04:12:51 -04:00
|
|
|
static Utils::Time::Interval frameLimit;
|
2022-12-29 06:34:43 -05:00
|
|
|
const auto interval = static_cast<int>(1000.0f / static_cast<float>(NETServerFrames.get<int>()));
|
2022-04-12 17:15:50 -04:00
|
|
|
|
|
|
|
if (!frameLimit.elapsed(std::chrono::milliseconds(interval)))
|
2022-12-29 06:34:43 -05:00
|
|
|
{
|
2022-04-12 17:15:50 -04:00
|
|
|
return;
|
2022-12-29 06:34:43 -05:00
|
|
|
}
|
2022-04-12 17:15:50 -04:00
|
|
|
|
2017-07-12 04:12:51 -04:00
|
|
|
frameLimit.update();
|
|
|
|
|
2022-12-29 06:34:43 -05:00
|
|
|
std::lock_guard _(RefreshContainer.mutex);
|
2017-01-16 11:42:50 -05:00
|
|
|
|
2022-12-29 06:34:43 -05:00
|
|
|
if (RefreshContainer.awatingList)
|
2017-01-16 11:42:50 -05:00
|
|
|
{
|
2022-05-03 02:34:57 -04:00
|
|
|
// Stop counting if we are out of the server browser menu
|
2022-12-29 06:34:43 -05:00
|
|
|
if (!IsServerListOpen())
|
2022-05-03 02:34:57 -04:00
|
|
|
{
|
2022-12-29 06:34:43 -05:00
|
|
|
RefreshContainer.awatingList = false;
|
2022-05-03 02:34:57 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// Check if we haven't got a response within 5 seconds
|
2022-12-29 06:34:43 -05:00
|
|
|
if (Game::Sys_Milliseconds() - RefreshContainer.awaitTime > 5000)
|
2017-01-16 11:42:50 -05:00
|
|
|
{
|
2022-12-29 06:34:43 -05:00
|
|
|
RefreshContainer.awatingList = false;
|
2017-01-16 11:42:50 -05:00
|
|
|
|
2022-12-29 06:34:43 -05:00
|
|
|
Logger::Print("We haven't received a response from the master within {} seconds!\n", (Game::Sys_Milliseconds() - RefreshContainer.awaitTime) / 1000);
|
|
|
|
Toast::Show("cardicon_headshot", "^3Warning", "Failed to reach master server. Using node system instead.", 5000);
|
2022-05-03 02:34:57 -04:00
|
|
|
|
2022-10-15 16:31:16 -04:00
|
|
|
UseMasterServer = false;
|
2022-05-03 02:34:57 -04:00
|
|
|
Node::Synchronize();
|
2017-01-16 11:42:50 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-12-29 06:34:43 -05:00
|
|
|
auto requestLimit = NETServerQueryLimit.get<int>();
|
|
|
|
for (std::size_t i = 0; i < RefreshContainer.servers.size() && requestLimit > 0; ++i)
|
2017-01-16 11:42:50 -05:00
|
|
|
{
|
2022-12-29 06:34:43 -05:00
|
|
|
auto* server = &RefreshContainer.servers[i];
|
2017-01-16 11:42:50 -05:00
|
|
|
if (server->sent) continue;
|
|
|
|
|
|
|
|
// Found server we can send a request to
|
|
|
|
server->sent = true;
|
2017-07-12 04:12:51 -04:00
|
|
|
requestLimit--;
|
2017-01-16 11:42:50 -05:00
|
|
|
|
|
|
|
server->sendTime = Game::Sys_Milliseconds();
|
2017-01-19 12:14:30 -05:00
|
|
|
server->challenge = Utils::Cryptography::Rand::GenerateChallenge();
|
2017-01-16 11:42:50 -05:00
|
|
|
|
2022-12-29 06:34:43 -05:00
|
|
|
++RefreshContainer.sentCount;
|
2017-01-16 11:42:50 -05:00
|
|
|
|
|
|
|
Network::SendCommand(server->target, "getinfo", server->challenge);
|
|
|
|
|
2022-12-29 06:34:43 -05:00
|
|
|
// Display in the menu, like in CoD4 - Disabled to avoid spamming?
|
2017-02-26 09:27:02 -05:00
|
|
|
//Localization::Set("MPUI_SERVERQUERIED", Utils::String::VA("Sent requests: %d/%d", ServerList::RefreshContainer.sentCount, ServerList::RefreshContainer.sendCount));
|
2017-01-16 11:42:50 -05:00
|
|
|
}
|
|
|
|
|
2022-12-29 06:34:43 -05:00
|
|
|
UpdateVisibleInfo();
|
2017-01-16 11:42:50 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
void ServerList::UpdateSource()
|
|
|
|
{
|
2022-12-29 06:34:43 -05:00
|
|
|
auto source = (*Game::ui_netSource)->current.integer;
|
2017-01-16 11:42:50 -05:00
|
|
|
|
2022-12-29 06:34:43 -05:00
|
|
|
if (++source > (*Game::ui_netSource)->domain.integer.max)
|
2017-01-16 11:42:50 -05:00
|
|
|
{
|
|
|
|
source = 0;
|
|
|
|
}
|
|
|
|
|
2022-12-29 06:34:43 -05:00
|
|
|
Game::Dvar_SetInt(*Game::ui_netSource, source);
|
2017-01-16 11:42:50 -05:00
|
|
|
|
2022-12-29 06:34:43 -05:00
|
|
|
RefreshVisibleListInternal(UIScript::Token(), nullptr, true);
|
2017-01-16 11:42:50 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
void ServerList::UpdateGameType()
|
|
|
|
{
|
2022-12-29 06:34:43 -05:00
|
|
|
auto gametype = (*Game::ui_joinGametype)->current.integer;
|
2017-01-16 11:42:50 -05:00
|
|
|
|
|
|
|
if (++gametype > *Game::gameTypeCount)
|
|
|
|
{
|
|
|
|
gametype = 0;
|
|
|
|
}
|
|
|
|
|
2022-12-29 06:34:43 -05:00
|
|
|
Game::Dvar_SetInt(*Game::ui_joinGametype, gametype);
|
2017-01-16 11:42:50 -05:00
|
|
|
|
2022-12-29 06:34:43 -05:00
|
|
|
RefreshVisibleListInternal(UIScript::Token(), nullptr);
|
2017-01-16 11:42:50 -05:00
|
|
|
}
|
|
|
|
|
2017-02-26 09:27:02 -05:00
|
|
|
void ServerList::UpdateVisibleInfo()
|
|
|
|
{
|
|
|
|
static int servers = 0;
|
|
|
|
static int players = 0;
|
2017-06-25 16:07:16 -04:00
|
|
|
static int bots = 0;
|
2017-02-26 09:27:02 -05:00
|
|
|
|
2022-12-29 06:34:43 -05:00
|
|
|
auto list = GetList();
|
2017-02-26 09:27:02 -05:00
|
|
|
|
|
|
|
if (list)
|
|
|
|
{
|
|
|
|
int newSevers = list->size();
|
|
|
|
int newPlayers = 0;
|
2017-06-25 16:07:16 -04:00
|
|
|
int newBots = 0;
|
2017-02-26 09:27:02 -05:00
|
|
|
|
2022-12-29 06:34:43 -05:00
|
|
|
for (std::size_t i = 0; i < list->size(); ++i)
|
2017-02-26 09:27:02 -05:00
|
|
|
{
|
|
|
|
newPlayers += list->at(i).clients;
|
2017-06-25 16:07:16 -04:00
|
|
|
newBots += list->at(i).bots;
|
2017-02-26 09:27:02 -05:00
|
|
|
}
|
|
|
|
|
2017-06-25 16:07:16 -04:00
|
|
|
if (newSevers != servers || newPlayers != players || newBots != bots)
|
2017-02-26 09:27:02 -05:00
|
|
|
{
|
|
|
|
servers = newSevers;
|
|
|
|
players = newPlayers;
|
2017-06-25 16:07:16 -04:00
|
|
|
bots = newBots;
|
2017-02-26 09:27:02 -05:00
|
|
|
|
2017-06-25 16:07:16 -04:00
|
|
|
Localization::Set("MPUI_SERVERQUERIED", Utils::String::VA("Servers: %i\nPlayers: %i (%i)", servers, players, bots));
|
2017-02-26 09:27:02 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-05-03 03:01:54 -04:00
|
|
|
bool ServerList::GetMasterServer(const char* ip, int port, Game::netadr_t& address)
|
2022-05-03 01:17:31 -04:00
|
|
|
{
|
2022-05-03 03:01:54 -04:00
|
|
|
return Game::NET_StringToAdr(Utils::String::VA("%s:%u", ip, port), &address);
|
2022-05-03 02:34:57 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
bool ServerList::IsServerListOpen()
|
|
|
|
{
|
2022-05-03 05:10:48 -04:00
|
|
|
auto* menu = Game::Menus_FindByName(Game::uiContext, "pc_join_unranked");
|
2022-05-03 02:34:57 -04:00
|
|
|
if (!menu)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return Game::Menu_IsVisible(Game::uiContext, menu);
|
2022-05-03 01:17:31 -04:00
|
|
|
}
|
|
|
|
|
2017-01-16 11:42:50 -05:00
|
|
|
ServerList::ServerList()
|
|
|
|
{
|
2022-12-29 06:34:43 -05:00
|
|
|
OnlineList.clear();
|
|
|
|
OfflineList.clear();
|
|
|
|
FavouriteList.clear();
|
|
|
|
VisibleList.clear();
|
2017-01-16 11:42:50 -05:00
|
|
|
|
2022-05-27 06:19:28 -04:00
|
|
|
Scheduler::Once([]
|
2017-01-16 11:42:50 -05:00
|
|
|
{
|
2022-12-29 06:34:43 -05:00
|
|
|
UIServerSelected = Dvar::Register<bool>("ui_serverSelected", false,
|
2022-07-02 13:52:57 -04:00
|
|
|
Game::DVAR_NONE, "Whether a server has been selected in the serverlist");
|
2022-12-29 06:34:43 -05:00
|
|
|
UIServerSelectedMap = Dvar::Register<const char*>("ui_serverSelectedMap", "mp_afghan",
|
2022-07-02 13:52:57 -04:00
|
|
|
Game::DVAR_NONE, "Map of the selected server");
|
2017-07-12 04:12:51 -04:00
|
|
|
|
2022-12-29 06:34:43 -05:00
|
|
|
NETServerQueryLimit = Dvar::Register<int>("net_serverQueryLimit", 1,
|
2022-07-02 13:52:57 -04:00
|
|
|
1, 10, Dedicated::IsEnabled() ? Game::DVAR_NONE : Game::DVAR_ARCHIVE, "Amount of server queries per frame");
|
2022-12-29 06:34:43 -05:00
|
|
|
NETServerFrames = Dvar::Register<int>("net_serverFrames", 30,
|
2022-07-02 13:52:57 -04:00
|
|
|
1, 60, Dedicated::IsEnabled() ? Game::DVAR_NONE : Game::DVAR_ARCHIVE, "Amount of server query frames per second");
|
2022-05-27 06:19:28 -04:00
|
|
|
}, Scheduler::Pipeline::MAIN);
|
2017-01-16 11:42:50 -05:00
|
|
|
|
2017-03-24 13:28:03 -04:00
|
|
|
// Fix ui_netsource dvar
|
|
|
|
Utils::Hook::Nop(0x4CDEEC, 5); // Don't reset the netsource when gametypes aren't loaded
|
|
|
|
|
2017-06-25 16:07:16 -04:00
|
|
|
Localization::Set("MPUI_SERVERQUERIED", "Servers: 0\nPlayers: 0 (0)");
|
2017-01-16 11:42:50 -05:00
|
|
|
|
2022-08-19 19:10:35 -04:00
|
|
|
Network::OnClientPacket("getServersResponse", [](const Network::Address& address, [[maybe_unused]] const std::string& data)
|
2017-01-16 11:42:50 -05:00
|
|
|
{
|
2022-12-29 06:34:43 -05:00
|
|
|
if (RefreshContainer.host != address) return; // Only parse from host we sent to
|
2017-01-16 11:42:50 -05:00
|
|
|
|
2022-12-29 06:34:43 -05:00
|
|
|
RefreshContainer.awatingList = false;
|
2017-01-16 11:42:50 -05:00
|
|
|
|
2022-12-29 06:34:43 -05:00
|
|
|
std::lock_guard _(RefreshContainer.mutex);
|
2017-01-16 11:42:50 -05:00
|
|
|
|
|
|
|
int offset = 0;
|
2022-12-29 06:34:43 -05:00
|
|
|
auto count = RefreshContainer.servers.size();
|
|
|
|
MasterEntry* entry = nullptr;
|
2017-01-16 11:42:50 -05:00
|
|
|
|
|
|
|
// Find first entry
|
2017-06-14 06:06:04 -04:00
|
|
|
do
|
2017-01-16 11:42:50 -05:00
|
|
|
{
|
2022-12-29 06:34:43 -05:00
|
|
|
entry = reinterpret_cast<MasterEntry*>(const_cast<char*>(data.data()) + offset++);
|
2017-06-14 06:06:04 -04:00
|
|
|
} while (!entry->HasSeparator() && !entry->IsEndToken());
|
2017-01-16 11:42:50 -05:00
|
|
|
|
|
|
|
for (int i = 0; !entry[i].IsEndToken() && entry[i].HasSeparator(); ++i)
|
|
|
|
{
|
|
|
|
Network::Address serverAddr = address;
|
|
|
|
serverAddr.setIP(entry[i].ip);
|
|
|
|
serverAddr.setPort(ntohs(entry[i].port));
|
|
|
|
serverAddr.setType(Game::NA_IP);
|
|
|
|
|
2022-12-29 06:34:43 -05:00
|
|
|
InsertRequest(serverAddr);
|
2017-01-16 11:42:50 -05:00
|
|
|
}
|
|
|
|
|
2022-12-29 06:34:43 -05:00
|
|
|
Logger::Print("Parsed {} servers from master\n", RefreshContainer.servers.size() - count);
|
2017-01-16 11:42:50 -05:00
|
|
|
});
|
|
|
|
|
|
|
|
// Set default masterServerName + port and save it
|
2022-05-03 05:10:48 -04:00
|
|
|
Utils::Hook::Set<const char*>(0x60AD92, "master.xlabs.dev");
|
2022-12-29 06:34:43 -05:00
|
|
|
Utils::Hook::Set<std::uint8_t>(0x60AD90, Game::DVAR_NONE); // masterServerName
|
|
|
|
Utils::Hook::Set<std::uint8_t>(0x60ADC6, Game::DVAR_NONE); // masterPort
|
2017-01-16 11:42:50 -05:00
|
|
|
|
|
|
|
// Add server list feeder
|
2022-12-29 06:34:43 -05:00
|
|
|
UIFeeder::Add(2.0f, GetServerCount, GetServerText, SelectServer);
|
2017-01-16 11:42:50 -05:00
|
|
|
|
|
|
|
// Add required UIScripts
|
2022-12-29 06:34:43 -05:00
|
|
|
UIScript::Add("UpdateFilter", RefreshVisibleList);
|
|
|
|
UIScript::Add("RefreshFilter", UpdateVisibleList);
|
2017-01-16 11:42:50 -05:00
|
|
|
|
2022-12-29 06:34:43 -05:00
|
|
|
UIScript::Add("RefreshServers", Refresh);
|
2022-04-12 17:15:50 -04:00
|
|
|
|
2022-08-24 10:38:14 -04:00
|
|
|
UIScript::Add("JoinServer", []([[maybe_unused]] const UIScript::Token& token, [[maybe_unused]] const Game::uiInfo_s* info)
|
2017-01-16 11:42:50 -05:00
|
|
|
{
|
2022-12-29 06:34:43 -05:00
|
|
|
auto* serverInfo = GetServer(CurrentServer);
|
2022-08-24 10:38:14 -04:00
|
|
|
if (serverInfo)
|
2017-01-16 11:42:50 -05:00
|
|
|
{
|
2022-08-24 10:38:14 -04:00
|
|
|
Party::Connect(serverInfo->addr);
|
2017-01-16 11:42:50 -05:00
|
|
|
}
|
|
|
|
});
|
2022-04-12 17:15:50 -04:00
|
|
|
|
2022-08-24 10:38:14 -04:00
|
|
|
UIScript::Add("ServerSort", []([[maybe_unused]] const UIScript::Token& token, [[maybe_unused]] const Game::uiInfo_s* info)
|
2017-01-16 11:42:50 -05:00
|
|
|
{
|
2022-12-29 06:34:43 -05:00
|
|
|
const auto key = token.get<int>();
|
|
|
|
if (SortKey == key)
|
2017-01-16 11:42:50 -05:00
|
|
|
{
|
2022-12-29 06:34:43 -05:00
|
|
|
SortAsc = !SortAsc;
|
2017-01-16 11:42:50 -05:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2022-12-29 06:34:43 -05:00
|
|
|
SortKey = key;
|
|
|
|
SortAsc = true;
|
2017-01-16 11:42:50 -05:00
|
|
|
}
|
|
|
|
|
2022-12-29 06:34:43 -05:00
|
|
|
Logger::Print("Sorting server list by token: {}\n", SortKey);
|
|
|
|
SortList();
|
2017-01-16 11:42:50 -05:00
|
|
|
});
|
2022-04-12 17:15:50 -04:00
|
|
|
|
2022-08-24 10:38:14 -04:00
|
|
|
UIScript::Add("CreateListFavorite", []([[maybe_unused]] const UIScript::Token& token, [[maybe_unused]] const Game::uiInfo_s* info)
|
2017-01-16 11:42:50 -05:00
|
|
|
{
|
2022-12-29 06:34:43 -05:00
|
|
|
auto* serverInfo = GetCurrentServer();
|
2017-01-16 11:42:50 -05:00
|
|
|
if (info)
|
|
|
|
{
|
2022-12-29 06:34:43 -05:00
|
|
|
StoreFavourite(serverInfo->addr.getString());
|
2017-01-16 11:42:50 -05:00
|
|
|
}
|
|
|
|
});
|
2022-04-12 17:15:50 -04:00
|
|
|
|
2022-08-24 10:38:14 -04:00
|
|
|
UIScript::Add("CreateFavorite", []([[maybe_unused]] const UIScript::Token& token, [[maybe_unused]] const Game::uiInfo_s* info)
|
2017-01-16 11:42:50 -05:00
|
|
|
{
|
2022-12-29 06:34:43 -05:00
|
|
|
StoreFavourite(Dvar::Var("ui_favoriteAddress").get<std::string>());
|
2017-01-16 11:42:50 -05:00
|
|
|
});
|
2022-04-12 17:15:50 -04:00
|
|
|
|
2022-08-24 10:38:14 -04:00
|
|
|
UIScript::Add("CreateCurrentServerFavorite", []([[maybe_unused]] const UIScript::Token& token, [[maybe_unused]] const Game::uiInfo_s* info)
|
2017-01-16 11:42:50 -05:00
|
|
|
{
|
2017-01-18 17:35:33 -05:00
|
|
|
if (Game::CL_IsCgameInitialized())
|
2017-01-16 11:42:50 -05:00
|
|
|
{
|
2022-12-29 06:34:43 -05:00
|
|
|
const auto addressText = Network::Address(*Game::connectedHost).getString();
|
|
|
|
if (addressText != "0.0.0.0:0"s && addressText != "loopback"s)
|
2017-01-16 11:42:50 -05:00
|
|
|
{
|
2022-12-29 06:34:43 -05:00
|
|
|
StoreFavourite(addressText);
|
2017-01-16 11:42:50 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
2022-04-12 17:15:50 -04:00
|
|
|
|
2022-08-24 10:38:14 -04:00
|
|
|
UIScript::Add("DeleteFavorite", []([[maybe_unused]] const UIScript::Token& token, [[maybe_unused]] const Game::uiInfo_s* info)
|
2017-01-18 17:35:33 -05:00
|
|
|
{
|
2022-12-29 06:34:43 -05:00
|
|
|
auto* serverInfo = GetCurrentServer();
|
2022-08-24 10:38:14 -04:00
|
|
|
if (serverInfo)
|
2017-01-18 17:35:33 -05:00
|
|
|
{
|
2022-12-29 06:34:43 -05:00
|
|
|
RemoveFavourite(serverInfo->addr.getString());
|
2022-08-24 10:38:14 -04:00
|
|
|
}
|
2017-01-18 17:35:33 -05:00
|
|
|
});
|
2017-01-16 11:42:50 -05:00
|
|
|
|
2022-04-12 17:15:50 -04:00
|
|
|
#ifdef _DEBUG
|
2017-01-28 07:31:43 -05:00
|
|
|
Command::Add("playerCount", [](Command::Params*)
|
|
|
|
{
|
2022-04-12 17:15:50 -04:00
|
|
|
auto count = 0;
|
2022-12-29 06:34:43 -05:00
|
|
|
for (const auto& server : OnlineList)
|
2017-01-28 07:31:43 -05:00
|
|
|
{
|
|
|
|
count += server.clients;
|
|
|
|
}
|
|
|
|
|
2022-06-22 04:58:51 -04:00
|
|
|
Logger::Debug("There are {} players playing", count);
|
2017-01-28 07:31:43 -05:00
|
|
|
});
|
2022-04-12 17:15:50 -04:00
|
|
|
#endif
|
2017-01-16 11:42:50 -05:00
|
|
|
// Add required ownerDraws
|
2022-12-29 06:34:43 -05:00
|
|
|
UIScript::AddOwnerDraw(220, UpdateSource);
|
|
|
|
UIScript::AddOwnerDraw(253, UpdateGameType);
|
2017-01-16 11:42:50 -05:00
|
|
|
|
|
|
|
// Add frame callback
|
2022-12-29 06:34:43 -05:00
|
|
|
Scheduler::Loop(Frame, Scheduler::Pipeline::CLIENT);
|
2017-01-16 11:42:50 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
ServerList::~ServerList()
|
|
|
|
{
|
2022-12-29 06:34:43 -05:00
|
|
|
std::lock_guard _(RefreshContainer.mutex);
|
|
|
|
RefreshContainer.awatingList = false;
|
|
|
|
RefreshContainer.servers.clear();
|
2017-01-16 11:42:50 -05:00
|
|
|
}
|
|
|
|
}
|