iw4x-client/src/Components/Modules/ServerList.cpp

936 lines
25 KiB
C++
Raw Normal View History

2022-02-27 07:53:44 -05:00
#include <STDInclude.hpp>
namespace Components
{
bool ServerList::SortAsc = true;
int ServerList::SortKey = ServerList::Column::Ping;
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;
bool ServerList::useMasterServer = true;
std::vector<ServerList::ServerInfo>* ServerList::GetList()
{
if (ServerList::IsOnlineList())
{
return &ServerList::OnlineList;
}
2022-06-25 12:21:12 -04:00
if (ServerList::IsOfflineList())
{
return &ServerList::OfflineList;
}
2022-06-25 12:21:12 -04:00
if (ServerList::IsFavouriteList())
{
return &ServerList::FavouriteList;
}
return nullptr;
}
bool ServerList::IsFavouriteList()
{
return (Dvar::Var("ui_netSource").get<int>() == 2);
}
bool ServerList::IsOfflineList()
{
return (Dvar::Var("ui_netSource").get<int>() == 0);
}
bool ServerList::IsOnlineList()
{
return (Monitor::IsEnabled() || Dvar::Var("ui_netSource").get<int>() == 1);
}
unsigned int ServerList::GetServerCount()
{
return ServerList::VisibleList.size();
}
const char* ServerList::GetServerText(unsigned int index, int column)
{
ServerList::ServerInfo* info = ServerList::GetServer(index);
if (info)
{
return ServerList::GetServerInfoText(info, column);
}
return "";
}
const char* ServerList::GetServerInfoText(ServerList::ServerInfo* server, int column, bool sorting)
{
if (!server) return "";
switch (column)
{
case Column::Password:
{
return (server->password ? "X" : "");
}
case Column::Matchtype:
{
return ((server->matchType == 1) ? "P" : "M");
}
case Column::Hostname:
{
return server->hostname.data();
}
case Column::Mapname:
{
if (server->svRunning)
{
if (!sorting && !Maps::CheckMapInstalled(server->mapname.data()))
{
return Utils::String::VA("^1%s", Game::UI_LocalizeMapName(server->mapname.data()));
}
return Game::UI_LocalizeMapName(server->mapname.data());
}
else
{
return Utils::String::VA("^3%s", Game::UI_LocalizeMapName(server->mapname.data()));
}
}
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());
}
case Column::Mod:
{
if (server->mod != "")
{
return (server->mod.data() + 5);
}
return "";
}
case Column::Ping:
{
if (server->ping < 75) // Below this is a good ping
{
return Utils::String::VA("^2%i", server->ping);
}
else if (server->ping < 150) // Below this is a medium ping
{
return Utils::String::VA("^3%i", server->ping);
}
else
{
return Utils::String::VA("^1%i", server->ping);
}
}
default:
{
break;
};
}
return "";
}
void ServerList::SelectServer(unsigned int index)
{
ServerList::CurrentServer = index;
ServerList::ServerInfo* info = ServerList::GetCurrentServer();
if (info)
{
2022-04-12 17:15:50 -04:00
ServerList::UIServerSelected.set(true);
ServerList::UIServerSelectedMap.set(info->mapname);
Dvar::Var("ui_serverSelectedGametype").set(info->gametype);
}
else
{
2022-04-12 17:15:50 -04:00
ServerList::UIServerSelected.set(false);
}
}
void ServerList::UpdateVisibleList(UIScript::Token)
{
auto list = ServerList::GetList();
if (!list) return;
std::vector<ServerList::ServerInfo> tempList(*list);
if (tempList.empty())
{
ServerList::Refresh(UIScript::Token());
}
else
{
list->clear();
2017-02-28 13:58:03 -05:00
std::lock_guard<std::recursive_mutex> _(ServerList::RefreshContainer.mutex);
ServerList::RefreshContainer.sendCount = 0;
ServerList::RefreshContainer.sentCount = 0;
2017-05-30 14:31:00 -04:00
for (auto& server : tempList)
{
2017-02-28 13:58:03 -05:00
ServerList::InsertRequest(server.addr);
}
}
}
void ServerList::RefreshVisibleList(UIScript::Token)
2022-05-07 09:23:26 -04:00
{
ServerList::RefreshVisibleListInternal(UIScript::Token());
}
void ServerList::RefreshVisibleListInternal(UIScript::Token, bool refresh)
{
Dvar::Var("ui_serverSelected").set(false);
ServerList::VisibleList.clear();
auto list = ServerList::GetList();
if (!list) return;
2022-05-07 09:23:26 -04:00
if (refresh)
{
ServerList::Refresh(UIScript::Token());
return;
}
bool ui_browserShowFull = Dvar::Var("ui_browserShowFull").get<bool>();
bool ui_browserShowEmpty = Dvar::Var("ui_browserShowEmpty").get<bool>();
int ui_browserShowHardcore = Dvar::Var("ui_browserKillcam").get<int>();
int ui_browserShowPassword = Dvar::Var("ui_browserShowPassword").get<int>();
int ui_browserMod = Dvar::Var("ui_browserMod").get<int>();
int ui_joinGametype = Dvar::Var("ui_joinGametype").get<int>();
for (unsigned int i = 0; i < list->size(); ++i)
{
ServerList::ServerInfo* info = &(*list)[i];
// Filter full servers
if (!ui_browserShowFull && info->clients >= info->maxClients) continue;
// Filter empty servers
if (!ui_browserShowEmpty && info->clients <= 0) continue;
// Filter hardcore servers
if ((ui_browserShowHardcore == 0 && info->hardcore) || (ui_browserShowHardcore == 1 && !info->hardcore)) continue;
// Filter servers with password
if ((ui_browserShowPassword == 0 && info->password) || (ui_browserShowPassword == 1 && !info->password)) continue;
// Don't show modded servers
if ((ui_browserMod == 0 && info->mod.size()) || (ui_browserMod == 1 && !info->mod.size())) continue;
// Filter by gametype
if (ui_joinGametype > 0 && (ui_joinGametype - 1) < *Game::gameTypeCount && Game::gameTypes[(ui_joinGametype - 1)].gameType != info->gametype) continue;
ServerList::VisibleList.push_back(i);
}
ServerList::SortList();
}
void ServerList::Refresh(UIScript::Token)
{
Dvar::Var("ui_serverSelected").set(false);
//Localization::Set("MPUI_SERVERQUERIED", "Sent requests: 0/0");
// ServerList::OnlineList.clear();
// ServerList::OfflineList.clear();
// ServerList::FavouriteList.clear();
auto list = ServerList::GetList();
if (list) list->clear();
ServerList::VisibleList.clear();
2017-05-30 14:31:00 -04:00
{
std::lock_guard<std::recursive_mutex> _(ServerList::RefreshContainer.mutex);
ServerList::RefreshContainer.servers.clear();
ServerList::RefreshContainer.sendCount = 0;
ServerList::RefreshContainer.sentCount = 0;
}
if (ServerList::IsOfflineList())
{
Discovery::Perform();
}
else if (ServerList::IsOnlineList())
{
const auto masterPort = Dvar::Var("masterPort").get<int>();
const auto masterServerName = Dvar::Var("masterServerName").get<const char*>();
// Check if our dvars can properly convert to a address
Game::netadr_t masterServerAddr;
if (!ServerList::GetMasterServer(masterServerName, masterPort, masterServerAddr))
{
2022-06-12 17:07:53 -04:00
Logger::Print("Could not resolve address for {}:{}", masterServerName, masterPort);
Toast::Show("cardicon_headshot", "^1Error", Utils::String::VA("Could not resolve address for %s:%u", masterServerName, masterPort), 5000);
2022-06-25 12:21:12 -04:00
useMasterServer = false;
return;
}
Toast::Show("cardicon_headshot", "Server Browser", "Fetching servers...", 3000);
useMasterServer = true;
ServerList::RefreshContainer.awatingList = true;
ServerList::RefreshContainer.awaitTime = Game::Sys_Milliseconds();
ServerList::RefreshContainer.host = Network::Address(Utils::String::VA("%s:%u", masterServerName, masterPort));
Logger::Print("Sending serverlist request to master\n");
Network::SendCommand(ServerList::RefreshContainer.host, "getservers", Utils::String::VA("IW4 %i full empty", PROTOCOL));
}
else if (ServerList::IsFavouriteList())
{
ServerList::LoadFavourties();
}
}
2018-12-17 08:29:18 -05:00
void ServerList::StoreFavourite(const std::string& server)
{
//json11::Json::parse()
std::vector<std::string> servers;
if (Utils::IO::FileExists("players/favourites.json"))
{
std::string data = Utils::IO::ReadFile("players/favourites.json");
json11::Json object = json11::Json::parse(data, data);
if (!object.is_array())
{
Logger::Print("Favourites storage file is invalid!\n");
Game::ShowMessageBox("Favourites storage file is invalid!", "Error");
return;
}
auto storedServers = object.array_items();
for (unsigned int i = 0; i < storedServers.size(); ++i)
{
if (!storedServers[i].is_string()) continue;
if (storedServers[i].string_value() == server)
{
Game::ShowMessageBox("Server already marked as favourite.", "Error");
return;
}
servers.push_back(storedServers[i].string_value());
}
}
servers.push_back(server);
json11::Json data = json11::Json(servers);
Utils::IO::WriteFile("players/favourites.json", data.dump());
Game::ShowMessageBox("Server added to favourites.", "Success");
}
2018-12-17 08:29:18 -05:00
void ServerList::RemoveFavourite(const std::string& server)
{
std::vector<std::string> servers;
if (Utils::IO::FileExists("players/favourites.json"))
{
std::string data = Utils::IO::ReadFile("players/favourites.json");
json11::Json object = json11::Json::parse(data, data);
if (!object.is_array())
{
Logger::Print("Favourites storage file is invalid!\n");
Game::ShowMessageBox("Favourites storage file is invalid!", "Error");
return;
}
for (auto& storedServer : object.array_items())
{
if (storedServer.is_string() && storedServer.string_value() != server)
{
servers.push_back(storedServer.string_value());
}
}
}
json11::Json data = json11::Json(servers);
Utils::IO::WriteFile("players/favourites.json", data.dump());
auto list = ServerList::GetList();
if (list) list->clear();
2022-05-07 09:23:26 -04:00
ServerList::RefreshVisibleListInternal(UIScript::Token());
Game::ShowMessageBox("Server removed from favourites.", "Success");
}
void ServerList::LoadFavourties()
{
if (ServerList::IsFavouriteList() && Utils::IO::FileExists("players/favourites.json"))
{
auto list = ServerList::GetList();
if (list) list->clear();
std::string data = Utils::IO::ReadFile("players/favourites.json");
json11::Json object = json11::Json::parse(data, data);
if (!object.is_array())
{
Logger::Print("Favourites storage file is invalid!\n");
Game::ShowMessageBox("Favourites storage file is invalid!", "Error");
return;
}
auto servers = object.array_items();
for (unsigned int i = 0; i < servers.size(); ++i)
{
if (!servers[i].is_string()) continue;
2017-02-28 13:58:03 -05:00
ServerList::InsertRequest(servers[i].string_value());
}
}
}
2017-02-28 13:58:03 -05:00
void ServerList::InsertRequest(Network::Address address)
{
2017-02-28 13:58:03 -05:00
std::lock_guard<std::recursive_mutex> _(ServerList::RefreshContainer.mutex);
ServerList::Container::ServerContainer container;
container.sent = false;
container.target = address;
bool alreadyInserted = false;
for (auto &server : ServerList::RefreshContainer.servers)
{
if (server.target == container.target)
{
alreadyInserted = true;
break;
}
}
if (!alreadyInserted)
{
ServerList::RefreshContainer.servers.push_back(container);
auto list = ServerList::GetList();
if (list)
{
2017-05-30 14:31:00 -04:00
for (auto& server : *list)
{
if (server.addr == container.target)
{
--ServerList::RefreshContainer.sendCount;
--ServerList::RefreshContainer.sentCount;
break;
}
}
}
++ServerList::RefreshContainer.sendCount;
}
}
2022-06-04 04:56:14 -04:00
void ServerList::Insert(const Network::Address& address, const Utils::InfoString& info)
{
2017-02-28 13:58:03 -05:00
std::lock_guard<std::recursive_mutex> _(ServerList::RefreshContainer.mutex);
for (auto i = ServerList::RefreshContainer.servers.begin(); i != ServerList::RefreshContainer.servers.end();)
{
// Our desired server
if (i->target == address && i->sent)
{
// 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");
server.matchType = atoi(info.get("matchtype").data());
server.clients = atoi(info.get("clients").data());
2017-06-12 15:01:56 -04:00
server.bots = atoi(info.get("bots").data());
server.securityLevel = atoi(info.get("securityLevel").data());
server.maxClients = atoi(info.get("sv_maxclients").data());
server.password = (atoi(info.get("isPrivate").data()) != 0);
server.hardcore = (atoi(info.get("hc").data()) != 0);
server.svRunning = (atoi(info.get("sv_running").data()) != 0);
server.ping = (Game::Sys_Milliseconds() - i->sendTime);
server.addr = address;
server.hostname = TextRenderer::StripMaterialTextIcons(server.hostname);
server.mapname = TextRenderer::StripMaterialTextIcons(server.mapname);
server.gametype = TextRenderer::StripMaterialTextIcons(server.gametype);
server.mod = TextRenderer::StripMaterialTextIcons(server.mod);
// Remove server from queue
i = ServerList::RefreshContainer.servers.erase(i);
// 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)
return;
// Check if already inserted and remove
auto list = ServerList::GetList();
if (!list) return;
unsigned int k = 0;
for (auto j = list->begin(); j != list->end(); ++k)
{
if (j->addr == address)
{
j = list->erase(j);
}
else
{
++j;
}
}
// Also remove from visible list
for (auto j = ServerList::VisibleList.begin(); j != ServerList::VisibleList.end();)
{
if (*j == k)
{
j = ServerList::VisibleList.erase(j);
}
else
{
++j;
}
}
if (info.get("gamename") == "IW4"
&& server.matchType
#if !defined(DEBUG) && defined(VERSION_FILTER)
&& ServerList::CompareVersion(server.shortversion, SHORTVERSION)
#endif
)
{
auto lList = ServerList::GetList();
if (lList)
{
lList->push_back(server);
2022-05-07 09:23:26 -04:00
ServerList::RefreshVisibleListInternal(UIScript::Token());
}
}
break;
}
else
{
++i;
}
}
}
2018-12-17 08:29:18 -05:00
bool ServerList::CompareVersion(const std::string& version1, const std::string& version2)
{
2022-02-26 18:02:04 -05:00
auto subVersions1 = Utils::String::Split(version1, '.');
auto subVersions2 = Utils::String::Split(version2, '.');
while (subVersions1.size() >= 3) subVersions1.pop_back();
while (subVersions2.size() >= 3) subVersions2.pop_back();
if (subVersions1.size() != subVersions2.size()) return false;
for (unsigned int i = 0; i < subVersions1.size(); ++i)
{
if (atoi(subVersions1[i].data()) != atoi(subVersions2[i].data()))
{
return false;
}
}
return true;
}
ServerList::ServerInfo* ServerList::GetCurrentServer()
{
return ServerList::GetServer(ServerList::CurrentServer);
}
void ServerList::SortList()
{
2017-06-05 10:12:15 -04:00
// Only sort when the serverlist is open
2022-05-03 05:10:48 -04:00
if (!ServerList::IsServerListOpen()) return;
2017-07-12 07:39:45 -04:00
std::stable_sort(ServerList::VisibleList.begin(), ServerList::VisibleList.end(), [](const unsigned int &server1, const unsigned int &server2) -> bool
2017-06-05 10:12:15 -04:00
{
ServerInfo* info1 = nullptr;
ServerInfo* info2 = nullptr;
auto list = ServerList::GetList();
2017-06-08 11:55:18 -04:00
if (!list) return false;
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;
// Numerical comparisons
if (ServerList::SortKey == ServerList::Column::Ping)
{
2017-06-08 11:55:18 -04:00
return info1->ping < info2->ping;
}
else if (ServerList::SortKey == ServerList::Column::Players)
{
2017-06-08 11:55:18 -04:00
return info1->clients < info2->clients;
}
std::string text1 = Utils::String::ToLower(TextRenderer::StripColors(ServerList::GetServerInfoText(info1, ServerList::SortKey, true)));
std::string text2 = Utils::String::ToLower(TextRenderer::StripColors(ServerList::GetServerInfoText(info2, ServerList::SortKey, true)));
// ASCII-based comparison
2017-06-08 11:55:18 -04:00
return text1.compare(text2) < 0;
});
2017-06-08 11:55:18 -04:00
if (!ServerList::SortAsc) std::reverse(ServerList::VisibleList.begin(), ServerList::VisibleList.end());
}
ServerList::ServerInfo* ServerList::GetServer(unsigned int index)
{
if (ServerList::VisibleList.size() > index)
{
auto list = ServerList::GetList();
if (!list) return nullptr;
if (list->size() > ServerList::VisibleList[index])
{
return &(*list)[ServerList::VisibleList[index]];
}
}
return nullptr;
}
void ServerList::Frame()
{
static Utils::Time::Interval frameLimit;
2022-04-12 17:15:50 -04:00
const auto interval = static_cast<int>(1000.0f / ServerList::NETServerFrames.get<int>());
if (!frameLimit.elapsed(std::chrono::milliseconds(interval)))
return;
frameLimit.update();
2017-05-30 14:31:00 -04:00
std::lock_guard<std::recursive_mutex> _(ServerList::RefreshContainer.mutex);
if (ServerList::RefreshContainer.awatingList)
{
// Stop counting if we are out of the server browser menu
2022-05-03 05:10:48 -04:00
if (!ServerList::IsServerListOpen())
{
ServerList::RefreshContainer.awatingList = false;
}
// Check if we haven't got a response within 5 seconds
if (Game::Sys_Milliseconds() - ServerList::RefreshContainer.awaitTime > 5000)
{
ServerList::RefreshContainer.awatingList = false;
2022-06-12 17:07:53 -04:00
Logger::Print("We haven't received a response from the master within {} seconds!\n", (Game::Sys_Milliseconds() - ServerList::RefreshContainer.awaitTime) / 1000);
Toast::Show("cardicon_headshot", "^1Error", "Failed to reach master server, using node servers instead.", 5000);
useMasterServer = false;
Node::Synchronize();
}
}
2022-04-12 17:15:50 -04:00
auto requestLimit = ServerList::NETServerQueryLimit.get<int>();
for (unsigned int i = 0; i < ServerList::RefreshContainer.servers.size() && requestLimit > 0; ++i)
{
ServerList::Container::ServerContainer* server = &ServerList::RefreshContainer.servers[i];
if (server->sent) continue;
// Found server we can send a request to
server->sent = true;
requestLimit--;
server->sendTime = Game::Sys_Milliseconds();
server->challenge = Utils::Cryptography::Rand::GenerateChallenge();
++ServerList::RefreshContainer.sentCount;
Network::SendCommand(server->target, "getinfo", server->challenge);
// Display in the menu, like in COD4
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-02-26 09:27:02 -05:00
ServerList::UpdateVisibleInfo();
}
void ServerList::UpdateSource()
{
Dvar::Var netSource("ui_netSource");
int source = netSource.get<int>();
2018-05-09 06:04:20 -04:00
if (++source > netSource.get<Game::dvar_t*>()->domain.integer.max)
{
source = 0;
}
netSource.set(source);
2022-05-07 09:23:26 -04:00
ServerList::RefreshVisibleListInternal(UIScript::Token(), true);
}
void ServerList::UpdateGameType()
{
Dvar::Var joinGametype("ui_joinGametype");
int gametype = joinGametype.get<int>();
if (++gametype > *Game::gameTypeCount)
{
gametype = 0;
}
joinGametype.set(gametype);
2022-05-07 09:23:26 -04:00
ServerList::RefreshVisibleListInternal(UIScript::Token());
}
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
auto list = ServerList::GetList();
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
for (unsigned int i = 0; i < list->size(); ++i)
{
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 03:01:54 -04:00
return Game::NET_StringToAdr(Utils::String::VA("%s:%u", ip, port), &address);
}
bool ServerList::IsServerListOpen()
{
2022-05-03 05:10:48 -04:00
auto* menu = Game::Menus_FindByName(Game::uiContext, "pc_join_unranked");
if (!menu)
return false;
return Game::Menu_IsVisible(Game::uiContext, menu);
}
ServerList::ServerList()
{
ServerList::OnlineList.clear();
2017-02-26 09:27:02 -05:00
ServerList::OfflineList.clear();
ServerList::FavouriteList.clear();
ServerList::VisibleList.clear();
Scheduler::Once([]
{
2022-04-12 17:15:50 -04:00
ServerList::UIServerSelected = Dvar::Register<bool>("ui_serverSelected", false,
Game::DVAR_NONE, "Whether a server has been selected in the serverlist");
2022-04-12 17:15:50 -04:00
ServerList::UIServerSelectedMap = Dvar::Register<const char*>("ui_serverSelectedMap", "mp_afghan",
Game::DVAR_NONE, "Map of the selected server");
2022-04-12 17:15:50 -04:00
ServerList::NETServerQueryLimit = Dvar::Register<int>("net_serverQueryLimit", 1,
1, 10, Dedicated::IsEnabled() ? Game::DVAR_NONE : Game::DVAR_ARCHIVE, "Amount of server queries per frame");
2022-04-12 17:15:50 -04:00
ServerList::NETServerFrames = Dvar::Register<int>("net_serverFrames", 30,
1, 60, Dedicated::IsEnabled() ? Game::DVAR_NONE : Game::DVAR_ARCHIVE, "Amount of server query frames per second");
}, Scheduler::Pipeline::MAIN);
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
Dvar::Register<int>("ui_netSource", 1, 0, 2, Game::DVAR_ARCHIVE, reinterpret_cast<const char*>(0x6D9F08));
2017-03-24 13:28:03 -04:00
2017-02-26 09:27:02 -05:00
//Localization::Set("MPUI_SERVERQUERIED", "Sent requests: 0/0");
2017-06-25 16:07:16 -04:00
Localization::Set("MPUI_SERVERQUERIED", "Servers: 0\nPlayers: 0 (0)");
2022-05-20 18:12:46 -04:00
Network::OnPacket("getServersResponse", [](const Network::Address& address, [[maybe_unused]] const std::string& data)
{
if (ServerList::RefreshContainer.host != address) return; // Only parse from host we sent to
ServerList::RefreshContainer.awatingList = false;
2017-02-28 13:58:03 -05:00
std::lock_guard<std::recursive_mutex> _(ServerList::RefreshContainer.mutex);
int offset = 0;
int count = ServerList::RefreshContainer.servers.size();
ServerList::MasterEntry* entry = nullptr;
// Find first entry
do
{
entry = reinterpret_cast<ServerList::MasterEntry*>(const_cast<char*>(data.data()) + offset++);
} while (!entry->HasSeparator() && !entry->IsEndToken());
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);
2017-02-28 13:58:03 -05:00
ServerList::InsertRequest(serverAddr);
}
2022-06-12 17:07:53 -04:00
Logger::Print("Parsed {} servers from master\n", ServerList::RefreshContainer.servers.size() - count);
});
// Set default masterServerName + port and save it
2022-05-03 05:10:48 -04:00
Utils::Hook::Set<const char*>(0x60AD92, "master.xlabs.dev");
Utils::Hook::Set<BYTE>(0x60AD90, Game::DVAR_NONE); // masterServerName
Utils::Hook::Set<BYTE>(0x60ADC6, Game::DVAR_NONE); // masterPort
// Add server list feeder
UIFeeder::Add(2.0f, ServerList::GetServerCount, ServerList::GetServerText, ServerList::SelectServer);
// Add required UIScripts
UIScript::Add("UpdateFilter", ServerList::RefreshVisibleList);
UIScript::Add("RefreshFilter", ServerList::UpdateVisibleList);
UIScript::Add("RefreshServers", ServerList::Refresh);
2022-04-12 17:15:50 -04:00
UIScript::Add("JoinServer", [](UIScript::Token)
{
ServerList::ServerInfo* info = ServerList::GetServer(ServerList::CurrentServer);
if (info)
{
Party::Connect(info->addr);
}
});
2022-04-12 17:15:50 -04:00
UIScript::Add("ServerSort", [](UIScript::Token token)
{
int key = token.get<int>();
if (ServerList::SortKey == key)
{
ServerList::SortAsc = !ServerList::SortAsc;
}
else
{
ServerList::SortKey = key;
ServerList::SortAsc = true;
}
2022-06-12 17:07:53 -04:00
Logger::Print("Sorting server list by token: {}\n", ServerList::SortKey);
ServerList::SortList();
});
2022-04-12 17:15:50 -04:00
UIScript::Add("CreateListFavorite", [](UIScript::Token)
{
ServerList::ServerInfo* info = ServerList::GetCurrentServer();
if (info)
{
ServerList::StoreFavourite(info->addr.getString());
}
});
2022-04-12 17:15:50 -04:00
UIScript::Add("CreateFavorite", [](UIScript::Token)
{
ServerList::StoreFavourite(Dvar::Var("ui_favoriteAddress").get<std::string>());
});
2022-04-12 17:15:50 -04:00
UIScript::Add("CreateCurrentServerFavorite", [](UIScript::Token)
{
if (Game::CL_IsCgameInitialized())
{
std::string addressText = Network::Address(*Game::connectedHost).getString();
if (addressText != "0.0.0.0:0" && addressText != "loopback")
{
ServerList::StoreFavourite(addressText);
}
}
});
2022-04-12 17:15:50 -04:00
UIScript::Add("DeleteFavorite", [](UIScript::Token)
{
ServerList::ServerInfo* info = ServerList::GetCurrentServer();
if (info)
{
ServerList::RemoveFavourite(info->addr.getString());
};
});
2022-04-12 17:15:50 -04:00
#ifdef _DEBUG
Command::Add("playerCount", [](Command::Params*)
{
2022-04-12 17:15:50 -04:00
auto count = 0;
for (const auto& server : ServerList::OnlineList)
{
count += server.clients;
}
Logger::Debug("There are {} players playing", count);
});
2022-04-12 17:15:50 -04:00
#endif
// Add required ownerDraws
UIScript::AddOwnerDraw(220, ServerList::UpdateSource);
UIScript::AddOwnerDraw(253, ServerList::UpdateGameType);
// Add frame callback
Scheduler::Loop(ServerList::Frame, Scheduler::Pipeline::CLIENT);
}
ServerList::~ServerList()
{
2022-03-15 20:44:59 -04:00
std::lock_guard<std::recursive_mutex> _(ServerList::RefreshContainer.mutex);
ServerList::RefreshContainer.awatingList = false;
ServerList::RefreshContainer.servers.clear();
}
}