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

528 lines
14 KiB
C++
Raw Normal View History

#include "STDInclude.hpp"
2016-01-04 04:58:58 -05:00
#include "..\..\Utils\Versioning.hpp"
2015-12-27 22:02:30 -05:00
namespace Components
{
2016-01-02 09:19:34 -05:00
bool ServerList::SortAsc = true;
int ServerList::SortKey = ServerList::Column::Ping;
2016-01-03 18:00:07 -05:00
2015-12-28 08:08:46 -05:00
unsigned int ServerList::CurrentServer = 0;
2015-12-27 22:02:30 -05:00
ServerList::Container ServerList::RefreshContainer;
2016-01-03 18:00:07 -05:00
2015-12-27 22:02:30 -05:00
std::vector<ServerList::ServerInfo> ServerList::OnlineList;
2016-01-03 18:00:07 -05:00
std::vector<ServerList::ServerInfo> ServerList::OfflineList;
std::vector<ServerList::ServerInfo> ServerList::FavouriteList;
2016-01-02 09:19:34 -05:00
std::vector<int> ServerList::VisibleList;
2015-12-27 22:02:30 -05:00
2016-01-03 18:00:07 -05:00
std::vector<ServerList::ServerInfo>& ServerList::GetList()
{
int source = Dvar::Var("ui_netSource").Get<int>();
if (ServerList::IsFavouriteList())
{
return ServerList::FavouriteList;
}
else if (ServerList::IsOfflineList())
{
return ServerList::OfflineList;
}
else
{
return ServerList::OnlineList;
}
}
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 (Dvar::Var("ui_netSource").Get<int>() == 1);
}
2015-12-27 22:02:30 -05:00
int ServerList::GetServerCount()
{
2016-01-02 09:19:34 -05:00
return (int)ServerList::VisibleList.size();
2015-12-27 22:02:30 -05:00
}
const char* ServerList::GetServerText(int index, int column)
{
2016-01-02 09:19:34 -05:00
return ServerList::GetServerText(ServerList::GetServer(index), column);
}
2015-12-27 22:02:30 -05:00
2016-01-02 09:19:34 -05:00
const char* ServerList::GetServerText(ServerList::ServerInfo* server, int column)
{
if (!server) return "";
2015-12-27 22:02:30 -05:00
switch (column)
{
case Column::Password:
{
2016-01-02 09:19:34 -05:00
return (server->Password ? "X" : "");
2015-12-27 22:02:30 -05:00
}
2016-01-03 19:30:15 -05:00
case Column::Matchtype:
{
return ((server->MatchType == 1) ? "L" : "D");
}
2015-12-27 22:02:30 -05:00
case Column::Hostname:
{
2016-01-02 09:19:34 -05:00
return server->Hostname.data();
2015-12-27 22:02:30 -05:00
}
case Column::Mapname:
{
2016-01-02 09:19:34 -05:00
return Game::UI_LocalizeMapName(server->Mapname.data());
2015-12-27 22:02:30 -05:00
}
case Column::Players:
{
2016-01-02 09:19:34 -05:00
return Utils::VA("%i (%i)", server->Clients, server->MaxClients);
2015-12-27 22:02:30 -05:00
}
case Column::Gametype:
2016-01-03 19:30:15 -05:00
{
return Game::UI_LocalizeGameType(server->Gametype.data());
}
case Column::Mod:
2015-12-27 22:02:30 -05:00
{
2016-01-02 09:19:34 -05:00
if (server->Mod != "")
2015-12-27 22:02:30 -05:00
{
2016-01-02 09:19:34 -05:00
return (server->Mod.data() + 5);
2015-12-27 22:02:30 -05:00
}
2016-01-03 19:30:15 -05:00
return "";
2015-12-27 22:02:30 -05:00
}
case Column::Ping:
{
2016-01-02 09:19:34 -05:00
return Utils::VA("%i", server->Ping);
2015-12-27 22:02:30 -05:00
}
}
return "";
}
void ServerList::SelectServer(int index)
{
2015-12-28 08:08:46 -05:00
ServerList::CurrentServer = (unsigned int)index;
2015-12-27 22:02:30 -05:00
}
2016-01-03 19:30:15 -05:00
void ServerList::RefreshVisibleList()
{
2016-01-07 15:28:39 -05:00
ServerList::VisibleList.clear();
auto list = ServerList::GetList();
// Refresh entirely, if there is no entry in the list
if (!list.size())
{
ServerList::Refresh();
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();
2016-01-03 19:30:15 -05:00
}
2015-12-27 22:02:30 -05:00
void ServerList::Refresh()
{
2016-01-04 12:28:11 -05:00
Localization::Set("MPUI_SERVERQUERIED", "Sent requests: 0/0");
2016-01-03 18:00:07 -05:00
// ServerList::OnlineList.clear();
// ServerList::OfflineList.clear();
// ServerList::FavouriteList.clear();
ServerList::GetList().clear();
2016-01-02 09:19:34 -05:00
ServerList::VisibleList.clear();
2015-12-27 22:02:30 -05:00
ServerList::RefreshContainer.Mutex.lock();
ServerList::RefreshContainer.Servers.clear();
ServerList::RefreshContainer.Mutex.unlock();
2015-12-28 09:46:08 -05:00
ServerList::RefreshContainer.SendCount = 0;
ServerList::RefreshContainer.SentCount = 0;
2016-01-03 18:00:07 -05:00
if (ServerList::IsOfflineList())
{
Discovery::Perform();
}
else if (ServerList::IsOnlineList())
{
ServerList::RefreshContainer.AwatingList = true;
ServerList::RefreshContainer.AwaitTime = Game::Com_Milliseconds();
2015-12-28 11:52:13 -05:00
2016-01-03 18:00:07 -05:00
int masterPort = Dvar::Var("masterPort").Get<int>();
const char* masterServerName = Dvar::Var("masterServerName").Get<const char*>();
2015-12-27 22:02:30 -05:00
2016-01-03 18:00:07 -05:00
ServerList::RefreshContainer.Host = Network::Address(Utils::VA("%s:%u", masterServerName, masterPort));
2015-12-27 22:02:30 -05:00
2016-01-03 18:00:07 -05:00
Logger::Print("Sending serverlist request to master: %s:%u\n", masterServerName, masterPort);
2015-12-28 11:52:13 -05:00
2016-01-07 15:28:39 -05:00
//Network::Send(ServerList::RefreshContainer.Host, Utils::VA("getservers IW4 %i full empty", PROTOCOL));
Network::Send(ServerList::RefreshContainer.Host, "getservers 0 full empty\n");
2016-01-03 18:00:07 -05:00
}
else if (ServerList::IsFavouriteList())
{
// TODO: Whatever
}
}
2016-01-04 04:54:31 -05:00
void ServerList::InsertRequest(Network::Address address, bool acquireMutex)
2016-01-03 18:00:07 -05:00
{
2016-01-04 04:54:31 -05:00
if (acquireMutex) ServerList::RefreshContainer.Mutex.lock();
2016-01-03 18:00:07 -05:00
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);
ServerList::RefreshContainer.SendCount++;
}
2016-01-04 04:54:31 -05:00
if (acquireMutex) ServerList::RefreshContainer.Mutex.unlock();
2015-12-27 22:02:30 -05:00
}
void ServerList::Insert(Network::Address address, Utils::InfoString info)
{
ServerList::RefreshContainer.Mutex.lock();
for (auto i = ServerList::RefreshContainer.Servers.begin(); i != ServerList::RefreshContainer.Servers.end(); i++)
{
// Our desired server
2015-12-28 09:46:08 -05:00
if (i->Target == address && i->Sent)
2015-12-27 22:02:30 -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");
2016-01-01 11:36:47 -05:00
server.Shortversion = info.Get("shortversion");
2015-12-27 22:02:30 -05:00
server.Mod = info.Get("fs_game");
server.MatchType = atoi(info.Get("matchtype").data());
server.Clients = atoi(info.Get("clients").data());
server.MaxClients = atoi(info.Get("sv_maxclients").data());
2016-01-04 12:28:11 -05:00
server.Password = (atoi(info.Get("isPrivate").data()) != 0);
server.Hardcore = (atoi(info.Get("hc").data()) != 0);
2015-12-27 22:02:30 -05:00
server.Ping = (Game::Com_Milliseconds() - i->SendTime);
2015-12-28 08:08:46 -05:00
server.Addr = address;
2015-12-27 22:02:30 -05:00
2016-01-03 21:27:43 -05:00
// Remove server from queue
ServerList::RefreshContainer.Servers.erase(i);
2015-12-28 09:46:08 -05:00
// Check if already inserted and remove
2016-01-02 09:19:34 -05:00
int k = 0;
2016-01-03 18:00:07 -05:00
for (auto j = ServerList::GetList().begin(); j != ServerList::GetList().end(); j++, k++)
2015-12-28 09:46:08 -05:00
{
if (j->Addr == address)
{
2016-01-03 18:00:07 -05:00
ServerList::GetList().erase(j);
2015-12-28 09:46:08 -05:00
break;
}
}
2016-01-02 09:19:34 -05:00
// Also remove from visible list
for (auto j = ServerList::VisibleList.begin(); j != ServerList::VisibleList.end(); j++)
{
if (*j == k)
{
ServerList::VisibleList.erase(j);
}
}
2016-01-07 15:28:39 -05:00
//if (info.Get("gamename") == "IW4" && server.MatchType && server.Shortversion == VERSION_STR)
2015-12-29 18:13:12 -05:00
{
2016-01-03 18:00:07 -05:00
ServerList::GetList().push_back(server);
2016-01-07 15:28:39 -05:00
ServerList::RefreshVisibleList();
2015-12-29 18:13:12 -05:00
}
2015-12-27 22:02:30 -05:00
break;
}
}
2015-12-28 09:46:08 -05:00
ServerList::RefreshContainer.Mutex.unlock();
}
2016-01-02 09:23:04 -05:00
void ServerList::SortList()
2016-01-02 09:19:34 -05:00
{
qsort(ServerList::VisibleList.data(), ServerList::VisibleList.size(), sizeof(int), [] (const void* first, const void* second)
{
int server1 = *(int*)first;
int server2 = *(int*)second;
ServerInfo* info1 = nullptr;
ServerInfo* info2 = nullptr;
2016-01-03 19:30:15 -05:00
if (ServerList::GetList().size() > (unsigned int)server1) info1 = &ServerList::GetList()[server1];
if (ServerList::GetList().size() > (unsigned int)server2) info2 = &ServerList::GetList()[server2];
2016-01-02 09:19:34 -05:00
if (!info1) return 1;
if (!info2) return -1;
// Numerical comparisons
2016-01-02 09:23:04 -05:00
if (ServerList::SortKey == ServerList::Column::Ping)
2016-01-02 09:19:34 -05:00
{
return ((info1->Ping - info2->Ping) * (ServerList::SortAsc ? 1 : -1));
}
2016-01-02 09:23:04 -05:00
else if (ServerList::SortKey == ServerList::Column::Players)
2016-01-02 09:19:34 -05:00
{
return ((info1->Clients - info2->Clients) * (ServerList::SortAsc ? 1 : -1));
}
2016-01-02 09:23:04 -05:00
std::string text1 = Colors::Strip(ServerList::GetServerText(info1, ServerList::SortKey));
std::string text2 = Colors::Strip(ServerList::GetServerText(info2, ServerList::SortKey));
2016-01-02 09:19:34 -05:00
// ASCII-based comparison
return (text1.compare(text2) * (ServerList::SortAsc ? 1 : -1));
});
}
ServerList::ServerInfo* ServerList::GetServer(int index)
{
if (ServerList::VisibleList.size() > (unsigned int)index)
{
2016-01-03 18:00:07 -05:00
if (ServerList::GetList().size() > (unsigned int)ServerList::VisibleList[index])
2016-01-02 09:19:34 -05:00
{
2016-01-03 18:00:07 -05:00
return &ServerList::GetList()[ServerList::VisibleList[index]];
2016-01-02 09:19:34 -05:00
}
}
return nullptr;
}
2015-12-28 09:46:08 -05:00
void ServerList::Frame()
{
2016-01-04 04:54:31 -05:00
if (!ServerList::RefreshContainer.Mutex.try_lock()) return;
2015-12-28 09:46:08 -05:00
2015-12-28 11:52:13 -05:00
if (ServerList::RefreshContainer.AwatingList)
{
// Check if we haven't got a response within 10 seconds
if (Game::Com_Milliseconds() - ServerList::RefreshContainer.AwaitTime > 5000)
{
ServerList::RefreshContainer.AwatingList = false;
Logger::Print("We haven't received a response from the master within %d seconds!\n", (Game::Com_Milliseconds() - ServerList::RefreshContainer.AwaitTime) / 1000);
}
}
2015-12-28 09:46:08 -05:00
// Send requests to 10 servers each frame
int SendServers = 10;
for (unsigned int i = 0; i < ServerList::RefreshContainer.Servers.size(); i++)
{
ServerList::Container::ServerContainer* server = &ServerList::RefreshContainer.Servers[i];
if (server->Sent) continue;
// Found server we can send a request to
server->Sent = true;
SendServers--;
server->SendTime = Game::Com_Milliseconds();
server->Challenge = Utils::VA("%d", server->SendTime);
ServerList::RefreshContainer.SentCount++;
Network::Send(server->Target, Utils::VA("getinfo %s\n", server->Challenge.data()));
// Display in the menu, like in COD4
2016-01-04 12:28:11 -05:00
Localization::Set("MPUI_SERVERQUERIED", Utils::VA("Sent requests: %d/%d", ServerList::RefreshContainer.SentCount, ServerList::RefreshContainer.SendCount));
2015-12-28 09:46:08 -05:00
if (SendServers <= 0) break;
}
2015-12-27 22:02:30 -05:00
ServerList::RefreshContainer.Mutex.unlock();
}
2016-01-03 19:30:15 -05:00
void ServerList::UpdateSource()
{
Dvar::Var netSource("ui_netSource");
int source = netSource.Get<int>();
if (++source > netSource.Get<Game::dvar_t*>()->max.i)
{
source = 0;
}
netSource.Set(source);
ServerList::RefreshVisibleList();
}
2016-01-07 15:28:39 -05:00
void ServerList::UpdateGameType()
{
Dvar::Var joinGametype("ui_joinGametype");
int gametype = joinGametype.Get<int>();
if (++gametype > *Game::gameTypeCount)
{
gametype = 0;
}
joinGametype.Set(gametype);
ServerList::RefreshVisibleList();
}
2015-12-27 22:02:30 -05:00
ServerList::ServerList()
{
ServerList::OnlineList.clear();
2016-01-02 09:19:34 -05:00
ServerList::VisibleList.clear();
2015-12-27 22:02:30 -05:00
2016-01-04 12:28:11 -05:00
Localization::Set("MPUI_SERVERQUERIED", "Sent requests: 0/0");
2016-01-03 13:28:47 -05:00
2015-12-27 22:02:30 -05:00
Network::Handle("getServersResponse", [] (Network::Address address, std::string data)
{
if (ServerList::RefreshContainer.Host != address) return; // Only parse from host we sent to
2015-12-28 11:52:13 -05:00
ServerList::RefreshContainer.AwatingList = false;
2015-12-27 22:02:30 -05:00
ServerList::RefreshContainer.Mutex.lock();
2015-12-28 09:46:08 -05:00
int offset = 0;
2015-12-28 11:52:13 -05:00
int count = ServerList::RefreshContainer.Servers.size();
2015-12-28 09:46:08 -05:00
ServerList::MasterEntry* entry = nullptr;
// Find first entry
do
{
entry = (ServerList::MasterEntry*)(data.data() + offset++);
}
while (!entry->HasSeparator() && !entry->IsEndToken());
2015-12-27 22:02:30 -05:00
2015-12-28 08:53:20 -05:00
for (int i = 0; !entry[i].IsEndToken() && entry[i].HasSeparator(); i++)
2015-12-27 22:02:30 -05:00
{
2015-12-28 08:53:20 -05:00
Network::Address serverAddr = address;
serverAddr.SetIP(entry[i].IP);
2016-01-03 14:13:49 -05:00
serverAddr.SetPort(ntohs(entry[i].Port));
2016-01-03 18:00:07 -05:00
serverAddr.SetType(Game::NA_IP);
2015-12-27 22:02:30 -05:00
2016-01-03 18:00:07 -05:00
ServerList::InsertRequest(serverAddr, false);
2015-12-27 22:02:30 -05:00
}
2015-12-28 11:52:13 -05:00
Logger::Print("Parsed %d servers from master\n", ServerList::RefreshContainer.Servers.size() - count);
2015-12-28 08:53:20 -05:00
2015-12-27 22:02:30 -05:00
ServerList::RefreshContainer.Mutex.unlock();
});
2015-12-28 08:08:46 -05:00
// Set default masterServerName + port and save it
2016-01-02 09:19:34 -05:00
Utils::Hook::Set<char*>(0x60AD92, "localhost");
2015-12-28 08:08:46 -05:00
Utils::Hook::Set<BYTE>(0x60AD90, Game::dvar_flag::DVAR_FLAG_SAVED); // masterServerName
Utils::Hook::Set<BYTE>(0x60ADC6, Game::dvar_flag::DVAR_FLAG_SAVED); // masterPort
// Add server list feeder
UIFeeder::Add(2.0f, ServerList::GetServerCount, ServerList::GetServerText, ServerList::SelectServer);
// Add required UIScripts
2016-01-07 15:28:39 -05:00
UIScript::Add("UpdateFilter", ServerList::RefreshVisibleList);
UIScript::Add("RefreshFilter", ServerList::RefreshVisibleList); // TODO: Re-query the servers
2015-12-28 08:08:46 -05:00
UIScript::Add("RefreshServers", ServerList::Refresh);
UIScript::Add("JoinServer", [] ()
2015-12-27 22:02:30 -05:00
{
2016-01-02 09:19:34 -05:00
if (ServerList::GetServer(ServerList::CurrentServer))
2015-12-28 08:08:46 -05:00
{
2016-01-02 09:19:34 -05:00
Party::Connect(ServerList::GetServer(ServerList::CurrentServer)->Addr);
2015-12-28 08:08:46 -05:00
}
});
UIScript::Add("ServerSort", [] (UIScript::Token token)
{
2016-01-02 09:19:34 -05:00
int key = token.Get<int>();
if (ServerList::SortKey == key)
{
ServerList::SortAsc = !ServerList::SortAsc;
}
else
{
ServerList::SortKey = key;
ServerList::SortAsc = true;
}
Logger::Print("Sorting server list by token: %d\n", ServerList::SortKey);
2016-01-02 09:23:04 -05:00
ServerList::SortList();
2015-12-27 22:02:30 -05:00
});
2015-12-28 09:46:08 -05:00
2016-01-03 19:30:15 -05:00
// Add required ownerDraws
UIScript::AddOwnerDraw(220, ServerList::UpdateSource);
2016-01-07 15:28:39 -05:00
UIScript::AddOwnerDraw(253, ServerList::UpdateGameType);
2016-01-03 19:30:15 -05:00
2015-12-28 09:46:08 -05:00
// Add frame callback
Renderer::OnFrame(ServerList::Frame);
2015-12-27 22:02:30 -05:00
}
ServerList::~ServerList()
{
ServerList::OnlineList.clear();
2016-01-03 18:00:07 -05:00
ServerList::OfflineList.clear();
ServerList::FavouriteList.clear();
2016-01-02 09:19:34 -05:00
ServerList::VisibleList.clear();
2016-01-04 05:32:05 -05:00
ServerList::RefreshContainer.Mutex.lock();
ServerList::RefreshContainer.AwatingList = false;
ServerList::RefreshContainer.Servers.clear();
ServerList::RefreshContainer.Mutex.unlock();
2015-12-27 22:02:30 -05:00
}
}