ddnet/src/engine/client/serverbrowser.cpp

1802 lines
48 KiB
C++
Raw Normal View History

2010-11-20 10:37:14 +00:00
/* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */
/* If you are missing that file, acquire a complete release at teeworlds.com. */
2021-04-17 14:05:24 +00:00
#include "serverbrowser.h"
#include "serverbrowser_http.h"
#include "serverbrowser_ping_cache.h"
Add client-side HTTP server info Summary ======= The idea of this is that clients will not have to ping each server for server infos which takes long, leaks the client's IP address even to servers the user does not join and is a DoS vector of the game servers for attackers. For the Internet, DDNet and KoG tab, the server list is entirely fetched from the master server, filtering out servers that don't belong into the list. The favorites tab is also supposed to work that way, except for servers that are marked as "also ping this server if it's not in the master server list". The LAN tab continues to broadcast the server info packet to find servers in the LAN. How does it work? ================= The client ships with a list of master server list URLs. On first start, the client checks which of these work and selects the fastest one. Querying the server list is a HTTP GET request on that URL. The response is a JSON document that contains server infos, server addresses as URLs and an approximate location. It can also contain a legacy server list which is a list of bare IP addresses similar to the functionality the old master servers provided via UDP. This allows us to backtrack on the larger update if it won't work out. Lost functionality ================== (also known as user-visible changes) Since the client doesn't ping each server in the list anymore, it has no way of knowing its latency to the servers. This is alleviated a bit by providing an approximate location for each server (continent) so the client only has to know its own location for approximating pings.
2018-07-11 20:46:04 +00:00
#include <algorithm>
2022-02-14 23:22:52 +00:00
#include <climits>
#include <vector>
2010-05-29 07:25:38 +00:00
#include <base/hash_ctxt.h>
#include <base/math.h>
2010-05-29 07:25:38 +00:00
#include <base/system.h>
2011-03-23 12:06:35 +00:00
2010-05-29 07:25:38 +00:00
#include <engine/shared/config.h>
#include <engine/shared/json.h>
Add HTTP masterserver registering and HTTP masterserver Registering ----------- The idea is that game servers push their server info to the masterservers every 15 seconds or when the server info changes, but not more than once per second. The game servers do not support the old registering protocol anymore, the backward compatibility is handled by the masterserver. The register call is a HTTP POST to a URL like `https://master1.ddnet.tw/ddnet/15/register` and looks like this: ```json POST /ddnet/15/register HTTP/1.1 Address: tw-0.6+udp://connecting-address.invalid:8303 Secret: 81fa3955-6f83-4290-818d-31c0906b1118 Challenge-Secret: 81fa3955-6f83-4290-818d-31c0906b1118:tw0.6/ipv6 Info-Serial: 0 { "max_clients": 64, "max_players": 64, "passworded": false, "game_type": "TestDDraceNetwork", "name": "My DDNet server", "map": { "name": "dm1", "sha256": "0b0c481d77519c32fbe85624ef16ec0fa9991aec7367ad538bd280f28d8c26cf", "size": 5805 }, "version": "0.6.4, 16.0.3", "clients": [] } ``` The `Address` header declares that the server wants to register itself as a `tw-0.6+udp` server, i.e. a server speaking a Teeworlds-0.6-compatible protocol. The free-form `Secret` header is used as a server identity, the server list will be deduplicated via this secret. The free-form `Challenge-Secret` is sent back via UDP for a port forward check. This might have security implications as the masterserver can be asked to send a UDP packet containing some user-controlled bytes. This is somewhat mitigated by the fact that it can only go to an attacker-controlled IP address. The `Info-Serial` header is an integer field that should increase each time the server info (in the body) changes. The masterserver uses that field to ensure that it doesn't use old server infos. The body is a free-form JSON object set by the game server. It should contain certain keys in the correct form to be accepted by clients. The body is optional if the masterserver already confirmed the reception of the info with the given `Info-Serial`. Not shown in this payload is the `Connless-Token` header that is used for Teeworlds 0.7 style communication. Also not shown is the `Challenge-Token` that should be included once the server receives the challenge token via UDP. The masterserver responds with a `200 OK` with a body like this: ``` {"status":"success"} ``` The `status` field can be `success` if the server was successfully registered on the masterserver, `need_challenge` if the masterserver wants the correct `Challenge-Token` header before the register process is successful, `need_info` if the server sent an empty body but the masterserver doesn't actually know the server info. It can also be `error` if the request was malformed, only in this case an HTTP status code except `200 OK` is sent. Synchronization --------------- The masterserver keeps state and outputs JSON files every second. ```json { "servers": [ { "addresses": [ "tw-0.6+udp://127.0.0.1:8303", "tw-0.6+udp://[::1]:8303" ], "info_serial": 0, "info": { "max_clients": 64, "max_players": 64, "passworded": false, "game_type": "TestDDraceNetwork", "name": "My DDNet server", "map": { "name": "dm1", "sha256": "0b0c481d77519c32fbe85624ef16ec0fa9991aec7367ad538bd280f28d8c26cf", "size": 5805 }, "version": "0.6.4, 16.0.3", "clients": [] } } ] } ``` `servers.json` (or configured by `--out`) is a server list that is compatible with DDNet 15.5+ clients. It is a JSON object containing a single key `servers` with a list of game servers. Each game server is represented by a JSON object with an `addresses` key containing a list of all known addresses of the server and an `info` key containing the free-form server info sent by the game server. The free-form `info` JSON object re-encoded by the master server and thus canonicalized and stripped of any whitespace characters outside strings. ```json { "kind": "mastersrv", "now": 1816002, "secrets": { "tw-0.6+udp://127.0.0.1:8303": { "ping_time": 1811999, "secret": "42d8f991-f2fa-46e5-a9ae-ebcc93846feb" }, "tw-0.6+udp://[::1]:8303": { "ping_time": 1811999, "secret": "42d8f991-f2fa-46e5-a9ae-ebcc93846feb" } }, "servers": { "42d8f991-f2fa-46e5-a9ae-ebcc93846feb": { "info_serial": 0, "info": { "max_clients": 64, "max_players": 64, "passworded": false, "game_type": "TestDDraceNetwork", "name": "My DDNet server", "map": { "name": "dm1", "sha256": "0b0c481d77519c32fbe85624ef16ec0fa9991aec7367ad538bd280f28d8c26cf", "size": 5805 }, "version": "0.6.4, 16.0.3", "clients": [] } } } } ``` `--write-dump` outputs a JSON file compatible with `--read-dump-dir`, this can be used to synchronize servers across different masterservers. `--read-dump-dir` is also used to ingest servers from the backward compatibility layer that pings each server for their server info using the old protocol. The `kind` field describe that this is `mastersrv` output and not from a `backcompat`. This is used for prioritizing `mastersrv` information over `backcompat` information. The `now` field contains an integer describing the current time in milliseconds relative an unspecified epoch that is fixed for each JSON file. This is done instead of using the current time as the epoch for better compression of non-changing data. `secrets` is a map from each server address and to a JSON object containing the last ping time (`ping_time`) in milliseconds relative to the same epoch as before, and the server secret (`secret`) that is used to unify server infos from different addresses of the same logical server. `servers` is a map from the aforementioned `secret`s to the corresponding `info_serial` and `info`. ```json [ "tw-0.6+udp://127.0.0.1:8303", "tw-0.6+udp://[::1]:8303" ] ``` `--write-addresses` outputs a JSON file containing all addresses corresponding to servers that are registered to HTTP masterservers. It does not contain the servers that are obtained via backward compatibility measures. This file can be used by an old-style masterserver to also list new-style servers without the game servers having to register there. An implementation of this can be found at https://github.com/heinrich5991/teeworlds/tree/mastersrv_6_backcompat for Teeworlds 0.5/0.6 masterservers and at https://github.com/heinrich5991/teeworlds/tree/mastersrv_7_backcompat for Teeworlds 0.7 masterservers. All these JSON files can be sent over the network in an efficient way using https://github.com/heinrich5991/twmaster-collect. It establishes a zstd-compressed TCP connection authenticated by a string token that is sent in plain-text. It watches the specified file and transmits it every time it changes. Due to the zstd-compression, the data sent over the network is similar to the size of a diff. Implementation -------------- The masterserver implementation was done in Rust. The current gameserver register implementation doesn't support more than one masterserver for registering.
2022-05-19 20:03:17 +00:00
#include <engine/shared/masterserver.h>
2010-05-29 07:25:38 +00:00
#include <engine/shared/memheap.h>
2011-03-23 12:06:35 +00:00
#include <engine/shared/network.h>
#include <engine/shared/protocol.h>
Add client-side HTTP server info Summary ======= The idea of this is that clients will not have to ping each server for server infos which takes long, leaks the client's IP address even to servers the user does not join and is a DoS vector of the game servers for attackers. For the Internet, DDNet and KoG tab, the server list is entirely fetched from the master server, filtering out servers that don't belong into the list. The favorites tab is also supposed to work that way, except for servers that are marked as "also ping this server if it's not in the master server list". The LAN tab continues to broadcast the server info packet to find servers in the LAN. How does it work? ================= The client ships with a list of master server list URLs. On first start, the client checks which of these work and selects the fastest one. Querying the server list is a HTTP GET request on that URL. The response is a JSON document that contains server infos, server addresses as URLs and an approximate location. It can also contain a legacy server list which is a list of bare IP addresses similar to the functionality the old master servers provided via UDP. This allows us to backtrack on the larger update if it won't work out. Lost functionality ================== (also known as user-visible changes) Since the client doesn't ping each server in the list anymore, it has no way of knowing its latency to the servers. This is alleviated a bit by providing an approximate location for each server (continent) so the client only has to know its own location for approximating pings.
2018-07-11 20:46:04 +00:00
#include <engine/shared/serverinfo.h>
2010-05-29 07:25:38 +00:00
#include <engine/config.h>
2011-03-23 12:06:35 +00:00
#include <engine/console.h>
Add client-side HTTP server info Summary ======= The idea of this is that clients will not have to ping each server for server infos which takes long, leaks the client's IP address even to servers the user does not join and is a DoS vector of the game servers for attackers. For the Internet, DDNet and KoG tab, the server list is entirely fetched from the master server, filtering out servers that don't belong into the list. The favorites tab is also supposed to work that way, except for servers that are marked as "also ping this server if it's not in the master server list". The LAN tab continues to broadcast the server info packet to find servers in the LAN. How does it work? ================= The client ships with a list of master server list URLs. On first start, the client checks which of these work and selects the fastest one. Querying the server list is a HTTP GET request on that URL. The response is a JSON document that contains server infos, server addresses as URLs and an approximate location. It can also contain a legacy server list which is a list of bare IP addresses similar to the functionality the old master servers provided via UDP. This allows us to backtrack on the larger update if it won't work out. Lost functionality ================== (also known as user-visible changes) Since the client doesn't ping each server in the list anymore, it has no way of knowing its latency to the servers. This is alleviated a bit by providing an approximate location for each server (continent) so the client only has to know its own location for approximating pings.
2018-07-11 20:46:04 +00:00
#include <engine/engine.h>
2011-03-23 12:06:35 +00:00
#include <engine/friends.h>
#include <engine/serverbrowser.h>
2014-09-13 14:36:25 +00:00
#include <engine/storage.h>
2010-05-29 07:25:38 +00:00
2014-09-18 14:13:06 +00:00
#include <engine/external/json-parser/json.h>
2022-03-24 15:14:54 +00:00
#include <game/client/components/menus.h> // PAGE_DDNET
2010-05-29 07:25:38 +00:00
class SortWrap
{
typedef bool (CServerBrowser::*SortFunc)(int, int) const;
SortFunc m_pfnSort;
CServerBrowser *m_pThis;
2010-05-29 07:25:38 +00:00
public:
SortWrap(CServerBrowser *t, SortFunc f) :
m_pfnSort(f), m_pThis(t) {}
bool operator()(int a, int b) { return (g_Config.m_BrSortOrder ? (m_pThis->*m_pfnSort)(b, a) : (m_pThis->*m_pfnSort)(a, b)); }
2010-05-29 07:25:38 +00:00
};
CServerBrowser::CServerBrowser()
{
m_ppServerlist = 0;
m_pSortedServerlist = 0;
m_NumFavoriteServers = 0;
mem_zero(m_aServerlistIp, sizeof(m_aServerlistIp));
m_pFirstReqServer = 0; // request list
m_pLastReqServer = 0;
m_NumRequests = 0;
m_NumSortedServers = 0;
m_NumSortedServersCapacity = 0;
m_NumServers = 0;
m_NumServerCapacity = 0;
m_Sorthash = 0;
m_aFilterString[0] = 0;
m_aFilterGametypeString[0] = 0;
m_ServerlistType = 0;
m_BroadcastTime = 0;
secure_random_fill(m_aTokenSeed, sizeof(m_aTokenSeed));
m_pDDNetInfo = 0;
2020-12-22 10:13:51 +00:00
m_SortOnNextUpdate = false;
}
CServerBrowser::~CServerBrowser()
{
free(m_ppServerlist);
free(m_pSortedServerlist);
json_value_free(m_pDDNetInfo);
Add client-side HTTP server info Summary ======= The idea of this is that clients will not have to ping each server for server infos which takes long, leaks the client's IP address even to servers the user does not join and is a DoS vector of the game servers for attackers. For the Internet, DDNet and KoG tab, the server list is entirely fetched from the master server, filtering out servers that don't belong into the list. The favorites tab is also supposed to work that way, except for servers that are marked as "also ping this server if it's not in the master server list". The LAN tab continues to broadcast the server info packet to find servers in the LAN. How does it work? ================= The client ships with a list of master server list URLs. On first start, the client checks which of these work and selects the fastest one. Querying the server list is a HTTP GET request on that URL. The response is a JSON document that contains server infos, server addresses as URLs and an approximate location. It can also contain a legacy server list which is a list of bare IP addresses similar to the functionality the old master servers provided via UDP. This allows us to backtrack on the larger update if it won't work out. Lost functionality ================== (also known as user-visible changes) Since the client doesn't ping each server in the list anymore, it has no way of knowing its latency to the servers. This is alleviated a bit by providing an approximate location for each server (continent) so the client only has to know its own location for approximating pings.
2018-07-11 20:46:04 +00:00
delete m_pHttp;
m_pHttp = nullptr;
2021-04-17 14:05:24 +00:00
delete m_pPingCache;
m_pPingCache = nullptr;
2010-05-29 07:25:38 +00:00
}
void CServerBrowser::SetBaseInfo(class CNetClient *pClient, const char *pNetVersion)
{
m_pNetClient = pClient;
str_copy(m_aNetVersion, pNetVersion, sizeof(m_aNetVersion));
m_pConsole = Kernel()->RequestInterface<IConsole>();
Add client-side HTTP server info Summary ======= The idea of this is that clients will not have to ping each server for server infos which takes long, leaks the client's IP address even to servers the user does not join and is a DoS vector of the game servers for attackers. For the Internet, DDNet and KoG tab, the server list is entirely fetched from the master server, filtering out servers that don't belong into the list. The favorites tab is also supposed to work that way, except for servers that are marked as "also ping this server if it's not in the master server list". The LAN tab continues to broadcast the server info packet to find servers in the LAN. How does it work? ================= The client ships with a list of master server list URLs. On first start, the client checks which of these work and selects the fastest one. Querying the server list is a HTTP GET request on that URL. The response is a JSON document that contains server infos, server addresses as URLs and an approximate location. It can also contain a legacy server list which is a list of bare IP addresses similar to the functionality the old master servers provided via UDP. This allows us to backtrack on the larger update if it won't work out. Lost functionality ================== (also known as user-visible changes) Since the client doesn't ping each server in the list anymore, it has no way of knowing its latency to the servers. This is alleviated a bit by providing an approximate location for each server (continent) so the client only has to know its own location for approximating pings.
2018-07-11 20:46:04 +00:00
m_pEngine = Kernel()->RequestInterface<IEngine>();
2011-03-23 12:06:35 +00:00
m_pFriends = Kernel()->RequestInterface<IFriends>();
2021-04-17 14:05:24 +00:00
m_pStorage = Kernel()->RequestInterface<IStorage>();
IConfigManager *pConfigManager = Kernel()->RequestInterface<IConfigManager>();
if(pConfigManager)
pConfigManager->RegisterCallback(ConfigSaveCallback, this);
2021-04-17 14:05:24 +00:00
m_pPingCache = CreateServerBrowserPingCache(m_pConsole, m_pStorage);
RegisterCommands();
}
void CServerBrowser::OnInit()
{
m_pHttp = CreateServerBrowserHttp(m_pEngine, m_pConsole, m_pStorage, g_Config.m_BrCachedBestServerinfoUrl);
}
void CServerBrowser::RegisterCommands()
{
m_pConsole->Register("leak_ip_address_to_all_servers", "", CFGFLAG_CLIENT, Con_LeakIpAddress, this, "Leaks your IP address to all servers by pinging each of them, also acquiring the latency in the process");
}
void CServerBrowser::Con_LeakIpAddress(IConsole::IResult *pResult, void *pUserData)
{
CServerBrowser *pThis = (CServerBrowser *)pUserData;
std::vector<int> aSortedServers;
// Sort servers by IP address, ignoring port.
class CAddrComparer
{
public:
CServerBrowser *m_pThis;
bool operator()(int i, int j)
{
NETADDR Addr1 = m_pThis->m_ppServerlist[i]->m_Addr;
NETADDR Addr2 = m_pThis->m_ppServerlist[j]->m_Addr;
Addr1.port = 0;
Addr2.port = 0;
return net_addr_comp(&Addr1, &Addr2) < 0;
}
};
aSortedServers.reserve(pThis->m_NumServers);
for(int i = 0; i < pThis->m_NumServers; i++)
{
aSortedServers.push_back(i);
}
std::sort(aSortedServers.begin(), aSortedServers.end(), CAddrComparer{pThis});
// Group the servers into those with same IP address (but differing
// port).
NETADDR Addr;
int Start = -1;
for(int i = 0; i <= (int)aSortedServers.size(); i++)
{
NETADDR NextAddr;
if(i < (int)aSortedServers.size())
{
NextAddr = pThis->m_ppServerlist[aSortedServers[i]]->m_Addr;
NextAddr.port = 0;
}
bool New = Start == -1 || i == (int)aSortedServers.size() || net_addr_comp(&Addr, &NextAddr) != 0;
if(Start != -1 && New)
{
int Chosen = Start + secure_rand_below(i - Start);
CServerEntry *pChosen = pThis->m_ppServerlist[aSortedServers[Chosen]];
pChosen->m_RequestIgnoreInfo = true;
pThis->QueueRequest(pChosen);
char aAddr[NETADDR_MAXSTRSIZE];
net_addr_str(&pChosen->m_Addr, aAddr, sizeof(aAddr), true);
dbg_msg("serverbrowse/dbg", "queuing ping request for %s", aAddr);
}
if(i < (int)aSortedServers.size() && New)
{
Start = i;
Addr = NextAddr;
}
}
2010-05-29 07:25:38 +00:00
}
const CServerInfo *CServerBrowser::SortedGet(int Index) const
{
if(Index < 0 || Index >= m_NumSortedServers)
return 0;
return &m_ppServerlist[m_pSortedServerlist[Index]]->m_Info;
}
int CServerBrowser::GenerateToken(const NETADDR &Addr) const
{
SHA256_CTX Sha256;
sha256_init(&Sha256);
sha256_update(&Sha256, m_aTokenSeed, sizeof(m_aTokenSeed));
sha256_update(&Sha256, (unsigned char *)&Addr, sizeof(Addr));
SHA256_DIGEST Digest = sha256_finish(&Sha256);
return (Digest.data[0] << 16) | (Digest.data[1] << 8) | Digest.data[2];
}
int CServerBrowser::GetBasicToken(int Token)
{
return Token & 0xff;
}
int CServerBrowser::GetExtraToken(int Token)
{
return Token >> 8;
}
2010-05-29 07:25:38 +00:00
bool CServerBrowser::SortCompareName(int Index1, int Index2) const
{
CServerEntry *a = m_ppServerlist[Index1];
CServerEntry *b = m_ppServerlist[Index2];
// make sure empty entries are listed last
return (a->m_GotInfo && b->m_GotInfo) || (!a->m_GotInfo && !b->m_GotInfo) ? str_comp(a->m_Info.m_aName, b->m_Info.m_aName) < 0 :
2022-01-22 16:34:23 +00:00
a->m_GotInfo != 0;
2010-05-29 07:25:38 +00:00
}
bool CServerBrowser::SortCompareMap(int Index1, int Index2) const
{
CServerEntry *a = m_ppServerlist[Index1];
CServerEntry *b = m_ppServerlist[Index2];
return str_comp(a->m_Info.m_aMap, b->m_Info.m_aMap) < 0;
}
bool CServerBrowser::SortComparePing(int Index1, int Index2) const
{
CServerEntry *a = m_ppServerlist[Index1];
CServerEntry *b = m_ppServerlist[Index2];
return a->m_Info.m_Latency < b->m_Info.m_Latency;
}
bool CServerBrowser::SortCompareGametype(int Index1, int Index2) const
{
CServerEntry *a = m_ppServerlist[Index1];
CServerEntry *b = m_ppServerlist[Index2];
return str_comp(a->m_Info.m_aGameType, b->m_Info.m_aGameType) < 0;
}
bool CServerBrowser::SortCompareNumPlayers(int Index1, int Index2) const
{
CServerEntry *a = m_ppServerlist[Index1];
CServerEntry *b = m_ppServerlist[Index2];
2020-08-25 00:26:36 +00:00
return a->m_Info.m_NumFilteredPlayers > b->m_Info.m_NumFilteredPlayers;
2010-05-29 07:25:38 +00:00
}
bool CServerBrowser::SortCompareNumClients(int Index1, int Index2) const
{
CServerEntry *a = m_ppServerlist[Index1];
CServerEntry *b = m_ppServerlist[Index2];
2020-08-25 00:26:36 +00:00
return a->m_Info.m_NumClients > b->m_Info.m_NumClients;
}
bool CServerBrowser::SortCompareNumPlayersAndPing(int Index1, int Index2) const
{
CServerEntry *a = m_ppServerlist[Index1];
CServerEntry *b = m_ppServerlist[Index2];
if(a->m_Info.m_NumFilteredPlayers == b->m_Info.m_NumFilteredPlayers)
return a->m_Info.m_Latency > b->m_Info.m_Latency;
else if(a->m_Info.m_NumFilteredPlayers == 0 || b->m_Info.m_NumFilteredPlayers == 0 || a->m_Info.m_Latency / 100 == b->m_Info.m_Latency / 100)
return a->m_Info.m_NumFilteredPlayers < b->m_Info.m_NumFilteredPlayers;
else
return a->m_Info.m_Latency > b->m_Info.m_Latency;
}
2010-05-29 07:25:38 +00:00
void CServerBrowser::Filter()
{
int i = 0, p = 0;
m_NumSortedServers = 0;
// allocate the sorted list
if(m_NumSortedServersCapacity < m_NumServers)
{
free(m_pSortedServerlist);
2010-05-29 07:25:38 +00:00
m_NumSortedServersCapacity = m_NumServers;
m_pSortedServerlist = (int *)calloc(m_NumSortedServersCapacity, sizeof(int));
2010-05-29 07:25:38 +00:00
}
// filter the servers
for(i = 0; i < m_NumServers; i++)
{
int Filtered = 0;
2018-10-29 21:03:57 +00:00
if(g_Config.m_BrFilterEmpty && m_ppServerlist[i]->m_Info.m_NumFilteredPlayers == 0)
2011-06-26 15:10:13 +00:00
Filtered = 1;
else if(g_Config.m_BrFilterFull && Players(m_ppServerlist[i]->m_Info) == Max(m_ppServerlist[i]->m_Info))
2011-06-26 15:10:13 +00:00
Filtered = 1;
else if(g_Config.m_BrFilterPw && m_ppServerlist[i]->m_Info.m_Flags & SERVER_FLAG_PASSWORD)
2011-06-26 15:10:13 +00:00
Filtered = 1;
else if(g_Config.m_BrFilterServerAddress[0] && !str_find_nocase(m_ppServerlist[i]->m_Info.m_aAddress, g_Config.m_BrFilterServerAddress))
Filtered = 1;
else if(g_Config.m_BrFilterGametypeStrict && g_Config.m_BrFilterGametype[0] && str_comp_nocase(m_ppServerlist[i]->m_Info.m_aGameType, g_Config.m_BrFilterGametype))
Filtered = 1;
else if(!g_Config.m_BrFilterGametypeStrict && g_Config.m_BrFilterGametype[0] && !str_utf8_find_nocase(m_ppServerlist[i]->m_Info.m_aGameType, g_Config.m_BrFilterGametype))
2011-06-26 15:10:13 +00:00
Filtered = 1;
else if(g_Config.m_BrFilterUnfinishedMap && m_ppServerlist[i]->m_Info.m_HasRank == 1)
Filtered = 1;
else
2011-06-26 15:10:13 +00:00
{
if(g_Config.m_BrFilterCountry)
2011-06-26 15:10:13 +00:00
{
Filtered = 1;
// match against player country
2020-10-18 16:41:18 +00:00
for(p = 0; p < minimum(m_ppServerlist[i]->m_Info.m_NumClients, (int)MAX_CLIENTS); p++)
{
if(m_ppServerlist[i]->m_Info.m_aClients[p].m_Country == g_Config.m_BrFilterCountryIndex)
{
Filtered = 0;
break;
}
}
2011-06-26 15:10:13 +00:00
}
if(!Filtered && g_Config.m_BrFilterString[0] != 0)
2011-06-26 15:10:13 +00:00
{
int MatchFound = 0;
m_ppServerlist[i]->m_Info.m_QuickSearchHit = 0;
// match against server name
if(str_utf8_find_nocase(m_ppServerlist[i]->m_Info.m_aName, g_Config.m_BrFilterString))
2011-03-23 12:06:35 +00:00
{
MatchFound = 1;
m_ppServerlist[i]->m_Info.m_QuickSearchHit |= IServerBrowser::QUICK_SERVERNAME;
}
// match against players
2020-10-18 16:41:18 +00:00
for(p = 0; p < minimum(m_ppServerlist[i]->m_Info.m_NumClients, (int)MAX_CLIENTS); p++)
{
if(str_utf8_find_nocase(m_ppServerlist[i]->m_Info.m_aClients[p].m_aName, g_Config.m_BrFilterString) ||
str_utf8_find_nocase(m_ppServerlist[i]->m_Info.m_aClients[p].m_aClan, g_Config.m_BrFilterString))
{
MatchFound = 1;
m_ppServerlist[i]->m_Info.m_QuickSearchHit |= IServerBrowser::QUICK_PLAYER;
break;
}
}
// match against map
if(str_utf8_find_nocase(m_ppServerlist[i]->m_Info.m_aMap, g_Config.m_BrFilterString))
{
MatchFound = 1;
m_ppServerlist[i]->m_Info.m_QuickSearchHit |= IServerBrowser::QUICK_MAPNAME;
}
if(!MatchFound)
Filtered = 1;
}
if(!Filtered && g_Config.m_BrExcludeString[0] != 0)
{
int MatchFound = 0;
// match against server name
if(str_utf8_find_nocase(m_ppServerlist[i]->m_Info.m_aName, g_Config.m_BrExcludeString))
{
MatchFound = 1;
}
// match against map
if(str_utf8_find_nocase(m_ppServerlist[i]->m_Info.m_aMap, g_Config.m_BrExcludeString))
{
MatchFound = 1;
}
2019-03-05 09:46:29 +00:00
2017-08-30 21:55:02 +00:00
// match against gametype
if(str_utf8_find_nocase(m_ppServerlist[i]->m_Info.m_aGameType, g_Config.m_BrExcludeString))
2017-08-30 21:55:02 +00:00
{
MatchFound = 1;
}
if(MatchFound)
Filtered = 1;
}
2010-05-29 07:25:38 +00:00
}
if(Filtered == 0)
2011-06-26 15:10:13 +00:00
{
// check for friend
m_ppServerlist[i]->m_Info.m_FriendState = IFriends::FRIEND_NO;
for(p = 0; p < minimum(m_ppServerlist[i]->m_Info.m_NumClients, (int)MAX_CLIENTS); p++)
2011-06-26 15:10:13 +00:00
{
m_ppServerlist[i]->m_Info.m_aClients[p].m_FriendState = m_pFriends->GetFriendState(m_ppServerlist[i]->m_Info.m_aClients[p].m_aName,
m_ppServerlist[i]->m_Info.m_aClients[p].m_aClan);
2019-04-26 19:36:49 +00:00
m_ppServerlist[i]->m_Info.m_FriendState = maximum(m_ppServerlist[i]->m_Info.m_FriendState, m_ppServerlist[i]->m_Info.m_aClients[p].m_FriendState);
2011-06-26 15:10:13 +00:00
}
if(!g_Config.m_BrFilterFriends || m_ppServerlist[i]->m_Info.m_FriendState != IFriends::FRIEND_NO)
m_pSortedServerlist[m_NumSortedServers++] = i;
}
2010-05-29 07:25:38 +00:00
}
}
int CServerBrowser::SortHash() const
{
2020-09-26 19:33:36 +00:00
int i = g_Config.m_BrSort & 0xff;
i |= g_Config.m_BrFilterEmpty << 4;
i |= g_Config.m_BrFilterFull << 5;
i |= g_Config.m_BrFilterSpectators << 6;
i |= g_Config.m_BrFilterFriends << 7;
i |= g_Config.m_BrFilterPw << 8;
i |= g_Config.m_BrSortOrder << 9;
i |= g_Config.m_BrFilterGametypeStrict << 12;
i |= g_Config.m_BrFilterUnfinishedMap << 13;
i |= g_Config.m_BrFilterCountry << 14;
i |= g_Config.m_BrFilterConnectingPlayers << 15;
2010-05-29 07:25:38 +00:00
return i;
}
2018-10-29 21:03:57 +00:00
void SetFilteredPlayers(const CServerInfo &Item)
{
Item.m_NumFilteredPlayers = g_Config.m_BrFilterSpectators ? Item.m_NumPlayers : Item.m_NumClients;
if(g_Config.m_BrFilterConnectingPlayers)
2018-10-29 21:03:57 +00:00
{
for(const auto &Client : Item.m_aClients)
2019-10-31 13:16:35 +00:00
{
if((!g_Config.m_BrFilterSpectators || Client.m_Player) && str_comp(Client.m_aName, "(connecting)") == 0 && Client.m_aClan[0] == '\0')
2019-10-31 13:16:35 +00:00
Item.m_NumFilteredPlayers--;
}
2018-10-29 21:03:57 +00:00
}
}
2010-05-29 07:25:38 +00:00
void CServerBrowser::Sort()
{
int i;
2018-10-29 21:03:57 +00:00
// fill m_NumFilteredPlayers
for(i = 0; i < m_NumServers; i++)
{
SetFilteredPlayers(m_ppServerlist[i]->m_Info);
}
2010-05-29 07:25:38 +00:00
// create filtered list
Filter();
// sort
if(g_Config.m_BrSortOrder == 2 && (g_Config.m_BrSort == IServerBrowser::SORT_NUMPLAYERS || g_Config.m_BrSort == IServerBrowser::SORT_PING))
std::stable_sort(m_pSortedServerlist, m_pSortedServerlist + m_NumSortedServers, SortWrap(this, &CServerBrowser::SortCompareNumPlayersAndPing));
else if(g_Config.m_BrSort == IServerBrowser::SORT_NAME)
std::stable_sort(m_pSortedServerlist, m_pSortedServerlist + m_NumSortedServers, SortWrap(this, &CServerBrowser::SortCompareName));
2010-05-29 07:25:38 +00:00
else if(g_Config.m_BrSort == IServerBrowser::SORT_PING)
std::stable_sort(m_pSortedServerlist, m_pSortedServerlist + m_NumSortedServers, SortWrap(this, &CServerBrowser::SortComparePing));
2010-05-29 07:25:38 +00:00
else if(g_Config.m_BrSort == IServerBrowser::SORT_MAP)
std::stable_sort(m_pSortedServerlist, m_pSortedServerlist + m_NumSortedServers, SortWrap(this, &CServerBrowser::SortCompareMap));
2010-05-29 07:25:38 +00:00
else if(g_Config.m_BrSort == IServerBrowser::SORT_NUMPLAYERS)
std::stable_sort(m_pSortedServerlist, m_pSortedServerlist + m_NumSortedServers, SortWrap(this, &CServerBrowser::SortCompareNumPlayers));
2010-05-29 07:25:38 +00:00
else if(g_Config.m_BrSort == IServerBrowser::SORT_GAMETYPE)
std::stable_sort(m_pSortedServerlist, m_pSortedServerlist + m_NumSortedServers, SortWrap(this, &CServerBrowser::SortCompareGametype));
2010-05-29 07:25:38 +00:00
str_copy(m_aFilterGametypeString, g_Config.m_BrFilterGametype, sizeof(m_aFilterGametypeString));
str_copy(m_aFilterString, g_Config.m_BrFilterString, sizeof(m_aFilterString));
m_Sorthash = SortHash();
}
void CServerBrowser::RemoveRequest(CServerEntry *pEntry)
{
if(pEntry->m_pPrevReq || pEntry->m_pNextReq || m_pFirstReqServer == pEntry)
{
if(pEntry->m_pPrevReq)
pEntry->m_pPrevReq->m_pNextReq = pEntry->m_pNextReq;
else
m_pFirstReqServer = pEntry->m_pNextReq;
if(pEntry->m_pNextReq)
pEntry->m_pNextReq->m_pPrevReq = pEntry->m_pPrevReq;
else
m_pLastReqServer = pEntry->m_pPrevReq;
pEntry->m_pPrevReq = 0;
pEntry->m_pNextReq = 0;
m_NumRequests--;
}
}
CServerBrowser::CServerEntry *CServerBrowser::Find(const NETADDR &Addr)
{
CServerEntry *pEntry = m_aServerlistIp[Addr.ip[0]];
for(; pEntry; pEntry = pEntry->m_pNextIp)
{
if(net_addr_comp(&pEntry->m_Addr, &Addr) == 0)
return pEntry;
}
return (CServerEntry *)0;
2010-05-29 07:25:38 +00:00
}
void CServerBrowser::QueueRequest(CServerEntry *pEntry)
{
// add it to the list of servers that we should request info from
pEntry->m_pPrevReq = m_pLastReqServer;
if(m_pLastReqServer)
m_pLastReqServer->m_pNextReq = pEntry;
else
m_pFirstReqServer = pEntry;
m_pLastReqServer = pEntry;
pEntry->m_pNextReq = 0;
2010-05-29 07:25:38 +00:00
m_NumRequests++;
}
void CServerBrowser::SetInfo(CServerEntry *pEntry, const CServerInfo &Info)
{
bool Fav = pEntry->m_Info.m_Favorite;
bool Off = pEntry->m_Info.m_Official;
2010-05-29 07:25:38 +00:00
pEntry->m_Info = Info;
pEntry->m_Info.m_Favorite = Fav;
pEntry->m_Info.m_Official = Off;
2010-05-29 07:25:38 +00:00
pEntry->m_Info.m_NetAddr = pEntry->m_Addr;
Add client-side HTTP server info Summary ======= The idea of this is that clients will not have to ping each server for server infos which takes long, leaks the client's IP address even to servers the user does not join and is a DoS vector of the game servers for attackers. For the Internet, DDNet and KoG tab, the server list is entirely fetched from the master server, filtering out servers that don't belong into the list. The favorites tab is also supposed to work that way, except for servers that are marked as "also ping this server if it's not in the master server list". The LAN tab continues to broadcast the server info packet to find servers in the LAN. How does it work? ================= The client ships with a list of master server list URLs. On first start, the client checks which of these work and selects the fastest one. Querying the server list is a HTTP GET request on that URL. The response is a JSON document that contains server infos, server addresses as URLs and an approximate location. It can also contain a legacy server list which is a list of bare IP addresses similar to the functionality the old master servers provided via UDP. This allows us to backtrack on the larger update if it won't work out. Lost functionality ================== (also known as user-visible changes) Since the client doesn't ping each server in the list anymore, it has no way of knowing its latency to the servers. This is alleviated a bit by providing an approximate location for each server (continent) so the client only has to know its own location for approximating pings.
2018-07-11 20:46:04 +00:00
net_addr_str(&pEntry->m_Info.m_NetAddr, pEntry->m_Info.m_aAddress, sizeof(pEntry->m_Info.m_aAddress), 1);
2010-05-29 07:25:38 +00:00
class CPlayerScoreNameLess
{
public:
bool operator()(const CServerInfo::CClient &p0, const CServerInfo::CClient &p1)
{
if(p0.m_Player && !p1.m_Player)
return true;
if(!p0.m_Player && p1.m_Player)
return false;
int Score0 = p0.m_Score;
int Score1 = p1.m_Score;
if(Score0 == -9999)
Score0 = INT_MIN;
if(Score1 == -9999)
Score1 = INT_MIN;
if(Score0 > Score1)
return true;
if(Score0 < Score1)
return false;
return str_comp_nocase(p0.m_aName, p1.m_aName) < 0;
}
};
std::sort(pEntry->m_Info.m_aClients, pEntry->m_Info.m_aClients + Info.m_NumReceivedClients, CPlayerScoreNameLess());
2010-05-29 07:25:38 +00:00
pEntry->m_GotInfo = 1;
}
void CServerBrowser::SetLatency(NETADDR Addr, int Latency)
{
Addr.port = 0;
for(CServerEntry *pEntry = m_aServerlistIp[Addr.ip[0]]; pEntry; pEntry = pEntry->m_pNextIp)
{
NETADDR Other = pEntry->m_Addr;
Other.port = 0;
if(net_addr_comp(&Addr, &Other) == 0 && pEntry->m_GotInfo)
{
pEntry->m_Info.m_Latency = Latency;
pEntry->m_Info.m_LatencyIsEstimated = false;
}
}
m_pPingCache->CachePing(Addr, Latency);
}
2010-05-29 07:25:38 +00:00
CServerBrowser::CServerEntry *CServerBrowser::Add(const NETADDR &Addr)
{
int Hash = Addr.ip[0];
CServerEntry *pEntry = 0;
// create new pEntry
pEntry = (CServerEntry *)m_ServerlistHeap.Allocate(sizeof(CServerEntry));
mem_zero(pEntry, sizeof(CServerEntry));
// set the info
pEntry->m_Addr = Addr;
pEntry->m_Info.m_NetAddr = Addr;
pEntry->m_Info.m_Latency = 999;
pEntry->m_Info.m_HasRank = -1;
2011-12-29 22:36:53 +00:00
net_addr_str(&Addr, pEntry->m_Info.m_aAddress, sizeof(pEntry->m_Info.m_aAddress), true);
str_copy(pEntry->m_Info.m_aName, pEntry->m_Info.m_aAddress, sizeof(pEntry->m_Info.m_aName));
2010-05-29 07:25:38 +00:00
// check if it's a favorite
pEntry->m_Info.m_Favorite = IsFavorite(Addr);
// check if it's an official server
2020-10-26 14:14:07 +00:00
for(auto &Network : m_aNetworks)
{
2020-10-26 14:14:07 +00:00
for(int i = 0; i < Network.m_NumCountries; i++)
{
2020-10-26 14:14:07 +00:00
CNetworkCountry *pCntr = &Network.m_aCountries[i];
for(int j = 0; j < pCntr->m_NumServers; j++)
{
if(net_addr_comp(&Addr, &pCntr->m_aServers[j]) == 0)
{
pEntry->m_Info.m_Official = true;
break;
}
}
}
2010-05-29 07:25:38 +00:00
}
// add to the hash list
pEntry->m_pNextIp = m_aServerlistIp[Hash];
m_aServerlistIp[Hash] = pEntry;
if(m_NumServers == m_NumServerCapacity)
{
CServerEntry **ppNewlist;
m_NumServerCapacity += 100;
ppNewlist = (CServerEntry **)calloc(m_NumServerCapacity, sizeof(CServerEntry *)); // NOLINT(bugprone-sizeof-expression)
if(m_NumServers > 0)
mem_copy(ppNewlist, m_ppServerlist, m_NumServers * sizeof(CServerEntry *)); // NOLINT(bugprone-sizeof-expression)
free(m_ppServerlist);
2010-05-29 07:25:38 +00:00
m_ppServerlist = ppNewlist;
}
// add to list
m_ppServerlist[m_NumServers] = pEntry;
pEntry->m_Info.m_ServerIndex = m_NumServers;
m_NumServers++;
return pEntry;
}
void CServerBrowser::Set(const NETADDR &Addr, int Type, int Token, const CServerInfo *pInfo)
{
CServerEntry *pEntry = 0;
if(Type == IServerBrowser::SET_MASTER_ADD)
{
if(m_ServerlistType != IServerBrowser::TYPE_INTERNET)
return;
if(!Find(Addr))
{
pEntry = Add(Addr);
QueueRequest(pEntry);
}
}
else if(Type == IServerBrowser::SET_FAV_ADD)
{
if(m_ServerlistType != IServerBrowser::TYPE_FAVORITES)
return;
if(!Find(Addr))
{
pEntry = Add(Addr);
QueueRequest(pEntry);
}
}
2014-09-13 14:36:25 +00:00
else if(Type == IServerBrowser::SET_DDNET_ADD)
{
if(m_ServerlistType != IServerBrowser::TYPE_DDNET)
return;
if(!Find(Addr))
{
pEntry = Add(Addr);
QueueRequest(pEntry);
}
}
else if(Type == IServerBrowser::SET_KOG_ADD)
{
if(m_ServerlistType != IServerBrowser::TYPE_KOG)
return;
if(!Find(Addr))
{
pEntry = Add(Addr);
QueueRequest(pEntry);
}
}
Add client-side HTTP server info Summary ======= The idea of this is that clients will not have to ping each server for server infos which takes long, leaks the client's IP address even to servers the user does not join and is a DoS vector of the game servers for attackers. For the Internet, DDNet and KoG tab, the server list is entirely fetched from the master server, filtering out servers that don't belong into the list. The favorites tab is also supposed to work that way, except for servers that are marked as "also ping this server if it's not in the master server list". The LAN tab continues to broadcast the server info packet to find servers in the LAN. How does it work? ================= The client ships with a list of master server list URLs. On first start, the client checks which of these work and selects the fastest one. Querying the server list is a HTTP GET request on that URL. The response is a JSON document that contains server infos, server addresses as URLs and an approximate location. It can also contain a legacy server list which is a list of bare IP addresses similar to the functionality the old master servers provided via UDP. This allows us to backtrack on the larger update if it won't work out. Lost functionality ================== (also known as user-visible changes) Since the client doesn't ping each server in the list anymore, it has no way of knowing its latency to the servers. This is alleviated a bit by providing an approximate location for each server (continent) so the client only has to know its own location for approximating pings.
2018-07-11 20:46:04 +00:00
else if(Type == IServerBrowser::SET_HTTPINFO)
{
if(!pEntry)
{
pEntry = Add(Addr);
}
if(pEntry)
{
SetInfo(pEntry, *pInfo);
pEntry->m_RequestIgnoreInfo = true;
Add client-side HTTP server info Summary ======= The idea of this is that clients will not have to ping each server for server infos which takes long, leaks the client's IP address even to servers the user does not join and is a DoS vector of the game servers for attackers. For the Internet, DDNet and KoG tab, the server list is entirely fetched from the master server, filtering out servers that don't belong into the list. The favorites tab is also supposed to work that way, except for servers that are marked as "also ping this server if it's not in the master server list". The LAN tab continues to broadcast the server info packet to find servers in the LAN. How does it work? ================= The client ships with a list of master server list URLs. On first start, the client checks which of these work and selects the fastest one. Querying the server list is a HTTP GET request on that URL. The response is a JSON document that contains server infos, server addresses as URLs and an approximate location. It can also contain a legacy server list which is a list of bare IP addresses similar to the functionality the old master servers provided via UDP. This allows us to backtrack on the larger update if it won't work out. Lost functionality ================== (also known as user-visible changes) Since the client doesn't ping each server in the list anymore, it has no way of knowing its latency to the servers. This is alleviated a bit by providing an approximate location for each server (continent) so the client only has to know its own location for approximating pings.
2018-07-11 20:46:04 +00:00
}
}
2010-05-29 07:25:38 +00:00
else if(Type == IServerBrowser::SET_TOKEN)
{
int BasicToken = Token;
int ExtraToken = 0;
if(pInfo->m_Type == SERVERINFO_EXTENDED)
{
BasicToken = Token & 0xff;
ExtraToken = Token >> 8;
}
2010-05-29 07:25:38 +00:00
pEntry = Find(Addr);
if(m_ServerlistType == IServerBrowser::TYPE_LAN)
{
NETADDR Broadcast;
mem_zero(&Broadcast, sizeof(Broadcast));
Broadcast.type = m_pNetClient->NetType() | NETTYPE_LINK_BROADCAST;
int TokenBC = GenerateToken(Broadcast);
bool Drop = false;
Drop = Drop || BasicToken != GetBasicToken(TokenBC);
Drop = Drop || (pInfo->m_Type == SERVERINFO_EXTENDED && ExtraToken != GetExtraToken(TokenBC));
if(Drop)
{
return;
}
if(!pEntry)
pEntry = Add(Addr);
}
else
{
if(!pEntry)
{
return;
}
int TokenAddr = GenerateToken(Addr);
bool Drop = false;
Drop = Drop || BasicToken != GetBasicToken(TokenAddr);
Drop = Drop || (pInfo->m_Type == SERVERINFO_EXTENDED && ExtraToken != GetExtraToken(TokenAddr));
if(Drop)
{
return;
}
}
if(m_ServerlistType == IServerBrowser::TYPE_LAN)
{
SetInfo(pEntry, *pInfo);
pEntry->m_Info.m_Latency = minimum(static_cast<int>((time_get() - m_BroadcastTime) * 1000 / time_freq()), 999);
if(pInfo->m_Type == SERVERINFO_VANILLA && Is64Player(pInfo))
{
pEntry->m_Request64Legacy = true;
// Force a quick update.
RequestImpl64(pEntry->m_Addr, pEntry);
}
}
else if(pEntry->m_RequestTime > 0)
2010-05-29 07:25:38 +00:00
{
if(!pEntry->m_RequestIgnoreInfo)
{
SetInfo(pEntry, *pInfo);
}
int Latency = minimum(static_cast<int>((time_get() - pEntry->m_RequestTime) * 1000 / time_freq()), 999);
if(!pEntry->m_RequestIgnoreInfo)
{
pEntry->m_Info.m_Latency = Latency;
}
else
{
char aAddr[NETADDR_MAXSTRSIZE];
net_addr_str(&Addr, aAddr, sizeof(aAddr), true);
dbg_msg("serverbrowse/dbg", "received ping response from %s", aAddr);
SetLatency(Addr, Latency);
}
pEntry->m_RequestTime = -1; // Request has been answered
if(!pEntry->m_RequestIgnoreInfo)
{
if(pInfo->m_Type == SERVERINFO_VANILLA && Is64Player(pInfo))
{
pEntry->m_Request64Legacy = true;
// Force a quick update.
RequestImpl64(pEntry->m_Addr, pEntry);
}
}
2010-05-29 07:25:38 +00:00
}
RemoveRequest(pEntry);
2010-05-29 07:25:38 +00:00
}
2020-12-22 10:13:51 +00:00
m_SortOnNextUpdate = true;
2010-05-29 07:25:38 +00:00
}
void CServerBrowser::Refresh(int Type)
{
2021-07-08 17:18:01 +00:00
bool ServerListTypeChanged = m_ServerlistType != Type;
int OldServerListType = m_ServerlistType;
2010-05-29 07:25:38 +00:00
m_ServerlistType = Type;
2020-09-03 00:03:22 +00:00
secure_random_fill(m_aTokenSeed, sizeof(m_aTokenSeed));
2010-05-29 07:25:38 +00:00
2021-07-08 17:18:01 +00:00
if(Type == IServerBrowser::TYPE_LAN || (ServerListTypeChanged && OldServerListType == IServerBrowser::TYPE_LAN))
CleanUp();
2010-05-29 07:25:38 +00:00
if(Type == IServerBrowser::TYPE_LAN)
{
unsigned char Buffer[sizeof(SERVERBROWSE_GETINFO) + 1];
2010-05-29 07:25:38 +00:00
CNetChunk Packet;
int i;
/* do the broadcast version */
2010-05-29 07:25:38 +00:00
Packet.m_ClientID = -1;
mem_zero(&Packet, sizeof(Packet));
Packet.m_Address.type = m_pNetClient->NetType() | NETTYPE_LINK_BROADCAST;
Packet.m_Flags = NETSENDFLAG_CONNLESS | NETSENDFLAG_EXTENDED;
2010-05-29 07:25:38 +00:00
Packet.m_DataSize = sizeof(Buffer);
Packet.m_pData = Buffer;
mem_zero(&Packet.m_aExtraData, sizeof(Packet.m_aExtraData));
int Token = GenerateToken(Packet.m_Address);
mem_copy(Buffer, SERVERBROWSE_GETINFO, sizeof(SERVERBROWSE_GETINFO));
Buffer[sizeof(SERVERBROWSE_GETINFO)] = GetBasicToken(Token);
Packet.m_aExtraData[0] = GetExtraToken(Token) >> 8;
Packet.m_aExtraData[1] = GetExtraToken(Token) & 0xff;
2010-05-29 07:25:38 +00:00
m_BroadcastTime = time_get();
for(i = 8303; i <= 8310; i++)
{
Packet.m_Address.port = i;
m_pNetClient->Send(&Packet);
}
if(g_Config.m_Debug)
m_pConsole->Print(IConsole::OUTPUT_LEVEL_DEBUG, "client_srvbrowse", "broadcasting for servers");
2010-05-29 07:25:38 +00:00
}
Add client-side HTTP server info Summary ======= The idea of this is that clients will not have to ping each server for server infos which takes long, leaks the client's IP address even to servers the user does not join and is a DoS vector of the game servers for attackers. For the Internet, DDNet and KoG tab, the server list is entirely fetched from the master server, filtering out servers that don't belong into the list. The favorites tab is also supposed to work that way, except for servers that are marked as "also ping this server if it's not in the master server list". The LAN tab continues to broadcast the server info packet to find servers in the LAN. How does it work? ================= The client ships with a list of master server list URLs. On first start, the client checks which of these work and selects the fastest one. Querying the server list is a HTTP GET request on that URL. The response is a JSON document that contains server infos, server addresses as URLs and an approximate location. It can also contain a legacy server list which is a list of bare IP addresses similar to the functionality the old master servers provided via UDP. This allows us to backtrack on the larger update if it won't work out. Lost functionality ================== (also known as user-visible changes) Since the client doesn't ping each server in the list anymore, it has no way of knowing its latency to the servers. This is alleviated a bit by providing an approximate location for each server (continent) so the client only has to know its own location for approximating pings.
2018-07-11 20:46:04 +00:00
else if(Type == IServerBrowser::TYPE_FAVORITES || Type == IServerBrowser::TYPE_INTERNET || Type == IServerBrowser::TYPE_DDNET || Type == IServerBrowser::TYPE_KOG)
{
Add client-side HTTP server info Summary ======= The idea of this is that clients will not have to ping each server for server infos which takes long, leaks the client's IP address even to servers the user does not join and is a DoS vector of the game servers for attackers. For the Internet, DDNet and KoG tab, the server list is entirely fetched from the master server, filtering out servers that don't belong into the list. The favorites tab is also supposed to work that way, except for servers that are marked as "also ping this server if it's not in the master server list". The LAN tab continues to broadcast the server info packet to find servers in the LAN. How does it work? ================= The client ships with a list of master server list URLs. On first start, the client checks which of these work and selects the fastest one. Querying the server list is a HTTP GET request on that URL. The response is a JSON document that contains server infos, server addresses as URLs and an approximate location. It can also contain a legacy server list which is a list of bare IP addresses similar to the functionality the old master servers provided via UDP. This allows us to backtrack on the larger update if it won't work out. Lost functionality ================== (also known as user-visible changes) Since the client doesn't ping each server in the list anymore, it has no way of knowing its latency to the servers. This is alleviated a bit by providing an approximate location for each server (continent) so the client only has to know its own location for approximating pings.
2018-07-11 20:46:04 +00:00
m_pHttp->Refresh();
m_pPingCache->Load();
Add client-side HTTP server info Summary ======= The idea of this is that clients will not have to ping each server for server infos which takes long, leaks the client's IP address even to servers the user does not join and is a DoS vector of the game servers for attackers. For the Internet, DDNet and KoG tab, the server list is entirely fetched from the master server, filtering out servers that don't belong into the list. The favorites tab is also supposed to work that way, except for servers that are marked as "also ping this server if it's not in the master server list". The LAN tab continues to broadcast the server info packet to find servers in the LAN. How does it work? ================= The client ships with a list of master server list URLs. On first start, the client checks which of these work and selects the fastest one. Querying the server list is a HTTP GET request on that URL. The response is a JSON document that contains server infos, server addresses as URLs and an approximate location. It can also contain a legacy server list which is a list of bare IP addresses similar to the functionality the old master servers provided via UDP. This allows us to backtrack on the larger update if it won't work out. Lost functionality ================== (also known as user-visible changes) Since the client doesn't ping each server in the list anymore, it has no way of knowing its latency to the servers. This is alleviated a bit by providing an approximate location for each server (continent) so the client only has to know its own location for approximating pings.
2018-07-11 20:46:04 +00:00
m_RefreshingHttp = true;
2021-07-08 17:18:01 +00:00
if(ServerListTypeChanged && m_pHttp->NumServers() > 0)
{
CleanUp();
UpdateFromHttp();
Sort();
}
}
2010-05-29 07:25:38 +00:00
}
void CServerBrowser::RequestImpl(const NETADDR &Addr, CServerEntry *pEntry, int *pBasicToken, int *pToken, bool RandomToken) const
2010-05-29 07:25:38 +00:00
{
unsigned char Buffer[sizeof(SERVERBROWSE_GETINFO) + 1];
2010-05-29 07:25:38 +00:00
CNetChunk Packet;
if(g_Config.m_Debug)
{
char aAddrStr[NETADDR_MAXSTRSIZE];
2011-12-29 22:36:53 +00:00
net_addr_str(&Addr, aAddrStr, sizeof(aAddrStr), true);
char aBuf[256];
str_format(aBuf, sizeof(aBuf), "requesting server info from %s", aAddrStr);
m_pConsole->Print(IConsole::OUTPUT_LEVEL_DEBUG, "client_srvbrowse", aBuf);
2010-05-29 07:25:38 +00:00
}
int Token = GenerateToken(Addr);
if(RandomToken)
{
int AvoidBasicToken = GetBasicToken(Token);
do
{
secure_random_fill(&Token, sizeof(Token));
Token &= 0xffffff;
} while(GetBasicToken(Token) == AvoidBasicToken);
}
if(pToken)
{
*pToken = Token;
}
if(pBasicToken)
{
*pBasicToken = GetBasicToken(Token);
}
mem_copy(Buffer, SERVERBROWSE_GETINFO, sizeof(SERVERBROWSE_GETINFO));
Buffer[sizeof(SERVERBROWSE_GETINFO)] = GetBasicToken(Token);
2010-05-29 07:25:38 +00:00
Packet.m_ClientID = -1;
Packet.m_Address = Addr;
Packet.m_Flags = NETSENDFLAG_CONNLESS | NETSENDFLAG_EXTENDED;
Packet.m_DataSize = sizeof(Buffer);
Packet.m_pData = Buffer;
mem_zero(&Packet.m_aExtraData, sizeof(Packet.m_aExtraData));
Packet.m_aExtraData[0] = GetExtraToken(Token) >> 8;
Packet.m_aExtraData[1] = GetExtraToken(Token) & 0xff;
2010-05-29 07:25:38 +00:00
m_pNetClient->Send(&Packet);
if(pEntry)
pEntry->m_RequestTime = time_get();
}
2014-01-08 05:15:56 +00:00
void CServerBrowser::RequestImpl64(const NETADDR &Addr, CServerEntry *pEntry) const
{
unsigned char Buffer[sizeof(SERVERBROWSE_GETINFO_64_LEGACY) + 1];
2014-01-08 05:15:56 +00:00
CNetChunk Packet;
if(g_Config.m_Debug)
{
char aAddrStr[NETADDR_MAXSTRSIZE];
net_addr_str(&Addr, aAddrStr, sizeof(aAddrStr), true);
char aBuf[256];
str_format(aBuf, sizeof(aBuf), "requesting server info 64 from %s", aAddrStr);
2014-01-08 05:15:56 +00:00
m_pConsole->Print(IConsole::OUTPUT_LEVEL_DEBUG, "client_srvbrowse", aBuf);
}
mem_copy(Buffer, SERVERBROWSE_GETINFO_64_LEGACY, sizeof(SERVERBROWSE_GETINFO_64_LEGACY));
Buffer[sizeof(SERVERBROWSE_GETINFO_64_LEGACY)] = GetBasicToken(GenerateToken(Addr));
2014-01-08 05:15:56 +00:00
Packet.m_ClientID = -1;
Packet.m_Address = Addr;
Packet.m_Flags = NETSENDFLAG_CONNLESS;
Packet.m_DataSize = sizeof(Buffer);
Packet.m_pData = Buffer;
m_pNetClient->Send(&Packet);
if(pEntry)
pEntry->m_RequestTime = time_get();
}
void CServerBrowser::RequestCurrentServer(const NETADDR &Addr) const
{
RequestImpl(Addr, nullptr, nullptr, nullptr, false);
}
void CServerBrowser::RequestCurrentServerWithRandomToken(const NETADDR &Addr, int *pBasicToken, int *pToken) const
{
RequestImpl(Addr, nullptr, pBasicToken, pToken, true);
}
void CServerBrowser::SetCurrentServerPing(const NETADDR &Addr, int Ping)
2010-05-29 07:25:38 +00:00
{
2021-08-20 15:16:18 +00:00
SetLatency(Addr, minimum(Ping, 999));
2010-05-29 07:25:38 +00:00
}
void ServerBrowserFillEstimatedLatency(int OwnLocation, const IServerBrowserPingCache::CEntry *pEntries, int NumEntries, int *pIndex, NETADDR Addr, CServerInfo *pInfo)
{
Addr.port = 0;
while(*pIndex < NumEntries && net_addr_comp(&pEntries[*pIndex].m_Addr, &Addr) < 0)
{
*pIndex += 1;
}
if(*pIndex >= NumEntries || net_addr_comp(&pEntries[*pIndex].m_Addr, &Addr) != 0)
{
pInfo->m_LatencyIsEstimated = true;
pInfo->m_Latency = CServerInfo::EstimateLatency(OwnLocation, pInfo->m_Location);
return;
}
pInfo->m_LatencyIsEstimated = false;
pInfo->m_Latency = pEntries[*pIndex].m_Ping;
}
Add client-side HTTP server info Summary ======= The idea of this is that clients will not have to ping each server for server infos which takes long, leaks the client's IP address even to servers the user does not join and is a DoS vector of the game servers for attackers. For the Internet, DDNet and KoG tab, the server list is entirely fetched from the master server, filtering out servers that don't belong into the list. The favorites tab is also supposed to work that way, except for servers that are marked as "also ping this server if it's not in the master server list". The LAN tab continues to broadcast the server info packet to find servers in the LAN. How does it work? ================= The client ships with a list of master server list URLs. On first start, the client checks which of these work and selects the fastest one. Querying the server list is a HTTP GET request on that URL. The response is a JSON document that contains server infos, server addresses as URLs and an approximate location. It can also contain a legacy server list which is a list of bare IP addresses similar to the functionality the old master servers provided via UDP. This allows us to backtrack on the larger update if it won't work out. Lost functionality ================== (also known as user-visible changes) Since the client doesn't ping each server in the list anymore, it has no way of knowing its latency to the servers. This is alleviated a bit by providing an approximate location for each server (continent) so the client only has to know its own location for approximating pings.
2018-07-11 20:46:04 +00:00
void CServerBrowser::UpdateFromHttp()
2015-07-09 00:08:14 +00:00
{
const IServerBrowserPingCache::CEntry *pPingEntries;
int NumPingEntries;
m_pPingCache->GetPingCache(&pPingEntries, &NumPingEntries);
int OwnLocation;
if(str_comp(g_Config.m_BrLocation, "auto") == 0)
{
OwnLocation = m_OwnLocation;
}
else
{
if(CServerInfo::ParseLocation(&OwnLocation, g_Config.m_BrLocation))
{
char aBuf[64];
str_format(aBuf, sizeof(aBuf), "cannot parse br_location: '%s'", g_Config.m_BrLocation);
m_pConsole->Print(IConsole::OUTPUT_LEVEL_STANDARD, "serverbrowse", aBuf);
}
}
Add client-side HTTP server info Summary ======= The idea of this is that clients will not have to ping each server for server infos which takes long, leaks the client's IP address even to servers the user does not join and is a DoS vector of the game servers for attackers. For the Internet, DDNet and KoG tab, the server list is entirely fetched from the master server, filtering out servers that don't belong into the list. The favorites tab is also supposed to work that way, except for servers that are marked as "also ping this server if it's not in the master server list". The LAN tab continues to broadcast the server info packet to find servers in the LAN. How does it work? ================= The client ships with a list of master server list URLs. On first start, the client checks which of these work and selects the fastest one. Querying the server list is a HTTP GET request on that URL. The response is a JSON document that contains server infos, server addresses as URLs and an approximate location. It can also contain a legacy server list which is a list of bare IP addresses similar to the functionality the old master servers provided via UDP. This allows us to backtrack on the larger update if it won't work out. Lost functionality ================== (also known as user-visible changes) Since the client doesn't ping each server in the list anymore, it has no way of knowing its latency to the servers. This is alleviated a bit by providing an approximate location for each server (continent) so the client only has to know its own location for approximating pings.
2018-07-11 20:46:04 +00:00
int NumServers = m_pHttp->NumServers();
int NumLegacyServers = m_pHttp->NumLegacyServers();
if(m_ServerlistType != IServerBrowser::TYPE_INTERNET)
2010-05-29 07:25:38 +00:00
{
class CWantedAddr
{
public:
NETADDR m_Addr;
bool m_FallbackToPing;
bool m_Got;
};
std::vector<CWantedAddr> aWantedAddresses;
Add client-side HTTP server info Summary ======= The idea of this is that clients will not have to ping each server for server infos which takes long, leaks the client's IP address even to servers the user does not join and is a DoS vector of the game servers for attackers. For the Internet, DDNet and KoG tab, the server list is entirely fetched from the master server, filtering out servers that don't belong into the list. The favorites tab is also supposed to work that way, except for servers that are marked as "also ping this server if it's not in the master server list". The LAN tab continues to broadcast the server info packet to find servers in the LAN. How does it work? ================= The client ships with a list of master server list URLs. On first start, the client checks which of these work and selects the fastest one. Querying the server list is a HTTP GET request on that URL. The response is a JSON document that contains server infos, server addresses as URLs and an approximate location. It can also contain a legacy server list which is a list of bare IP addresses similar to the functionality the old master servers provided via UDP. This allows us to backtrack on the larger update if it won't work out. Lost functionality ================== (also known as user-visible changes) Since the client doesn't ping each server in the list anymore, it has no way of knowing its latency to the servers. This is alleviated a bit by providing an approximate location for each server (continent) so the client only has to know its own location for approximating pings.
2018-07-11 20:46:04 +00:00
int LegacySetType;
if(m_ServerlistType == IServerBrowser::TYPE_FAVORITES)
{
Add client-side HTTP server info Summary ======= The idea of this is that clients will not have to ping each server for server infos which takes long, leaks the client's IP address even to servers the user does not join and is a DoS vector of the game servers for attackers. For the Internet, DDNet and KoG tab, the server list is entirely fetched from the master server, filtering out servers that don't belong into the list. The favorites tab is also supposed to work that way, except for servers that are marked as "also ping this server if it's not in the master server list". The LAN tab continues to broadcast the server info packet to find servers in the LAN. How does it work? ================= The client ships with a list of master server list URLs. On first start, the client checks which of these work and selects the fastest one. Querying the server list is a HTTP GET request on that URL. The response is a JSON document that contains server infos, server addresses as URLs and an approximate location. It can also contain a legacy server list which is a list of bare IP addresses similar to the functionality the old master servers provided via UDP. This allows us to backtrack on the larger update if it won't work out. Lost functionality ================== (also known as user-visible changes) Since the client doesn't ping each server in the list anymore, it has no way of knowing its latency to the servers. This is alleviated a bit by providing an approximate location for each server (continent) so the client only has to know its own location for approximating pings.
2018-07-11 20:46:04 +00:00
for(int i = 0; i < m_NumFavoriteServers; i++)
2014-12-14 15:45:18 +00:00
{
aWantedAddresses.push_back(CWantedAddr{m_aFavoriteServers[i], m_aFavoriteServersAllowPing[i], false});
2014-12-14 15:45:18 +00:00
}
Add client-side HTTP server info Summary ======= The idea of this is that clients will not have to ping each server for server infos which takes long, leaks the client's IP address even to servers the user does not join and is a DoS vector of the game servers for attackers. For the Internet, DDNet and KoG tab, the server list is entirely fetched from the master server, filtering out servers that don't belong into the list. The favorites tab is also supposed to work that way, except for servers that are marked as "also ping this server if it's not in the master server list". The LAN tab continues to broadcast the server info packet to find servers in the LAN. How does it work? ================= The client ships with a list of master server list URLs. On first start, the client checks which of these work and selects the fastest one. Querying the server list is a HTTP GET request on that URL. The response is a JSON document that contains server infos, server addresses as URLs and an approximate location. It can also contain a legacy server list which is a list of bare IP addresses similar to the functionality the old master servers provided via UDP. This allows us to backtrack on the larger update if it won't work out. Lost functionality ================== (also known as user-visible changes) Since the client doesn't ping each server in the list anymore, it has no way of knowing its latency to the servers. This is alleviated a bit by providing an approximate location for each server (continent) so the client only has to know its own location for approximating pings.
2018-07-11 20:46:04 +00:00
LegacySetType = IServerBrowser::SET_FAV_ADD;
}
Add client-side HTTP server info Summary ======= The idea of this is that clients will not have to ping each server for server infos which takes long, leaks the client's IP address even to servers the user does not join and is a DoS vector of the game servers for attackers. For the Internet, DDNet and KoG tab, the server list is entirely fetched from the master server, filtering out servers that don't belong into the list. The favorites tab is also supposed to work that way, except for servers that are marked as "also ping this server if it's not in the master server list". The LAN tab continues to broadcast the server info packet to find servers in the LAN. How does it work? ================= The client ships with a list of master server list URLs. On first start, the client checks which of these work and selects the fastest one. Querying the server list is a HTTP GET request on that URL. The response is a JSON document that contains server infos, server addresses as URLs and an approximate location. It can also contain a legacy server list which is a list of bare IP addresses similar to the functionality the old master servers provided via UDP. This allows us to backtrack on the larger update if it won't work out. Lost functionality ================== (also known as user-visible changes) Since the client doesn't ping each server in the list anymore, it has no way of knowing its latency to the servers. This is alleviated a bit by providing an approximate location for each server (continent) so the client only has to know its own location for approximating pings.
2018-07-11 20:46:04 +00:00
else
{
Add client-side HTTP server info Summary ======= The idea of this is that clients will not have to ping each server for server infos which takes long, leaks the client's IP address even to servers the user does not join and is a DoS vector of the game servers for attackers. For the Internet, DDNet and KoG tab, the server list is entirely fetched from the master server, filtering out servers that don't belong into the list. The favorites tab is also supposed to work that way, except for servers that are marked as "also ping this server if it's not in the master server list". The LAN tab continues to broadcast the server info packet to find servers in the LAN. How does it work? ================= The client ships with a list of master server list URLs. On first start, the client checks which of these work and selects the fastest one. Querying the server list is a HTTP GET request on that URL. The response is a JSON document that contains server infos, server addresses as URLs and an approximate location. It can also contain a legacy server list which is a list of bare IP addresses similar to the functionality the old master servers provided via UDP. This allows us to backtrack on the larger update if it won't work out. Lost functionality ================== (also known as user-visible changes) Since the client doesn't ping each server in the list anymore, it has no way of knowing its latency to the servers. This is alleviated a bit by providing an approximate location for each server (continent) so the client only has to know its own location for approximating pings.
2018-07-11 20:46:04 +00:00
int Network;
char *pExcludeCountries;
char *pExcludeTypes;
switch(m_ServerlistType)
2015-07-09 00:08:14 +00:00
{
Add client-side HTTP server info Summary ======= The idea of this is that clients will not have to ping each server for server infos which takes long, leaks the client's IP address even to servers the user does not join and is a DoS vector of the game servers for attackers. For the Internet, DDNet and KoG tab, the server list is entirely fetched from the master server, filtering out servers that don't belong into the list. The favorites tab is also supposed to work that way, except for servers that are marked as "also ping this server if it's not in the master server list". The LAN tab continues to broadcast the server info packet to find servers in the LAN. How does it work? ================= The client ships with a list of master server list URLs. On first start, the client checks which of these work and selects the fastest one. Querying the server list is a HTTP GET request on that URL. The response is a JSON document that contains server infos, server addresses as URLs and an approximate location. It can also contain a legacy server list which is a list of bare IP addresses similar to the functionality the old master servers provided via UDP. This allows us to backtrack on the larger update if it won't work out. Lost functionality ================== (also known as user-visible changes) Since the client doesn't ping each server in the list anymore, it has no way of knowing its latency to the servers. This is alleviated a bit by providing an approximate location for each server (continent) so the client only has to know its own location for approximating pings.
2018-07-11 20:46:04 +00:00
case IServerBrowser::TYPE_DDNET:
Network = NETWORK_DDNET;
LegacySetType = IServerBrowser::SET_DDNET_ADD;
pExcludeCountries = g_Config.m_BrFilterExcludeCountries;
pExcludeTypes = g_Config.m_BrFilterExcludeTypes;
break;
case IServerBrowser::TYPE_KOG:
Network = NETWORK_KOG;
LegacySetType = IServerBrowser::SET_KOG_ADD;
pExcludeCountries = g_Config.m_BrFilterExcludeCountriesKoG;
pExcludeTypes = g_Config.m_BrFilterExcludeTypesKoG;
break;
default:
dbg_assert(0, "invalid network");
return;
}
Add client-side HTTP server info Summary ======= The idea of this is that clients will not have to ping each server for server infos which takes long, leaks the client's IP address even to servers the user does not join and is a DoS vector of the game servers for attackers. For the Internet, DDNet and KoG tab, the server list is entirely fetched from the master server, filtering out servers that don't belong into the list. The favorites tab is also supposed to work that way, except for servers that are marked as "also ping this server if it's not in the master server list". The LAN tab continues to broadcast the server info packet to find servers in the LAN. How does it work? ================= The client ships with a list of master server list URLs. On first start, the client checks which of these work and selects the fastest one. Querying the server list is a HTTP GET request on that URL. The response is a JSON document that contains server infos, server addresses as URLs and an approximate location. It can also contain a legacy server list which is a list of bare IP addresses similar to the functionality the old master servers provided via UDP. This allows us to backtrack on the larger update if it won't work out. Lost functionality ================== (also known as user-visible changes) Since the client doesn't ping each server in the list anymore, it has no way of knowing its latency to the servers. This is alleviated a bit by providing an approximate location for each server (continent) so the client only has to know its own location for approximating pings.
2018-07-11 20:46:04 +00:00
// remove unknown elements of exclude list
CountryFilterClean(Network);
TypeFilterClean(Network);
2010-05-29 07:25:38 +00:00
Add client-side HTTP server info Summary ======= The idea of this is that clients will not have to ping each server for server infos which takes long, leaks the client's IP address even to servers the user does not join and is a DoS vector of the game servers for attackers. For the Internet, DDNet and KoG tab, the server list is entirely fetched from the master server, filtering out servers that don't belong into the list. The favorites tab is also supposed to work that way, except for servers that are marked as "also ping this server if it's not in the master server list". The LAN tab continues to broadcast the server info packet to find servers in the LAN. How does it work? ================= The client ships with a list of master server list URLs. On first start, the client checks which of these work and selects the fastest one. Querying the server list is a HTTP GET request on that URL. The response is a JSON document that contains server infos, server addresses as URLs and an approximate location. It can also contain a legacy server list which is a list of bare IP addresses similar to the functionality the old master servers provided via UDP. This allows us to backtrack on the larger update if it won't work out. Lost functionality ================== (also known as user-visible changes) Since the client doesn't ping each server in the list anymore, it has no way of knowing its latency to the servers. This is alleviated a bit by providing an approximate location for each server (continent) so the client only has to know its own location for approximating pings.
2018-07-11 20:46:04 +00:00
int MaxServers = 0;
for(int i = 0; i < m_aNetworks[Network].m_NumCountries; i++)
{
CNetworkCountry *pCntr = &m_aNetworks[Network].m_aCountries[i];
MaxServers = maximum(MaxServers, pCntr->m_NumServers);
}
2010-05-29 07:25:38 +00:00
Add client-side HTTP server info Summary ======= The idea of this is that clients will not have to ping each server for server infos which takes long, leaks the client's IP address even to servers the user does not join and is a DoS vector of the game servers for attackers. For the Internet, DDNet and KoG tab, the server list is entirely fetched from the master server, filtering out servers that don't belong into the list. The favorites tab is also supposed to work that way, except for servers that are marked as "also ping this server if it's not in the master server list". The LAN tab continues to broadcast the server info packet to find servers in the LAN. How does it work? ================= The client ships with a list of master server list URLs. On first start, the client checks which of these work and selects the fastest one. Querying the server list is a HTTP GET request on that URL. The response is a JSON document that contains server infos, server addresses as URLs and an approximate location. It can also contain a legacy server list which is a list of bare IP addresses similar to the functionality the old master servers provided via UDP. This allows us to backtrack on the larger update if it won't work out. Lost functionality ================== (also known as user-visible changes) Since the client doesn't ping each server in the list anymore, it has no way of knowing its latency to the servers. This is alleviated a bit by providing an approximate location for each server (continent) so the client only has to know its own location for approximating pings.
2018-07-11 20:46:04 +00:00
for(int g = 0; g < MaxServers; g++)
{
for(int i = 0; i < m_aNetworks[Network].m_NumCountries; i++)
{
CNetworkCountry *pCntr = &m_aNetworks[Network].m_aCountries[i];
// check for filter
if(DDNetFiltered(pExcludeCountries, pCntr->m_aName))
continue;
if(g >= pCntr->m_NumServers)
continue;
if(DDNetFiltered(pExcludeTypes, pCntr->m_aTypes[g]))
continue;
aWantedAddresses.push_back(CWantedAddr{pCntr->m_aServers[g], false, false});
Add client-side HTTP server info Summary ======= The idea of this is that clients will not have to ping each server for server infos which takes long, leaks the client's IP address even to servers the user does not join and is a DoS vector of the game servers for attackers. For the Internet, DDNet and KoG tab, the server list is entirely fetched from the master server, filtering out servers that don't belong into the list. The favorites tab is also supposed to work that way, except for servers that are marked as "also ping this server if it's not in the master server list". The LAN tab continues to broadcast the server info packet to find servers in the LAN. How does it work? ================= The client ships with a list of master server list URLs. On first start, the client checks which of these work and selects the fastest one. Querying the server list is a HTTP GET request on that URL. The response is a JSON document that contains server infos, server addresses as URLs and an approximate location. It can also contain a legacy server list which is a list of bare IP addresses similar to the functionality the old master servers provided via UDP. This allows us to backtrack on the larger update if it won't work out. Lost functionality ================== (also known as user-visible changes) Since the client doesn't ping each server in the list anymore, it has no way of knowing its latency to the servers. This is alleviated a bit by providing an approximate location for each server (continent) so the client only has to know its own location for approximating pings.
2018-07-11 20:46:04 +00:00
}
}
2010-05-29 07:25:38 +00:00
}
Add client-side HTTP server info Summary ======= The idea of this is that clients will not have to ping each server for server infos which takes long, leaks the client's IP address even to servers the user does not join and is a DoS vector of the game servers for attackers. For the Internet, DDNet and KoG tab, the server list is entirely fetched from the master server, filtering out servers that don't belong into the list. The favorites tab is also supposed to work that way, except for servers that are marked as "also ping this server if it's not in the master server list". The LAN tab continues to broadcast the server info packet to find servers in the LAN. How does it work? ================= The client ships with a list of master server list URLs. On first start, the client checks which of these work and selects the fastest one. Querying the server list is a HTTP GET request on that URL. The response is a JSON document that contains server infos, server addresses as URLs and an approximate location. It can also contain a legacy server list which is a list of bare IP addresses similar to the functionality the old master servers provided via UDP. This allows us to backtrack on the larger update if it won't work out. Lost functionality ================== (also known as user-visible changes) Since the client doesn't ping each server in the list anymore, it has no way of knowing its latency to the servers. This is alleviated a bit by providing an approximate location for each server (continent) so the client only has to know its own location for approximating pings.
2018-07-11 20:46:04 +00:00
std::vector<int> aSortedServers;
std::vector<int> aSortedLegacyServers;
aSortedServers.reserve(NumServers);
Add client-side HTTP server info Summary ======= The idea of this is that clients will not have to ping each server for server infos which takes long, leaks the client's IP address even to servers the user does not join and is a DoS vector of the game servers for attackers. For the Internet, DDNet and KoG tab, the server list is entirely fetched from the master server, filtering out servers that don't belong into the list. The favorites tab is also supposed to work that way, except for servers that are marked as "also ping this server if it's not in the master server list". The LAN tab continues to broadcast the server info packet to find servers in the LAN. How does it work? ================= The client ships with a list of master server list URLs. On first start, the client checks which of these work and selects the fastest one. Querying the server list is a HTTP GET request on that URL. The response is a JSON document that contains server infos, server addresses as URLs and an approximate location. It can also contain a legacy server list which is a list of bare IP addresses similar to the functionality the old master servers provided via UDP. This allows us to backtrack on the larger update if it won't work out. Lost functionality ================== (also known as user-visible changes) Since the client doesn't ping each server in the list anymore, it has no way of knowing its latency to the servers. This is alleviated a bit by providing an approximate location for each server (continent) so the client only has to know its own location for approximating pings.
2018-07-11 20:46:04 +00:00
for(int i = 0; i < NumServers; i++)
{
Add client-side HTTP server info Summary ======= The idea of this is that clients will not have to ping each server for server infos which takes long, leaks the client's IP address even to servers the user does not join and is a DoS vector of the game servers for attackers. For the Internet, DDNet and KoG tab, the server list is entirely fetched from the master server, filtering out servers that don't belong into the list. The favorites tab is also supposed to work that way, except for servers that are marked as "also ping this server if it's not in the master server list". The LAN tab continues to broadcast the server info packet to find servers in the LAN. How does it work? ================= The client ships with a list of master server list URLs. On first start, the client checks which of these work and selects the fastest one. Querying the server list is a HTTP GET request on that URL. The response is a JSON document that contains server infos, server addresses as URLs and an approximate location. It can also contain a legacy server list which is a list of bare IP addresses similar to the functionality the old master servers provided via UDP. This allows us to backtrack on the larger update if it won't work out. Lost functionality ================== (also known as user-visible changes) Since the client doesn't ping each server in the list anymore, it has no way of knowing its latency to the servers. This is alleviated a bit by providing an approximate location for each server (continent) so the client only has to know its own location for approximating pings.
2018-07-11 20:46:04 +00:00
aSortedServers.push_back(i);
}
aSortedLegacyServers.reserve(NumLegacyServers);
Add client-side HTTP server info Summary ======= The idea of this is that clients will not have to ping each server for server infos which takes long, leaks the client's IP address even to servers the user does not join and is a DoS vector of the game servers for attackers. For the Internet, DDNet and KoG tab, the server list is entirely fetched from the master server, filtering out servers that don't belong into the list. The favorites tab is also supposed to work that way, except for servers that are marked as "also ping this server if it's not in the master server list". The LAN tab continues to broadcast the server info packet to find servers in the LAN. How does it work? ================= The client ships with a list of master server list URLs. On first start, the client checks which of these work and selects the fastest one. Querying the server list is a HTTP GET request on that URL. The response is a JSON document that contains server infos, server addresses as URLs and an approximate location. It can also contain a legacy server list which is a list of bare IP addresses similar to the functionality the old master servers provided via UDP. This allows us to backtrack on the larger update if it won't work out. Lost functionality ================== (also known as user-visible changes) Since the client doesn't ping each server in the list anymore, it has no way of knowing its latency to the servers. This is alleviated a bit by providing an approximate location for each server (continent) so the client only has to know its own location for approximating pings.
2018-07-11 20:46:04 +00:00
for(int i = 0; i < NumLegacyServers; i++)
{
aSortedLegacyServers.push_back(i);
}
class CWantedAddrComparer
{
public:
bool operator()(const CWantedAddr &a, const CWantedAddr &b)
Add client-side HTTP server info Summary ======= The idea of this is that clients will not have to ping each server for server infos which takes long, leaks the client's IP address even to servers the user does not join and is a DoS vector of the game servers for attackers. For the Internet, DDNet and KoG tab, the server list is entirely fetched from the master server, filtering out servers that don't belong into the list. The favorites tab is also supposed to work that way, except for servers that are marked as "also ping this server if it's not in the master server list". The LAN tab continues to broadcast the server info packet to find servers in the LAN. How does it work? ================= The client ships with a list of master server list URLs. On first start, the client checks which of these work and selects the fastest one. Querying the server list is a HTTP GET request on that URL. The response is a JSON document that contains server infos, server addresses as URLs and an approximate location. It can also contain a legacy server list which is a list of bare IP addresses similar to the functionality the old master servers provided via UDP. This allows us to backtrack on the larger update if it won't work out. Lost functionality ================== (also known as user-visible changes) Since the client doesn't ping each server in the list anymore, it has no way of knowing its latency to the servers. This is alleviated a bit by providing an approximate location for each server (continent) so the client only has to know its own location for approximating pings.
2018-07-11 20:46:04 +00:00
{
return net_addr_comp(&a.m_Addr, &b.m_Addr) < 0;
Add client-side HTTP server info Summary ======= The idea of this is that clients will not have to ping each server for server infos which takes long, leaks the client's IP address even to servers the user does not join and is a DoS vector of the game servers for attackers. For the Internet, DDNet and KoG tab, the server list is entirely fetched from the master server, filtering out servers that don't belong into the list. The favorites tab is also supposed to work that way, except for servers that are marked as "also ping this server if it's not in the master server list". The LAN tab continues to broadcast the server info packet to find servers in the LAN. How does it work? ================= The client ships with a list of master server list URLs. On first start, the client checks which of these work and selects the fastest one. Querying the server list is a HTTP GET request on that URL. The response is a JSON document that contains server infos, server addresses as URLs and an approximate location. It can also contain a legacy server list which is a list of bare IP addresses similar to the functionality the old master servers provided via UDP. This allows us to backtrack on the larger update if it won't work out. Lost functionality ================== (also known as user-visible changes) Since the client doesn't ping each server in the list anymore, it has no way of knowing its latency to the servers. This is alleviated a bit by providing an approximate location for each server (continent) so the client only has to know its own location for approximating pings.
2018-07-11 20:46:04 +00:00
}
};
class CAddrComparer
{
public:
IServerBrowserHttp *m_pHttp;
bool operator()(int i, int j)
{
return net_addr_comp(&m_pHttp->ServerAddress(i), &m_pHttp->ServerAddress(j)) < 0;
}
};
class CLegacyAddrComparer
{
Add client-side HTTP server info Summary ======= The idea of this is that clients will not have to ping each server for server infos which takes long, leaks the client's IP address even to servers the user does not join and is a DoS vector of the game servers for attackers. For the Internet, DDNet and KoG tab, the server list is entirely fetched from the master server, filtering out servers that don't belong into the list. The favorites tab is also supposed to work that way, except for servers that are marked as "also ping this server if it's not in the master server list". The LAN tab continues to broadcast the server info packet to find servers in the LAN. How does it work? ================= The client ships with a list of master server list URLs. On first start, the client checks which of these work and selects the fastest one. Querying the server list is a HTTP GET request on that URL. The response is a JSON document that contains server infos, server addresses as URLs and an approximate location. It can also contain a legacy server list which is a list of bare IP addresses similar to the functionality the old master servers provided via UDP. This allows us to backtrack on the larger update if it won't work out. Lost functionality ================== (also known as user-visible changes) Since the client doesn't ping each server in the list anymore, it has no way of knowing its latency to the servers. This is alleviated a bit by providing an approximate location for each server (continent) so the client only has to know its own location for approximating pings.
2018-07-11 20:46:04 +00:00
public:
IServerBrowserHttp *m_pHttp;
bool operator()(int i, int j)
{
return net_addr_comp(&m_pHttp->LegacyServer(i), &m_pHttp->LegacyServer(j)) < 0;
}
};
std::sort(aWantedAddresses.begin(), aWantedAddresses.end(), CWantedAddrComparer());
std::sort(aSortedServers.begin(), aSortedServers.end(), CAddrComparer{m_pHttp});
std::sort(aSortedLegacyServers.begin(), aSortedLegacyServers.end(), CLegacyAddrComparer{m_pHttp});
unsigned i = 0;
unsigned j = 0;
int p = 0;
Add client-side HTTP server info Summary ======= The idea of this is that clients will not have to ping each server for server infos which takes long, leaks the client's IP address even to servers the user does not join and is a DoS vector of the game servers for attackers. For the Internet, DDNet and KoG tab, the server list is entirely fetched from the master server, filtering out servers that don't belong into the list. The favorites tab is also supposed to work that way, except for servers that are marked as "also ping this server if it's not in the master server list". The LAN tab continues to broadcast the server info packet to find servers in the LAN. How does it work? ================= The client ships with a list of master server list URLs. On first start, the client checks which of these work and selects the fastest one. Querying the server list is a HTTP GET request on that URL. The response is a JSON document that contains server infos, server addresses as URLs and an approximate location. It can also contain a legacy server list which is a list of bare IP addresses similar to the functionality the old master servers provided via UDP. This allows us to backtrack on the larger update if it won't work out. Lost functionality ================== (also known as user-visible changes) Since the client doesn't ping each server in the list anymore, it has no way of knowing its latency to the servers. This is alleviated a bit by providing an approximate location for each server (continent) so the client only has to know its own location for approximating pings.
2018-07-11 20:46:04 +00:00
while(i < aWantedAddresses.size() && j < aSortedServers.size())
{
int Cmp = net_addr_comp(&aWantedAddresses[i].m_Addr, &m_pHttp->ServerAddress(aSortedServers[j]));
Add client-side HTTP server info Summary ======= The idea of this is that clients will not have to ping each server for server infos which takes long, leaks the client's IP address even to servers the user does not join and is a DoS vector of the game servers for attackers. For the Internet, DDNet and KoG tab, the server list is entirely fetched from the master server, filtering out servers that don't belong into the list. The favorites tab is also supposed to work that way, except for servers that are marked as "also ping this server if it's not in the master server list". The LAN tab continues to broadcast the server info packet to find servers in the LAN. How does it work? ================= The client ships with a list of master server list URLs. On first start, the client checks which of these work and selects the fastest one. Querying the server list is a HTTP GET request on that URL. The response is a JSON document that contains server infos, server addresses as URLs and an approximate location. It can also contain a legacy server list which is a list of bare IP addresses similar to the functionality the old master servers provided via UDP. This allows us to backtrack on the larger update if it won't work out. Lost functionality ================== (also known as user-visible changes) Since the client doesn't ping each server in the list anymore, it has no way of knowing its latency to the servers. This is alleviated a bit by providing an approximate location for each server (continent) so the client only has to know its own location for approximating pings.
2018-07-11 20:46:04 +00:00
if(Cmp != 0)
{
if(Cmp < 0)
{
i++;
}
else
{
j++;
}
continue;
Add client-side HTTP server info Summary ======= The idea of this is that clients will not have to ping each server for server infos which takes long, leaks the client's IP address even to servers the user does not join and is a DoS vector of the game servers for attackers. For the Internet, DDNet and KoG tab, the server list is entirely fetched from the master server, filtering out servers that don't belong into the list. The favorites tab is also supposed to work that way, except for servers that are marked as "also ping this server if it's not in the master server list". The LAN tab continues to broadcast the server info packet to find servers in the LAN. How does it work? ================= The client ships with a list of master server list URLs. On first start, the client checks which of these work and selects the fastest one. Querying the server list is a HTTP GET request on that URL. The response is a JSON document that contains server infos, server addresses as URLs and an approximate location. It can also contain a legacy server list which is a list of bare IP addresses similar to the functionality the old master servers provided via UDP. This allows us to backtrack on the larger update if it won't work out. Lost functionality ================== (also known as user-visible changes) Since the client doesn't ping each server in the list anymore, it has no way of knowing its latency to the servers. This is alleviated a bit by providing an approximate location for each server (continent) so the client only has to know its own location for approximating pings.
2018-07-11 20:46:04 +00:00
}
aWantedAddresses[i].m_Got = true;
Add client-side HTTP server info Summary ======= The idea of this is that clients will not have to ping each server for server infos which takes long, leaks the client's IP address even to servers the user does not join and is a DoS vector of the game servers for attackers. For the Internet, DDNet and KoG tab, the server list is entirely fetched from the master server, filtering out servers that don't belong into the list. The favorites tab is also supposed to work that way, except for servers that are marked as "also ping this server if it's not in the master server list". The LAN tab continues to broadcast the server info packet to find servers in the LAN. How does it work? ================= The client ships with a list of master server list URLs. On first start, the client checks which of these work and selects the fastest one. Querying the server list is a HTTP GET request on that URL. The response is a JSON document that contains server infos, server addresses as URLs and an approximate location. It can also contain a legacy server list which is a list of bare IP addresses similar to the functionality the old master servers provided via UDP. This allows us to backtrack on the larger update if it won't work out. Lost functionality ================== (also known as user-visible changes) Since the client doesn't ping each server in the list anymore, it has no way of knowing its latency to the servers. This is alleviated a bit by providing an approximate location for each server (continent) so the client only has to know its own location for approximating pings.
2018-07-11 20:46:04 +00:00
NETADDR Addr;
CServerInfo Info;
m_pHttp->Server(aSortedServers[j], &Addr, &Info);
ServerBrowserFillEstimatedLatency(OwnLocation, pPingEntries, NumPingEntries, &p, Addr, &Info);
Add client-side HTTP server info Summary ======= The idea of this is that clients will not have to ping each server for server infos which takes long, leaks the client's IP address even to servers the user does not join and is a DoS vector of the game servers for attackers. For the Internet, DDNet and KoG tab, the server list is entirely fetched from the master server, filtering out servers that don't belong into the list. The favorites tab is also supposed to work that way, except for servers that are marked as "also ping this server if it's not in the master server list". The LAN tab continues to broadcast the server info packet to find servers in the LAN. How does it work? ================= The client ships with a list of master server list URLs. On first start, the client checks which of these work and selects the fastest one. Querying the server list is a HTTP GET request on that URL. The response is a JSON document that contains server infos, server addresses as URLs and an approximate location. It can also contain a legacy server list which is a list of bare IP addresses similar to the functionality the old master servers provided via UDP. This allows us to backtrack on the larger update if it won't work out. Lost functionality ================== (also known as user-visible changes) Since the client doesn't ping each server in the list anymore, it has no way of knowing its latency to the servers. This is alleviated a bit by providing an approximate location for each server (continent) so the client only has to know its own location for approximating pings.
2018-07-11 20:46:04 +00:00
Info.m_HasRank = HasRank(Info.m_aMap);
Set(Addr, IServerBrowser::SET_HTTPINFO, -1, &Info);
i++;
j++;
}
i = 0;
j = 0;
while(i < aWantedAddresses.size() && j < aSortedLegacyServers.size())
{
int Cmp = net_addr_comp(&aWantedAddresses[i].m_Addr, &m_pHttp->LegacyServer(aSortedLegacyServers[j]));
Add client-side HTTP server info Summary ======= The idea of this is that clients will not have to ping each server for server infos which takes long, leaks the client's IP address even to servers the user does not join and is a DoS vector of the game servers for attackers. For the Internet, DDNet and KoG tab, the server list is entirely fetched from the master server, filtering out servers that don't belong into the list. The favorites tab is also supposed to work that way, except for servers that are marked as "also ping this server if it's not in the master server list". The LAN tab continues to broadcast the server info packet to find servers in the LAN. How does it work? ================= The client ships with a list of master server list URLs. On first start, the client checks which of these work and selects the fastest one. Querying the server list is a HTTP GET request on that URL. The response is a JSON document that contains server infos, server addresses as URLs and an approximate location. It can also contain a legacy server list which is a list of bare IP addresses similar to the functionality the old master servers provided via UDP. This allows us to backtrack on the larger update if it won't work out. Lost functionality ================== (also known as user-visible changes) Since the client doesn't ping each server in the list anymore, it has no way of knowing its latency to the servers. This is alleviated a bit by providing an approximate location for each server (continent) so the client only has to know its own location for approximating pings.
2018-07-11 20:46:04 +00:00
if(Cmp != 0)
2015-07-09 00:08:14 +00:00
{
Add client-side HTTP server info Summary ======= The idea of this is that clients will not have to ping each server for server infos which takes long, leaks the client's IP address even to servers the user does not join and is a DoS vector of the game servers for attackers. For the Internet, DDNet and KoG tab, the server list is entirely fetched from the master server, filtering out servers that don't belong into the list. The favorites tab is also supposed to work that way, except for servers that are marked as "also ping this server if it's not in the master server list". The LAN tab continues to broadcast the server info packet to find servers in the LAN. How does it work? ================= The client ships with a list of master server list URLs. On first start, the client checks which of these work and selects the fastest one. Querying the server list is a HTTP GET request on that URL. The response is a JSON document that contains server infos, server addresses as URLs and an approximate location. It can also contain a legacy server list which is a list of bare IP addresses similar to the functionality the old master servers provided via UDP. This allows us to backtrack on the larger update if it won't work out. Lost functionality ================== (also known as user-visible changes) Since the client doesn't ping each server in the list anymore, it has no way of knowing its latency to the servers. This is alleviated a bit by providing an approximate location for each server (continent) so the client only has to know its own location for approximating pings.
2018-07-11 20:46:04 +00:00
if(Cmp < 0)
{
i++;
}
else
{
j++;
}
continue;
2014-01-03 15:14:41 +00:00
}
aWantedAddresses[i].m_Got = true;
Add client-side HTTP server info Summary ======= The idea of this is that clients will not have to ping each server for server infos which takes long, leaks the client's IP address even to servers the user does not join and is a DoS vector of the game servers for attackers. For the Internet, DDNet and KoG tab, the server list is entirely fetched from the master server, filtering out servers that don't belong into the list. The favorites tab is also supposed to work that way, except for servers that are marked as "also ping this server if it's not in the master server list". The LAN tab continues to broadcast the server info packet to find servers in the LAN. How does it work? ================= The client ships with a list of master server list URLs. On first start, the client checks which of these work and selects the fastest one. Querying the server list is a HTTP GET request on that URL. The response is a JSON document that contains server infos, server addresses as URLs and an approximate location. It can also contain a legacy server list which is a list of bare IP addresses similar to the functionality the old master servers provided via UDP. This allows us to backtrack on the larger update if it won't work out. Lost functionality ================== (also known as user-visible changes) Since the client doesn't ping each server in the list anymore, it has no way of knowing its latency to the servers. This is alleviated a bit by providing an approximate location for each server (continent) so the client only has to know its own location for approximating pings.
2018-07-11 20:46:04 +00:00
Set(m_pHttp->LegacyServer(aSortedLegacyServers[j]), LegacySetType, -1, nullptr);
i++;
j++;
}
for(const CWantedAddr &Wanted : aWantedAddresses)
{
if(!Wanted.m_Got)
{
if(Wanted.m_FallbackToPing)
{
Set(Wanted.m_Addr, LegacySetType, -1, nullptr);
}
else
{
// Also add favorites we're not allowed to ping.
if(LegacySetType == IServerBrowser::SET_FAV_ADD && !Find(Wanted.m_Addr))
{
Add(Wanted.m_Addr);
}
}
}
}
Add client-side HTTP server info Summary ======= The idea of this is that clients will not have to ping each server for server infos which takes long, leaks the client's IP address even to servers the user does not join and is a DoS vector of the game servers for attackers. For the Internet, DDNet and KoG tab, the server list is entirely fetched from the master server, filtering out servers that don't belong into the list. The favorites tab is also supposed to work that way, except for servers that are marked as "also ping this server if it's not in the master server list". The LAN tab continues to broadcast the server info packet to find servers in the LAN. How does it work? ================= The client ships with a list of master server list URLs. On first start, the client checks which of these work and selects the fastest one. Querying the server list is a HTTP GET request on that URL. The response is a JSON document that contains server infos, server addresses as URLs and an approximate location. It can also contain a legacy server list which is a list of bare IP addresses similar to the functionality the old master servers provided via UDP. This allows us to backtrack on the larger update if it won't work out. Lost functionality ================== (also known as user-visible changes) Since the client doesn't ping each server in the list anymore, it has no way of knowing its latency to the servers. This is alleviated a bit by providing an approximate location for each server (continent) so the client only has to know its own location for approximating pings.
2018-07-11 20:46:04 +00:00
return;
}
int p = 0;
Add client-side HTTP server info Summary ======= The idea of this is that clients will not have to ping each server for server infos which takes long, leaks the client's IP address even to servers the user does not join and is a DoS vector of the game servers for attackers. For the Internet, DDNet and KoG tab, the server list is entirely fetched from the master server, filtering out servers that don't belong into the list. The favorites tab is also supposed to work that way, except for servers that are marked as "also ping this server if it's not in the master server list". The LAN tab continues to broadcast the server info packet to find servers in the LAN. How does it work? ================= The client ships with a list of master server list URLs. On first start, the client checks which of these work and selects the fastest one. Querying the server list is a HTTP GET request on that URL. The response is a JSON document that contains server infos, server addresses as URLs and an approximate location. It can also contain a legacy server list which is a list of bare IP addresses similar to the functionality the old master servers provided via UDP. This allows us to backtrack on the larger update if it won't work out. Lost functionality ================== (also known as user-visible changes) Since the client doesn't ping each server in the list anymore, it has no way of knowing its latency to the servers. This is alleviated a bit by providing an approximate location for each server (continent) so the client only has to know its own location for approximating pings.
2018-07-11 20:46:04 +00:00
for(int i = 0; i < NumServers; i++)
{
NETADDR Addr;
CServerInfo Info;
m_pHttp->Server(i, &Addr, &Info);
ServerBrowserFillEstimatedLatency(OwnLocation, pPingEntries, NumPingEntries, &p, Addr, &Info);
Add client-side HTTP server info Summary ======= The idea of this is that clients will not have to ping each server for server infos which takes long, leaks the client's IP address even to servers the user does not join and is a DoS vector of the game servers for attackers. For the Internet, DDNet and KoG tab, the server list is entirely fetched from the master server, filtering out servers that don't belong into the list. The favorites tab is also supposed to work that way, except for servers that are marked as "also ping this server if it's not in the master server list". The LAN tab continues to broadcast the server info packet to find servers in the LAN. How does it work? ================= The client ships with a list of master server list URLs. On first start, the client checks which of these work and selects the fastest one. Querying the server list is a HTTP GET request on that URL. The response is a JSON document that contains server infos, server addresses as URLs and an approximate location. It can also contain a legacy server list which is a list of bare IP addresses similar to the functionality the old master servers provided via UDP. This allows us to backtrack on the larger update if it won't work out. Lost functionality ================== (also known as user-visible changes) Since the client doesn't ping each server in the list anymore, it has no way of knowing its latency to the servers. This is alleviated a bit by providing an approximate location for each server (continent) so the client only has to know its own location for approximating pings.
2018-07-11 20:46:04 +00:00
Info.m_HasRank = HasRank(Info.m_aMap);
Set(Addr, IServerBrowser::SET_HTTPINFO, -1, &Info);
}
for(int i = 0; i < NumLegacyServers; i++)
{
NETADDR Addr = m_pHttp->LegacyServer(i);
Set(Addr, IServerBrowser::SET_MASTER_ADD, -1, nullptr);
2014-01-03 15:14:41 +00:00
}
Add client-side HTTP server info Summary ======= The idea of this is that clients will not have to ping each server for server infos which takes long, leaks the client's IP address even to servers the user does not join and is a DoS vector of the game servers for attackers. For the Internet, DDNet and KoG tab, the server list is entirely fetched from the master server, filtering out servers that don't belong into the list. The favorites tab is also supposed to work that way, except for servers that are marked as "also ping this server if it's not in the master server list". The LAN tab continues to broadcast the server info packet to find servers in the LAN. How does it work? ================= The client ships with a list of master server list URLs. On first start, the client checks which of these work and selects the fastest one. Querying the server list is a HTTP GET request on that URL. The response is a JSON document that contains server infos, server addresses as URLs and an approximate location. It can also contain a legacy server list which is a list of bare IP addresses similar to the functionality the old master servers provided via UDP. This allows us to backtrack on the larger update if it won't work out. Lost functionality ================== (also known as user-visible changes) Since the client doesn't ping each server in the list anymore, it has no way of knowing its latency to the servers. This is alleviated a bit by providing an approximate location for each server (continent) so the client only has to know its own location for approximating pings.
2018-07-11 20:46:04 +00:00
}
2021-07-08 17:18:01 +00:00
void CServerBrowser::CleanUp()
{
// clear out everything
m_ServerlistHeap.Reset();
m_NumServers = 0;
m_NumSortedServers = 0;
mem_zero(m_aServerlistIp, sizeof(m_aServerlistIp));
m_pFirstReqServer = 0;
m_pLastReqServer = 0;
m_NumRequests = 0;
m_CurrentMaxRequests = g_Config.m_BrMaxRequests;
}
Add client-side HTTP server info Summary ======= The idea of this is that clients will not have to ping each server for server infos which takes long, leaks the client's IP address even to servers the user does not join and is a DoS vector of the game servers for attackers. For the Internet, DDNet and KoG tab, the server list is entirely fetched from the master server, filtering out servers that don't belong into the list. The favorites tab is also supposed to work that way, except for servers that are marked as "also ping this server if it's not in the master server list". The LAN tab continues to broadcast the server info packet to find servers in the LAN. How does it work? ================= The client ships with a list of master server list URLs. On first start, the client checks which of these work and selects the fastest one. Querying the server list is a HTTP GET request on that URL. The response is a JSON document that contains server infos, server addresses as URLs and an approximate location. It can also contain a legacy server list which is a list of bare IP addresses similar to the functionality the old master servers provided via UDP. This allows us to backtrack on the larger update if it won't work out. Lost functionality ================== (also known as user-visible changes) Since the client doesn't ping each server in the list anymore, it has no way of knowing its latency to the servers. This is alleviated a bit by providing an approximate location for each server (continent) so the client only has to know its own location for approximating pings.
2018-07-11 20:46:04 +00:00
void CServerBrowser::Update(bool ForceResort)
{
2021-06-23 05:05:49 +00:00
int64_t Timeout = time_freq();
int64_t Now = time_get();
Add client-side HTTP server info Summary ======= The idea of this is that clients will not have to ping each server for server infos which takes long, leaks the client's IP address even to servers the user does not join and is a DoS vector of the game servers for attackers. For the Internet, DDNet and KoG tab, the server list is entirely fetched from the master server, filtering out servers that don't belong into the list. The favorites tab is also supposed to work that way, except for servers that are marked as "also ping this server if it's not in the master server list". The LAN tab continues to broadcast the server info packet to find servers in the LAN. How does it work? ================= The client ships with a list of master server list URLs. On first start, the client checks which of these work and selects the fastest one. Querying the server list is a HTTP GET request on that URL. The response is a JSON document that contains server infos, server addresses as URLs and an approximate location. It can also contain a legacy server list which is a list of bare IP addresses similar to the functionality the old master servers provided via UDP. This allows us to backtrack on the larger update if it won't work out. Lost functionality ================== (also known as user-visible changes) Since the client doesn't ping each server in the list anymore, it has no way of knowing its latency to the servers. This is alleviated a bit by providing an approximate location for each server (continent) so the client only has to know its own location for approximating pings.
2018-07-11 20:46:04 +00:00
const char *pHttpBestUrl;
if(!m_pHttp->GetBestUrl(&pHttpBestUrl) && pHttpBestUrl != m_pHttpPrevBestUrl)
{
str_copy(g_Config.m_BrCachedBestServerinfoUrl, pHttpBestUrl, sizeof(g_Config.m_BrCachedBestServerinfoUrl));
m_pHttpPrevBestUrl = pHttpBestUrl;
}
Add client-side HTTP server info Summary ======= The idea of this is that clients will not have to ping each server for server infos which takes long, leaks the client's IP address even to servers the user does not join and is a DoS vector of the game servers for attackers. For the Internet, DDNet and KoG tab, the server list is entirely fetched from the master server, filtering out servers that don't belong into the list. The favorites tab is also supposed to work that way, except for servers that are marked as "also ping this server if it's not in the master server list". The LAN tab continues to broadcast the server info packet to find servers in the LAN. How does it work? ================= The client ships with a list of master server list URLs. On first start, the client checks which of these work and selects the fastest one. Querying the server list is a HTTP GET request on that URL. The response is a JSON document that contains server infos, server addresses as URLs and an approximate location. It can also contain a legacy server list which is a list of bare IP addresses similar to the functionality the old master servers provided via UDP. This allows us to backtrack on the larger update if it won't work out. Lost functionality ================== (also known as user-visible changes) Since the client doesn't ping each server in the list anymore, it has no way of knowing its latency to the servers. This is alleviated a bit by providing an approximate location for each server (continent) so the client only has to know its own location for approximating pings.
2018-07-11 20:46:04 +00:00
m_pHttp->Update();
if(m_ServerlistType != TYPE_LAN && m_RefreshingHttp && !m_pHttp->IsRefreshing())
2010-05-29 07:25:38 +00:00
{
Add client-side HTTP server info Summary ======= The idea of this is that clients will not have to ping each server for server infos which takes long, leaks the client's IP address even to servers the user does not join and is a DoS vector of the game servers for attackers. For the Internet, DDNet and KoG tab, the server list is entirely fetched from the master server, filtering out servers that don't belong into the list. The favorites tab is also supposed to work that way, except for servers that are marked as "also ping this server if it's not in the master server list". The LAN tab continues to broadcast the server info packet to find servers in the LAN. How does it work? ================= The client ships with a list of master server list URLs. On first start, the client checks which of these work and selects the fastest one. Querying the server list is a HTTP GET request on that URL. The response is a JSON document that contains server infos, server addresses as URLs and an approximate location. It can also contain a legacy server list which is a list of bare IP addresses similar to the functionality the old master servers provided via UDP. This allows us to backtrack on the larger update if it won't work out. Lost functionality ================== (also known as user-visible changes) Since the client doesn't ping each server in the list anymore, it has no way of knowing its latency to the servers. This is alleviated a bit by providing an approximate location for each server (continent) so the client only has to know its own location for approximating pings.
2018-07-11 20:46:04 +00:00
m_RefreshingHttp = false;
2021-07-08 17:18:01 +00:00
CleanUp();
Add client-side HTTP server info Summary ======= The idea of this is that clients will not have to ping each server for server infos which takes long, leaks the client's IP address even to servers the user does not join and is a DoS vector of the game servers for attackers. For the Internet, DDNet and KoG tab, the server list is entirely fetched from the master server, filtering out servers that don't belong into the list. The favorites tab is also supposed to work that way, except for servers that are marked as "also ping this server if it's not in the master server list". The LAN tab continues to broadcast the server info packet to find servers in the LAN. How does it work? ================= The client ships with a list of master server list URLs. On first start, the client checks which of these work and selects the fastest one. Querying the server list is a HTTP GET request on that URL. The response is a JSON document that contains server infos, server addresses as URLs and an approximate location. It can also contain a legacy server list which is a list of bare IP addresses similar to the functionality the old master servers provided via UDP. This allows us to backtrack on the larger update if it won't work out. Lost functionality ================== (also known as user-visible changes) Since the client doesn't ping each server in the list anymore, it has no way of knowing its latency to the servers. This is alleviated a bit by providing an approximate location for each server (continent) so the client only has to know its own location for approximating pings.
2018-07-11 20:46:04 +00:00
UpdateFromHttp();
// TODO: move this somewhere else
2021-07-08 17:18:01 +00:00
Sort();
Add client-side HTTP server info Summary ======= The idea of this is that clients will not have to ping each server for server infos which takes long, leaks the client's IP address even to servers the user does not join and is a DoS vector of the game servers for attackers. For the Internet, DDNet and KoG tab, the server list is entirely fetched from the master server, filtering out servers that don't belong into the list. The favorites tab is also supposed to work that way, except for servers that are marked as "also ping this server if it's not in the master server list". The LAN tab continues to broadcast the server info packet to find servers in the LAN. How does it work? ================= The client ships with a list of master server list URLs. On first start, the client checks which of these work and selects the fastest one. Querying the server list is a HTTP GET request on that URL. The response is a JSON document that contains server infos, server addresses as URLs and an approximate location. It can also contain a legacy server list which is a list of bare IP addresses similar to the functionality the old master servers provided via UDP. This allows us to backtrack on the larger update if it won't work out. Lost functionality ================== (also known as user-visible changes) Since the client doesn't ping each server in the list anymore, it has no way of knowing its latency to the servers. This is alleviated a bit by providing an approximate location for each server (continent) so the client only has to know its own location for approximating pings.
2018-07-11 20:46:04 +00:00
return;
2010-05-29 07:25:38 +00:00
}
Add client-side HTTP server info Summary ======= The idea of this is that clients will not have to ping each server for server infos which takes long, leaks the client's IP address even to servers the user does not join and is a DoS vector of the game servers for attackers. For the Internet, DDNet and KoG tab, the server list is entirely fetched from the master server, filtering out servers that don't belong into the list. The favorites tab is also supposed to work that way, except for servers that are marked as "also ping this server if it's not in the master server list". The LAN tab continues to broadcast the server info packet to find servers in the LAN. How does it work? ================= The client ships with a list of master server list URLs. On first start, the client checks which of these work and selects the fastest one. Querying the server list is a HTTP GET request on that URL. The response is a JSON document that contains server infos, server addresses as URLs and an approximate location. It can also contain a legacy server list which is a list of bare IP addresses similar to the functionality the old master servers provided via UDP. This allows us to backtrack on the larger update if it won't work out. Lost functionality ================== (also known as user-visible changes) Since the client doesn't ping each server in the list anymore, it has no way of knowing its latency to the servers. This is alleviated a bit by providing an approximate location for each server (continent) so the client only has to know its own location for approximating pings.
2018-07-11 20:46:04 +00:00
CServerEntry *pEntry = m_pFirstReqServer;
int Count = 0;
2022-02-14 23:12:52 +00:00
while(true)
2010-05-29 07:25:38 +00:00
{
if(!pEntry) // no more entries
break;
if(pEntry->m_RequestTime && pEntry->m_RequestTime + Timeout < Now)
{
pEntry = pEntry->m_pNextReq;
continue;
}
// no more than 10 concurrent requests
if(Count == m_CurrentMaxRequests)
2010-05-29 07:25:38 +00:00
break;
if(pEntry->m_RequestTime == 0)
2014-01-08 05:15:56 +00:00
{
if(pEntry->m_Request64Legacy)
2014-01-11 20:38:50 +00:00
RequestImpl64(pEntry->m_Addr, pEntry);
2014-01-14 20:40:55 +00:00
else
RequestImpl(pEntry->m_Addr, pEntry, nullptr, nullptr, false);
2014-01-08 05:15:56 +00:00
}
2010-05-29 07:25:38 +00:00
Count++;
pEntry = pEntry->m_pNextReq;
}
2015-07-09 00:08:14 +00:00
if(m_pFirstReqServer && Count == 0 && m_CurrentMaxRequests > 1) //NO More current Server Requests
{
//reset old ones
pEntry = m_pFirstReqServer;
2022-02-14 23:12:52 +00:00
while(true)
{
if(!pEntry) // no more entries
break;
2015-07-09 00:08:14 +00:00
pEntry->m_RequestTime = 0;
pEntry = pEntry->m_pNextReq;
}
2015-07-09 00:08:14 +00:00
//update max-requests
m_CurrentMaxRequests = m_CurrentMaxRequests / 2;
if(m_CurrentMaxRequests < 1)
m_CurrentMaxRequests = 1;
}
else if(Count == 0 && m_CurrentMaxRequests == 1) //we reached the limit, just release all left requests. IF a server sends us a packet, a new request will be added automatically, so we can delete all
2015-07-09 00:08:14 +00:00
{
pEntry = m_pFirstReqServer;
2022-02-14 23:12:52 +00:00
while(true)
{
if(!pEntry) // no more entries
2015-07-09 00:08:14 +00:00
break;
Add client-side HTTP server info Summary ======= The idea of this is that clients will not have to ping each server for server infos which takes long, leaks the client's IP address even to servers the user does not join and is a DoS vector of the game servers for attackers. For the Internet, DDNet and KoG tab, the server list is entirely fetched from the master server, filtering out servers that don't belong into the list. The favorites tab is also supposed to work that way, except for servers that are marked as "also ping this server if it's not in the master server list". The LAN tab continues to broadcast the server info packet to find servers in the LAN. How does it work? ================= The client ships with a list of master server list URLs. On first start, the client checks which of these work and selects the fastest one. Querying the server list is a HTTP GET request on that URL. The response is a JSON document that contains server infos, server addresses as URLs and an approximate location. It can also contain a legacy server list which is a list of bare IP addresses similar to the functionality the old master servers provided via UDP. This allows us to backtrack on the larger update if it won't work out. Lost functionality ================== (also known as user-visible changes) Since the client doesn't ping each server in the list anymore, it has no way of knowing its latency to the servers. This is alleviated a bit by providing an approximate location for each server (continent) so the client only has to know its own location for approximating pings.
2018-07-11 20:46:04 +00:00
CServerEntry *pNext = pEntry->m_pNextReq;
RemoveRequest(pEntry); //release request
pEntry = pNext;
2014-01-03 15:14:41 +00:00
}
}
2015-07-09 00:08:14 +00:00
2010-05-29 07:25:38 +00:00
// check if we need to resort
2020-12-22 10:13:51 +00:00
if(m_Sorthash != SortHash() || ForceResort || m_SortOnNextUpdate)
{
2010-05-29 07:25:38 +00:00
Sort();
2020-12-22 10:13:51 +00:00
m_SortOnNextUpdate = false;
}
2010-05-29 07:25:38 +00:00
}
int CServerBrowser::FindFavorite(const NETADDR &Addr) const
2010-05-29 07:25:38 +00:00
{
// search for the address
for(int i = 0; i < m_NumFavoriteServers; i++)
2010-05-29 07:25:38 +00:00
{
if(net_addr_comp(&Addr, &m_aFavoriteServers[i]) == 0)
return i;
2010-05-29 07:25:38 +00:00
}
return -1;
}
bool CServerBrowser::GotInfo(const NETADDR &Addr) const
{
CServerEntry *pEntry = ((CServerBrowser *)this)->Find(Addr);
return pEntry && pEntry->m_GotInfo;
}
bool CServerBrowser::IsFavorite(const NETADDR &Addr) const
{
return FindFavorite(Addr) >= 0;
}
bool CServerBrowser::IsFavoritePingAllowed(const NETADDR &Addr) const
{
int i = FindFavorite(Addr);
dbg_assert(i >= 0, "invalid favorite");
return i >= 0 && m_aFavoriteServersAllowPing[i];
2010-05-29 07:25:38 +00:00
}
void CServerBrowser::AddFavorite(const NETADDR &Addr)
{
CServerEntry *pEntry;
if(m_NumFavoriteServers == MAX_FAVORITES)
return;
// make sure that we don't already have the server in our list
if(IsFavorite(Addr))
2010-05-29 07:25:38 +00:00
{
return;
2010-05-29 07:25:38 +00:00
}
// add the server to the list
m_aFavoriteServers[m_NumFavoriteServers] = Addr;
m_aFavoriteServersAllowPing[m_NumFavoriteServers] = false;
m_NumFavoriteServers++;
2010-05-29 07:25:38 +00:00
pEntry = Find(Addr);
if(pEntry)
pEntry->m_Info.m_Favorite = true;
2010-05-29 07:25:38 +00:00
if(g_Config.m_Debug)
{
char aAddrStr[NETADDR_MAXSTRSIZE];
2011-12-29 22:36:53 +00:00
net_addr_str(&Addr, aAddrStr, sizeof(aAddrStr), true);
char aBuf[256];
str_format(aBuf, sizeof(aBuf), "added fav, %s", aAddrStr);
m_pConsole->Print(IConsole::OUTPUT_LEVEL_DEBUG, "client_srvbrowse", aBuf);
}
2010-05-29 07:25:38 +00:00
}
void CServerBrowser::FavoriteAllowPing(const NETADDR &Addr, bool AllowPing)
2010-05-29 07:25:38 +00:00
{
int i = FindFavorite(Addr);
dbg_assert(i >= 0, "invalid favorite");
m_aFavoriteServersAllowPing[i] = AllowPing;
}
2010-05-29 07:25:38 +00:00
void CServerBrowser::RemoveFavorite(const NETADDR &Addr)
{
int i = FindFavorite(Addr);
if(i < 0)
2010-05-29 07:25:38 +00:00
{
return;
2010-05-29 07:25:38 +00:00
}
mem_move(&m_aFavoriteServers[i], &m_aFavoriteServers[i + 1], sizeof(NETADDR) * (m_NumFavoriteServers - (i + 1)));
mem_move(&m_aFavoriteServersAllowPing[i], &m_aFavoriteServersAllowPing[i + 1], sizeof(bool) * (m_NumFavoriteServers - (i + 1)));
m_NumFavoriteServers--;
CServerEntry *pEntry = Find(Addr);
if(pEntry)
pEntry->m_Info.m_Favorite = false;
2010-05-29 07:25:38 +00:00
}
void CServerBrowser::LoadDDNetServers()
2014-09-13 14:36:25 +00:00
{
if(!m_pDDNetInfo)
return;
2014-09-18 14:13:06 +00:00
// reset servers / countries
for(int Network = 0; Network < NUM_NETWORKS; Network++)
{
CNetwork *pNet = &m_aNetworks[Network];
2015-07-09 00:08:14 +00:00
// parse JSON
const json_value *pServers = json_object_get(m_pDDNetInfo, Network == NETWORK_DDNET ? "servers" : "servers-kog");
2014-09-13 14:36:25 +00:00
if(!pServers || pServers->type != json_array)
return;
2014-09-13 14:36:25 +00:00
pNet->m_NumCountries = 0;
pNet->m_NumTypes = 0;
for(int i = 0; i < json_array_length(pServers) && pNet->m_NumCountries < MAX_COUNTRIES; i++)
2014-09-13 14:36:25 +00:00
{
// pSrv - { name, flagId, servers }
const json_value *pSrv = json_array_get(pServers, i);
const json_value *pTypes = json_object_get(pSrv, "servers");
const json_value *pName = json_object_get(pSrv, "name");
const json_value *pFlagID = json_object_get(pSrv, "flagId");
if(pSrv->type != json_object || pTypes->type != json_object || pName->type != json_string || pFlagID->type != json_integer)
2014-09-13 14:36:25 +00:00
{
dbg_msg("client_srvbrowse", "invalid attributes");
continue;
}
2014-09-19 21:52:09 +00:00
// build structure
CNetworkCountry *pCntr = &pNet->m_aCountries[pNet->m_NumCountries];
pCntr->Reset();
str_copy(pCntr->m_aName, json_string_get(pName), sizeof(pCntr->m_aName));
pCntr->m_FlagID = json_int_get(pFlagID);
// add country
for(unsigned int t = 0; t < pTypes->u.object.length; t++)
{
const char *pType = pTypes->u.object.values[t].name;
const json_value *pAddrs = pTypes->u.object.values[t].value;
if(pAddrs->type != json_array)
2014-09-18 14:13:06 +00:00
{
dbg_msg("client_srvbrowse", "invalid attributes");
continue;
2014-09-18 14:13:06 +00:00
}
// add type
if(json_array_length(pAddrs) > 0 && pNet->m_NumTypes < MAX_TYPES)
{
int Pos;
for(Pos = 0; Pos < pNet->m_NumTypes; Pos++)
{
if(!str_comp(pNet->m_aTypes[Pos], pType))
break;
}
if(Pos == pNet->m_NumTypes)
{
str_copy(pNet->m_aTypes[pNet->m_NumTypes], pType, sizeof(pNet->m_aTypes[pNet->m_NumTypes]));
pNet->m_NumTypes++;
}
}
2014-12-02 12:36:27 +00:00
// add addresses
for(int g = 0; g < json_array_length(pAddrs); g++, pCntr->m_NumServers++)
{
const json_value *pAddr = json_array_get(pAddrs, g);
if(pAddr->type != json_string)
{
dbg_msg("client_srvbrowse", "invalid attributes");
continue;
}
const char *pStr = json_string_get(pAddr);
net_addr_from_str(&pCntr->m_aServers[pCntr->m_NumServers], pStr);
str_copy(pCntr->m_aTypes[pCntr->m_NumServers], pType, sizeof(pCntr->m_aTypes[pCntr->m_NumServers]));
}
}
pNet->m_NumCountries++;
}
}
2014-09-13 14:36:25 +00:00
}
void CServerBrowser::RecheckOfficial()
{
2020-10-26 14:14:07 +00:00
for(auto &Network : m_aNetworks)
{
2020-10-26 14:14:07 +00:00
for(int i = 0; i < Network.m_NumCountries; i++)
{
2020-10-26 14:14:07 +00:00
CNetworkCountry *pCntr = &Network.m_aCountries[i];
for(int j = 0; j < pCntr->m_NumServers; j++)
{
CServerEntry *pEntry = Find(pCntr->m_aServers[j]);
if(pEntry)
{
pEntry->m_Info.m_Official = true;
}
}
}
}
}
void CServerBrowser::LoadDDNetRanks()
{
for(int i = 0; i < m_NumServers; i++)
{
if(m_ppServerlist[i]->m_Info.m_aMap[0])
m_ppServerlist[i]->m_Info.m_HasRank = HasRank(m_ppServerlist[i]->m_Info.m_aMap);
}
}
int CServerBrowser::HasRank(const char *pMap)
{
if(m_ServerlistType != IServerBrowser::TYPE_DDNET || !m_pDDNetInfo)
return -1;
const json_value *pDDNetRanks = json_object_get(m_pDDNetInfo, "maps");
if(!pDDNetRanks || pDDNetRanks->type != json_array)
return -1;
for(int i = 0; i < json_array_length(pDDNetRanks); i++)
{
const json_value *pJson = json_array_get(pDDNetRanks, i);
if(!pJson || pJson->type != json_string)
continue;
const char *pStr = json_string_get(pJson);
if(str_comp(pMap, pStr) == 0)
return 1;
}
return 0;
}
void CServerBrowser::LoadDDNetInfoJson()
{
IOHANDLE File = m_pStorage->OpenFile(DDNET_INFO, IOFLAG_READ | IOFLAG_SKIP_BOM, IStorage::TYPE_SAVE);
if(!File)
return;
const int Length = io_length(File);
if(Length <= 0)
{
io_close(File);
return;
}
char *pBuf = (char *)malloc(Length);
pBuf[0] = '\0';
io_read(File, pBuf, Length);
io_close(File);
json_value_free(m_pDDNetInfo);
m_pDDNetInfo = json_parse(pBuf, Length);
free(pBuf);
2017-09-20 20:25:55 +00:00
if(m_pDDNetInfo && m_pDDNetInfo->type != json_object)
{
json_value_free(m_pDDNetInfo);
m_pDDNetInfo = 0;
}
m_OwnLocation = CServerInfo::LOC_UNKNOWN;
if(m_pDDNetInfo)
{
const json_value &Location = (*m_pDDNetInfo)["location"];
if(Location.type != json_string || CServerInfo::ParseLocation(&m_OwnLocation, Location))
{
char aBuf[64];
2021-06-21 12:51:45 +00:00
str_format(aBuf, sizeof(aBuf), "cannot parse location from info.json: '%s'", (const char *)Location);
m_pConsole->Print(IConsole::OUTPUT_LEVEL_STANDARD, "serverbrowse", aBuf);
}
}
}
const char *CServerBrowser::GetTutorialServer()
{
2022-03-24 15:14:54 +00:00
// Use DDNet tab as default after joining tutorial, also makes sure Find() actually works
// Note that when no server info has been loaded yet, this will not return a result immediately.
g_Config.m_UiPage = CMenus::PAGE_DDNET;
Refresh(IServerBrowser::TYPE_DDNET);
CNetwork *pNetwork = &m_aNetworks[NETWORK_DDNET];
const char *pBestAddr = nullptr;
int BestLatency = std::numeric_limits<int>::max();
for(int i = 0; i < pNetwork->m_NumCountries; i++)
{
CNetworkCountry *pCntr = &pNetwork->m_aCountries[i];
for(int j = 0; j < pCntr->m_NumServers; j++)
{
CServerEntry *pEntry = Find(pCntr->m_aServers[j]);
if(!pEntry)
continue;
if(str_find(pEntry->m_Info.m_aName, "(Tutorial)") == 0)
continue;
if(pEntry->m_Info.m_NumPlayers > pEntry->m_Info.m_MaxPlayers - 10)
continue;
if(pEntry->m_Info.m_Latency >= BestLatency)
continue;
BestLatency = pEntry->m_Info.m_Latency;
pBestAddr = pEntry->m_Info.m_aAddress;
}
}
return pBestAddr;
}
const json_value *CServerBrowser::LoadDDNetInfo()
{
LoadDDNetInfoJson();
LoadDDNetServers();
Add client-side HTTP server info Summary ======= The idea of this is that clients will not have to ping each server for server infos which takes long, leaks the client's IP address even to servers the user does not join and is a DoS vector of the game servers for attackers. For the Internet, DDNet and KoG tab, the server list is entirely fetched from the master server, filtering out servers that don't belong into the list. The favorites tab is also supposed to work that way, except for servers that are marked as "also ping this server if it's not in the master server list". The LAN tab continues to broadcast the server info packet to find servers in the LAN. How does it work? ================= The client ships with a list of master server list URLs. On first start, the client checks which of these work and selects the fastest one. Querying the server list is a HTTP GET request on that URL. The response is a JSON document that contains server infos, server addresses as URLs and an approximate location. It can also contain a legacy server list which is a list of bare IP addresses similar to the functionality the old master servers provided via UDP. This allows us to backtrack on the larger update if it won't work out. Lost functionality ================== (also known as user-visible changes) Since the client doesn't ping each server in the list anymore, it has no way of knowing its latency to the servers. This is alleviated a bit by providing an approximate location for each server (continent) so the client only has to know its own location for approximating pings.
2018-07-11 20:46:04 +00:00
RecheckOfficial();
LoadDDNetRanks();
return m_pDDNetInfo;
}
bool CServerBrowser::IsRefreshing() const
{
return m_pFirstReqServer != 0;
}
2010-05-29 07:25:38 +00:00
Add client-side HTTP server info Summary ======= The idea of this is that clients will not have to ping each server for server infos which takes long, leaks the client's IP address even to servers the user does not join and is a DoS vector of the game servers for attackers. For the Internet, DDNet and KoG tab, the server list is entirely fetched from the master server, filtering out servers that don't belong into the list. The favorites tab is also supposed to work that way, except for servers that are marked as "also ping this server if it's not in the master server list". The LAN tab continues to broadcast the server info packet to find servers in the LAN. How does it work? ================= The client ships with a list of master server list URLs. On first start, the client checks which of these work and selects the fastest one. Querying the server list is a HTTP GET request on that URL. The response is a JSON document that contains server infos, server addresses as URLs and an approximate location. It can also contain a legacy server list which is a list of bare IP addresses similar to the functionality the old master servers provided via UDP. This allows us to backtrack on the larger update if it won't work out. Lost functionality ================== (also known as user-visible changes) Since the client doesn't ping each server in the list anymore, it has no way of knowing its latency to the servers. This is alleviated a bit by providing an approximate location for each server (continent) so the client only has to know its own location for approximating pings.
2018-07-11 20:46:04 +00:00
bool CServerBrowser::IsGettingServerlist() const
2010-05-29 07:25:38 +00:00
{
Add client-side HTTP server info Summary ======= The idea of this is that clients will not have to ping each server for server infos which takes long, leaks the client's IP address even to servers the user does not join and is a DoS vector of the game servers for attackers. For the Internet, DDNet and KoG tab, the server list is entirely fetched from the master server, filtering out servers that don't belong into the list. The favorites tab is also supposed to work that way, except for servers that are marked as "also ping this server if it's not in the master server list". The LAN tab continues to broadcast the server info packet to find servers in the LAN. How does it work? ================= The client ships with a list of master server list URLs. On first start, the client checks which of these work and selects the fastest one. Querying the server list is a HTTP GET request on that URL. The response is a JSON document that contains server infos, server addresses as URLs and an approximate location. It can also contain a legacy server list which is a list of bare IP addresses similar to the functionality the old master servers provided via UDP. This allows us to backtrack on the larger update if it won't work out. Lost functionality ================== (also known as user-visible changes) Since the client doesn't ping each server in the list anymore, it has no way of knowing its latency to the servers. This is alleviated a bit by providing an approximate location for each server (continent) so the client only has to know its own location for approximating pings.
2018-07-11 20:46:04 +00:00
return m_pHttp->IsRefreshing();
2010-05-29 07:25:38 +00:00
}
int CServerBrowser::LoadingProgression() const
{
if(m_NumServers == 0)
return 0;
int Servers = m_NumServers;
int Loaded = m_NumServers - m_NumRequests;
return 100.0f * Loaded / Servers;
}
void CServerBrowser::ConfigSaveCallback(IConfigManager *pConfigManager, void *pUserData)
2010-05-29 07:25:38 +00:00
{
CServerBrowser *pSelf = (CServerBrowser *)pUserData;
char aAddrStr[128];
char aBuffer[256];
2011-12-29 22:36:53 +00:00
for(int i = 0; i < pSelf->m_NumFavoriteServers; i++)
2010-05-29 07:25:38 +00:00
{
2011-12-29 22:36:53 +00:00
net_addr_str(&pSelf->m_aFavoriteServers[i], aAddrStr, sizeof(aAddrStr), true);
if(!pSelf->m_aFavoriteServersAllowPing[i])
{
str_format(aBuffer, sizeof(aBuffer), "add_favorite %s", aAddrStr);
}
else
{
// Add quotes to the first parameter for backward
// compatibility with versions that took a `r` console
// parameter.
str_format(aBuffer, sizeof(aBuffer), "add_favorite \"%s\" allow_ping", aAddrStr);
}
pConfigManager->WriteLine(aBuffer);
2010-05-29 07:25:38 +00:00
}
}
2014-09-19 21:52:09 +00:00
2014-12-14 15:45:18 +00:00
void CServerBrowser::DDNetFilterAdd(char *pFilter, const char *pName)
2014-09-19 21:52:09 +00:00
{
if(DDNetFiltered(pFilter, pName))
2014-09-19 21:52:09 +00:00
return;
2015-07-09 00:08:14 +00:00
2014-12-14 15:45:18 +00:00
char aBuf[128];
2014-09-19 21:52:09 +00:00
str_format(aBuf, sizeof(aBuf), ",%s", pName);
2014-12-14 15:45:18 +00:00
str_append(pFilter, aBuf, 128);
2014-09-19 21:52:09 +00:00
}
2014-12-14 15:45:18 +00:00
void CServerBrowser::DDNetFilterRem(char *pFilter, const char *pName)
2014-09-19 21:52:09 +00:00
{
if(!DDNetFiltered(pFilter, pName))
2014-09-19 21:52:09 +00:00
return;
// rewrite exclude/filter list
2014-12-14 15:45:18 +00:00
char aBuf[128];
2014-09-19 21:52:09 +00:00
2014-12-14 15:45:18 +00:00
str_copy(aBuf, pFilter, sizeof(aBuf));
pFilter[0] = '\0';
2014-09-19 21:52:09 +00:00
2019-03-05 09:46:29 +00:00
char aToken[128];
2019-03-11 11:54:31 +00:00
for(const char *tok = aBuf; (tok = str_next_token(tok, ",", aToken, sizeof(aToken)));)
2014-09-19 21:52:09 +00:00
{
2019-03-05 09:46:29 +00:00
if(str_comp_nocase(pName, aToken) != 0)
2014-09-19 21:52:09 +00:00
{
2014-12-14 15:45:18 +00:00
char aBuf2[128];
2019-03-05 09:46:29 +00:00
str_format(aBuf2, sizeof(aBuf2), ",%s", aToken);
2014-12-14 15:45:18 +00:00
str_append(pFilter, aBuf2, 128);
2014-09-19 21:52:09 +00:00
}
2019-03-11 11:39:54 +00:00
}
2014-09-19 21:52:09 +00:00
}
2014-12-14 15:45:18 +00:00
bool CServerBrowser::DDNetFiltered(char *pFilter, const char *pName)
2014-09-19 21:52:09 +00:00
{
2019-03-05 09:46:29 +00:00
return str_in_list(pFilter, ",", pName); // country not excluded
2014-09-19 21:52:09 +00:00
}
void CServerBrowser::CountryFilterClean(int Network)
2014-09-19 21:52:09 +00:00
{
char *pExcludeCountries = Network == NETWORK_KOG ? g_Config.m_BrFilterExcludeCountriesKoG : g_Config.m_BrFilterExcludeCountries;
2014-12-14 15:45:18 +00:00
char aNewList[128];
aNewList[0] = '\0';
2015-07-09 00:08:14 +00:00
for(auto &Net : m_aNetworks)
2014-09-19 21:52:09 +00:00
{
for(int i = 0; i < Net.m_NumCountries; i++)
2014-09-19 21:52:09 +00:00
{
const char *pName = Net.m_aCountries[i].m_aName;
if(DDNetFiltered(pExcludeCountries, pName))
{
char aBuf[128];
str_format(aBuf, sizeof(aBuf), ",%s", pName);
str_append(aNewList, aBuf, sizeof(aNewList));
}
2014-09-19 21:52:09 +00:00
}
}
str_copy(pExcludeCountries, aNewList, sizeof(g_Config.m_BrFilterExcludeCountries));
2014-09-19 21:52:09 +00:00
}
2014-12-14 15:45:18 +00:00
void CServerBrowser::TypeFilterClean(int Network)
2014-12-14 15:45:18 +00:00
{
char *pExcludeTypes = Network == NETWORK_KOG ? g_Config.m_BrFilterExcludeTypesKoG : g_Config.m_BrFilterExcludeTypes;
2014-12-14 15:45:18 +00:00
char aNewList[128];
aNewList[0] = '\0';
2015-07-09 00:08:14 +00:00
for(int i = 0; i < m_aNetworks[Network].m_NumTypes; i++)
2014-12-14 15:45:18 +00:00
{
const char *pName = m_aNetworks[Network].m_aTypes[i];
if(DDNetFiltered(pExcludeTypes, pName))
2014-12-14 15:45:18 +00:00
{
char aBuf[128];
str_format(aBuf, sizeof(aBuf), ",%s", pName);
str_append(aNewList, aBuf, sizeof(aNewList));
}
}
str_copy(pExcludeTypes, aNewList, sizeof(g_Config.m_BrFilterExcludeTypes));
2014-12-14 15:45:18 +00:00
}
Add client-side HTTP server info Summary ======= The idea of this is that clients will not have to ping each server for server infos which takes long, leaks the client's IP address even to servers the user does not join and is a DoS vector of the game servers for attackers. For the Internet, DDNet and KoG tab, the server list is entirely fetched from the master server, filtering out servers that don't belong into the list. The favorites tab is also supposed to work that way, except for servers that are marked as "also ping this server if it's not in the master server list". The LAN tab continues to broadcast the server info packet to find servers in the LAN. How does it work? ================= The client ships with a list of master server list URLs. On first start, the client checks which of these work and selects the fastest one. Querying the server list is a HTTP GET request on that URL. The response is a JSON document that contains server infos, server addresses as URLs and an approximate location. It can also contain a legacy server list which is a list of bare IP addresses similar to the functionality the old master servers provided via UDP. This allows us to backtrack on the larger update if it won't work out. Lost functionality ================== (also known as user-visible changes) Since the client doesn't ping each server in the list anymore, it has no way of knowing its latency to the servers. This is alleviated a bit by providing an approximate location for each server (continent) so the client only has to know its own location for approximating pings.
2018-07-11 20:46:04 +00:00
int CServerInfo::EstimateLatency(int Loc1, int Loc2)
{
if(Loc1 == LOC_UNKNOWN || Loc2 == LOC_UNKNOWN)
{
return 999;
}
if(Loc1 != Loc2)
{
return 199;
Add client-side HTTP server info Summary ======= The idea of this is that clients will not have to ping each server for server infos which takes long, leaks the client's IP address even to servers the user does not join and is a DoS vector of the game servers for attackers. For the Internet, DDNet and KoG tab, the server list is entirely fetched from the master server, filtering out servers that don't belong into the list. The favorites tab is also supposed to work that way, except for servers that are marked as "also ping this server if it's not in the master server list". The LAN tab continues to broadcast the server info packet to find servers in the LAN. How does it work? ================= The client ships with a list of master server list URLs. On first start, the client checks which of these work and selects the fastest one. Querying the server list is a HTTP GET request on that URL. The response is a JSON document that contains server infos, server addresses as URLs and an approximate location. It can also contain a legacy server list which is a list of bare IP addresses similar to the functionality the old master servers provided via UDP. This allows us to backtrack on the larger update if it won't work out. Lost functionality ================== (also known as user-visible changes) Since the client doesn't ping each server in the list anymore, it has no way of knowing its latency to the servers. This is alleviated a bit by providing an approximate location for each server (continent) so the client only has to know its own location for approximating pings.
2018-07-11 20:46:04 +00:00
}
return 99;
Add client-side HTTP server info Summary ======= The idea of this is that clients will not have to ping each server for server infos which takes long, leaks the client's IP address even to servers the user does not join and is a DoS vector of the game servers for attackers. For the Internet, DDNet and KoG tab, the server list is entirely fetched from the master server, filtering out servers that don't belong into the list. The favorites tab is also supposed to work that way, except for servers that are marked as "also ping this server if it's not in the master server list". The LAN tab continues to broadcast the server info packet to find servers in the LAN. How does it work? ================= The client ships with a list of master server list URLs. On first start, the client checks which of these work and selects the fastest one. Querying the server list is a HTTP GET request on that URL. The response is a JSON document that contains server infos, server addresses as URLs and an approximate location. It can also contain a legacy server list which is a list of bare IP addresses similar to the functionality the old master servers provided via UDP. This allows us to backtrack on the larger update if it won't work out. Lost functionality ================== (also known as user-visible changes) Since the client doesn't ping each server in the list anymore, it has no way of knowing its latency to the servers. This is alleviated a bit by providing an approximate location for each server (continent) so the client only has to know its own location for approximating pings.
2018-07-11 20:46:04 +00:00
}
bool CServerInfo::ParseLocation(int *pResult, const char *pString)
{
*pResult = LOC_UNKNOWN;
int Length = str_length(pString);
if(Length < 2)
{
return true;
}
// ISO continent code. Allow antarctica, but treat it as unknown.
static const char LOCATIONS[][6] = {
Add client-side HTTP server info Summary ======= The idea of this is that clients will not have to ping each server for server infos which takes long, leaks the client's IP address even to servers the user does not join and is a DoS vector of the game servers for attackers. For the Internet, DDNet and KoG tab, the server list is entirely fetched from the master server, filtering out servers that don't belong into the list. The favorites tab is also supposed to work that way, except for servers that are marked as "also ping this server if it's not in the master server list". The LAN tab continues to broadcast the server info packet to find servers in the LAN. How does it work? ================= The client ships with a list of master server list URLs. On first start, the client checks which of these work and selects the fastest one. Querying the server list is a HTTP GET request on that URL. The response is a JSON document that contains server infos, server addresses as URLs and an approximate location. It can also contain a legacy server list which is a list of bare IP addresses similar to the functionality the old master servers provided via UDP. This allows us to backtrack on the larger update if it won't work out. Lost functionality ================== (also known as user-visible changes) Since the client doesn't ping each server in the list anymore, it has no way of knowing its latency to the servers. This is alleviated a bit by providing an approximate location for each server (continent) so the client only has to know its own location for approximating pings.
2018-07-11 20:46:04 +00:00
"an", // LOC_UNKNOWN
"af", // LOC_AFRICA
"as", // LOC_ASIA
"oc", // LOC_AUSTRALIA
"eu", // LOC_EUROPE
"na", // LOC_NORTH_AMERICA
"sa", // LOC_SOUTH_AMERICA
"as:cn", // LOC_CHINA
Add client-side HTTP server info Summary ======= The idea of this is that clients will not have to ping each server for server infos which takes long, leaks the client's IP address even to servers the user does not join and is a DoS vector of the game servers for attackers. For the Internet, DDNet and KoG tab, the server list is entirely fetched from the master server, filtering out servers that don't belong into the list. The favorites tab is also supposed to work that way, except for servers that are marked as "also ping this server if it's not in the master server list". The LAN tab continues to broadcast the server info packet to find servers in the LAN. How does it work? ================= The client ships with a list of master server list URLs. On first start, the client checks which of these work and selects the fastest one. Querying the server list is a HTTP GET request on that URL. The response is a JSON document that contains server infos, server addresses as URLs and an approximate location. It can also contain a legacy server list which is a list of bare IP addresses similar to the functionality the old master servers provided via UDP. This allows us to backtrack on the larger update if it won't work out. Lost functionality ================== (also known as user-visible changes) Since the client doesn't ping each server in the list anymore, it has no way of knowing its latency to the servers. This is alleviated a bit by providing an approximate location for each server (continent) so the client only has to know its own location for approximating pings.
2018-07-11 20:46:04 +00:00
};
for(int i = std::size(LOCATIONS) - 1; i >= 0; i--)
Add client-side HTTP server info Summary ======= The idea of this is that clients will not have to ping each server for server infos which takes long, leaks the client's IP address even to servers the user does not join and is a DoS vector of the game servers for attackers. For the Internet, DDNet and KoG tab, the server list is entirely fetched from the master server, filtering out servers that don't belong into the list. The favorites tab is also supposed to work that way, except for servers that are marked as "also ping this server if it's not in the master server list". The LAN tab continues to broadcast the server info packet to find servers in the LAN. How does it work? ================= The client ships with a list of master server list URLs. On first start, the client checks which of these work and selects the fastest one. Querying the server list is a HTTP GET request on that URL. The response is a JSON document that contains server infos, server addresses as URLs and an approximate location. It can also contain a legacy server list which is a list of bare IP addresses similar to the functionality the old master servers provided via UDP. This allows us to backtrack on the larger update if it won't work out. Lost functionality ================== (also known as user-visible changes) Since the client doesn't ping each server in the list anymore, it has no way of knowing its latency to the servers. This is alleviated a bit by providing an approximate location for each server (continent) so the client only has to know its own location for approximating pings.
2018-07-11 20:46:04 +00:00
{
if(str_startswith(pString, LOCATIONS[i]))
Add client-side HTTP server info Summary ======= The idea of this is that clients will not have to ping each server for server infos which takes long, leaks the client's IP address even to servers the user does not join and is a DoS vector of the game servers for attackers. For the Internet, DDNet and KoG tab, the server list is entirely fetched from the master server, filtering out servers that don't belong into the list. The favorites tab is also supposed to work that way, except for servers that are marked as "also ping this server if it's not in the master server list". The LAN tab continues to broadcast the server info packet to find servers in the LAN. How does it work? ================= The client ships with a list of master server list URLs. On first start, the client checks which of these work and selects the fastest one. Querying the server list is a HTTP GET request on that URL. The response is a JSON document that contains server infos, server addresses as URLs and an approximate location. It can also contain a legacy server list which is a list of bare IP addresses similar to the functionality the old master servers provided via UDP. This allows us to backtrack on the larger update if it won't work out. Lost functionality ================== (also known as user-visible changes) Since the client doesn't ping each server in the list anymore, it has no way of knowing its latency to the servers. This is alleviated a bit by providing an approximate location for each server (continent) so the client only has to know its own location for approximating pings.
2018-07-11 20:46:04 +00:00
{
*pResult = i;
return false;
}
}
return true;
}
bool IsVanilla(const CServerInfo *pInfo)
{
return !str_comp(pInfo->m_aGameType, "DM") || !str_comp(pInfo->m_aGameType, "TDM") || !str_comp(pInfo->m_aGameType, "CTF");
}
bool IsCatch(const CServerInfo *pInfo)
{
return str_find_nocase(pInfo->m_aGameType, "catch");
}
bool IsInsta(const CServerInfo *pInfo)
{
return str_find_nocase(pInfo->m_aGameType, "idm") || str_find_nocase(pInfo->m_aGameType, "itdm") || str_find_nocase(pInfo->m_aGameType, "ictf");
}
bool IsFNG(const CServerInfo *pInfo)
{
return str_find_nocase(pInfo->m_aGameType, "fng");
}
bool IsRace(const CServerInfo *pInfo)
{
return str_find_nocase(pInfo->m_aGameType, "race") || str_find_nocase(pInfo->m_aGameType, "fastcap");
}
bool IsFastCap(const CServerInfo *pInfo)
{
return str_find_nocase(pInfo->m_aGameType, "fastcap");
}
bool IsBlockInfectionZ(const CServerInfo *pInfo)
{
return str_find_nocase(pInfo->m_aGameType, "blockz") ||
str_find_nocase(pInfo->m_aGameType, "infectionz");
}
bool IsBlockWorlds(const CServerInfo *pInfo)
{
return (str_startswith(pInfo->m_aGameType, "bw ")) || (str_comp_nocase(pInfo->m_aGameType, "bw") == 0);
Add client-side HTTP server info Summary ======= The idea of this is that clients will not have to ping each server for server infos which takes long, leaks the client's IP address even to servers the user does not join and is a DoS vector of the game servers for attackers. For the Internet, DDNet and KoG tab, the server list is entirely fetched from the master server, filtering out servers that don't belong into the list. The favorites tab is also supposed to work that way, except for servers that are marked as "also ping this server if it's not in the master server list". The LAN tab continues to broadcast the server info packet to find servers in the LAN. How does it work? ================= The client ships with a list of master server list URLs. On first start, the client checks which of these work and selects the fastest one. Querying the server list is a HTTP GET request on that URL. The response is a JSON document that contains server infos, server addresses as URLs and an approximate location. It can also contain a legacy server list which is a list of bare IP addresses similar to the functionality the old master servers provided via UDP. This allows us to backtrack on the larger update if it won't work out. Lost functionality ================== (also known as user-visible changes) Since the client doesn't ping each server in the list anymore, it has no way of knowing its latency to the servers. This is alleviated a bit by providing an approximate location for each server (continent) so the client only has to know its own location for approximating pings.
2018-07-11 20:46:04 +00:00
}
bool IsCity(const CServerInfo *pInfo)
{
return str_find_nocase(pInfo->m_aGameType, "city");
}
bool IsDDRace(const CServerInfo *pInfo)
{
return str_find_nocase(pInfo->m_aGameType, "ddrace") || str_find_nocase(pInfo->m_aGameType, "mkrace");
}
bool IsDDNet(const CServerInfo *pInfo)
{
return str_find_nocase(pInfo->m_aGameType, "ddracenet") || str_find_nocase(pInfo->m_aGameType, "ddnet");
}
// other
bool Is64Player(const CServerInfo *pInfo)
{
return str_find(pInfo->m_aGameType, "64") || str_find(pInfo->m_aName, "64") || IsDDNet(pInfo);
}
bool IsPlus(const CServerInfo *pInfo)
{
return str_find(pInfo->m_aGameType, "+");
}