ddnet/src/engine/serverbrowser.h

314 lines
7.3 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. */
2010-05-29 07:25:38 +00:00
#ifndef ENGINE_SERVERBROWSER_H
#define ENGINE_SERVERBROWSER_H
#include <base/hash.h>
#include <base/system.h>
2020-06-18 16:29:27 +00:00
#include <engine/map.h>
#include <engine/shared/protocol.h>
2010-05-29 07:25:38 +00:00
#include "kernel.h"
#include <unordered_set>
#include <vector>
static constexpr const char *DDNET_INFO_FILE = "ddnet-info.json";
static constexpr const char *DDNET_INFO_URL = "https://info.ddnet.org/info";
2018-08-23 07:57:35 +00:00
class CUIElement;
2010-05-29 07:25:38 +00:00
class CServerInfo
{
public:
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
enum
{
LOC_UNKNOWN = 0,
LOC_AFRICA,
LOC_ASIA,
LOC_AUSTRALIA,
LOC_EUROPE,
LOC_NORTH_AMERICA,
LOC_SOUTH_AMERICA,
// Special case China because it has an exceptionally bad
// connection to the outside due to the Great Firewall of
// China:
// https://en.wikipedia.org/w/index.php?title=Great_Firewall&oldid=1019589632
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
NUM_LOCS,
};
2023-09-30 10:35:53 +00:00
enum EClientScoreKind
{
CLIENT_SCORE_KIND_UNSPECIFIED,
CLIENT_SCORE_KIND_POINTS,
CLIENT_SCORE_KIND_TIME,
CLIENT_SCORE_KIND_TIME_BACKCOMPAT,
};
enum ERankState
{
RANK_UNAVAILABLE,
RANK_RANKED,
RANK_UNRANKED,
};
enum
{
MAX_COMMUNITY_ID_LENGTH = 32,
MAX_COMMUNITY_COUNTRY_LENGTH = 32,
MAX_COMMUNITY_TYPE_LENGTH = 32,
};
class CClient
2010-05-29 07:25:38 +00:00
{
public:
char m_aName[MAX_NAME_LENGTH];
char m_aClan[MAX_CLAN_LENGTH];
int m_Country;
2010-05-29 07:25:38 +00:00
int m_Score;
bool m_Player;
bool m_Afk;
2011-08-11 08:59:14 +00:00
2023-01-07 08:20:25 +00:00
// skin info
char m_aSkin[24 + 1];
bool m_CustomSkinColors;
int m_CustomSkinColorBody;
int m_CustomSkinColorFeet;
2011-06-26 15:10:13 +00:00
int m_FriendState;
};
2010-05-29 07:25:38 +00:00
int m_ServerIndex;
int m_Type;
2021-06-23 05:05:49 +00:00
uint64_t m_ReceivedPackets;
int m_NumReceivedClients;
int m_NumAddresses;
NETADDR m_aAddresses[MAX_SERVER_ADDRESSES];
2010-05-29 07:25:38 +00:00
int m_QuickSearchHit;
2011-06-26 15:10:13 +00:00
int m_FriendState;
int m_FriendNum;
int m_MaxClients;
int m_NumClients;
2010-05-29 07:25:38 +00:00
int m_MaxPlayers;
int m_NumPlayers;
int m_Flags;
2023-09-30 10:35:53 +00:00
EClientScoreKind m_ClientScoreKind;
TRISTATE m_Favorite;
TRISTATE m_FavoriteAllowPing;
char m_aCommunityId[MAX_COMMUNITY_ID_LENGTH];
char m_aCommunityCountry[MAX_COMMUNITY_COUNTRY_LENGTH];
char m_aCommunityType[MAX_COMMUNITY_TYPE_LENGTH];
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 m_Location;
bool m_LatencyIsEstimated;
2010-05-29 07:25:38 +00:00
int m_Latency; // in ms
ERankState m_HasRank;
2010-05-29 07:25:38 +00:00
char m_aGameType[16];
char m_aName[64];
2020-06-18 16:29:27 +00:00
char m_aMap[MAX_MAP_LENGTH];
int m_MapCrc;
int m_MapSize;
2010-05-29 07:25:38 +00:00
char m_aVersion[32];
char m_aAddress[MAX_SERVER_ADDRESSES * NETADDR_MAXSTRSIZE];
2022-01-02 01:27:37 +00:00
CClient m_aClients[SERVERINFO_MAX_CLIENTS];
int m_NumFilteredPlayers;
2020-10-12 10:29:47 +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
static int EstimateLatency(int Loc1, int Loc2);
static bool ParseLocation(int *pResult, const char *pString);
void InfoToString(char *pBuffer, int BufferSize) const;
2010-05-29 07:25:38 +00:00
};
class CCommunityCountryServer
{
NETADDR m_Address;
char m_aTypeName[CServerInfo::MAX_COMMUNITY_TYPE_LENGTH];
public:
CCommunityCountryServer(NETADDR Address, const char *pTypeName) :
m_Address(Address)
{
str_copy(m_aTypeName, pTypeName);
}
NETADDR Address() const { return m_Address; }
const char *TypeName() const { return m_aTypeName; }
};
class CCommunityCountry
{
friend class CServerBrowser;
char m_aName[CServerInfo::MAX_COMMUNITY_COUNTRY_LENGTH];
int m_FlagId;
std::vector<CCommunityCountryServer> m_vServers;
public:
CCommunityCountry(const char *pName, int FlagId) :
m_FlagId(FlagId)
{
str_copy(m_aName, pName);
}
const char *Name() const { return m_aName; }
int FlagId() const { return m_FlagId; }
const std::vector<CCommunityCountryServer> &Servers() const { return m_vServers; }
};
class CCommunityType
{
char m_aName[CServerInfo::MAX_COMMUNITY_TYPE_LENGTH];
public:
CCommunityType(const char *pName)
{
str_copy(m_aName, pName);
}
const char *Name() const { return m_aName; }
};
class CCommunityMap
{
char m_aName[MAX_MAP_LENGTH];
public:
CCommunityMap(const char *pName)
{
str_copy(m_aName, pName);
}
const char *Name() const { return m_aName; }
bool operator==(const CCommunityMap &Other) const
{
return str_comp(Name(), Other.Name()) == 0;
}
bool operator!=(const CCommunityMap &Other) const
{
return !(*this == Other);
}
struct SHash
{
size_t operator()(const CCommunityMap &Map) const
{
return str_quickhash(Map.Name());
}
};
};
class CCommunity
{
friend class CServerBrowser;
char m_aId[CServerInfo::MAX_COMMUNITY_ID_LENGTH];
char m_aName[64];
SHA256_DIGEST m_IconSha256;
char m_aIconUrl[128];
std::vector<CCommunityCountry> m_vCountries;
std::vector<CCommunityType> m_vTypes;
bool m_HasFinishes = false;
std::unordered_set<CCommunityMap, CCommunityMap::SHash> m_FinishedMaps;
public:
CCommunity(const char *pId, const char *pName, SHA256_DIGEST IconSha256, const char *pIconUrl) :
m_IconSha256(IconSha256)
{
str_copy(m_aId, pId);
str_copy(m_aName, pName);
str_copy(m_aIconUrl, pIconUrl);
}
const char *Id() const { return m_aId; }
const char *Name() const { return m_aName; }
const char *IconUrl() const { return m_aIconUrl; }
const SHA256_DIGEST &IconSha256() const { return m_IconSha256; }
const std::vector<CCommunityCountry> &Countries() const { return m_vCountries; }
const std::vector<CCommunityType> &Types() const { return m_vTypes; }
bool HasRanks() const { return m_HasFinishes; }
CServerInfo::ERankState HasRank(const char *pMap) const;
};
class IFilterList
{
public:
virtual void Add(const char *pElement) = 0;
virtual void Remove(const char *pElement) = 0;
virtual void Clear() = 0;
virtual bool Empty() const = 0;
virtual bool Filtered(const char *pElement) const = 0;
};
2010-05-29 07:25:38 +00:00
class IServerBrowser : public IInterface
{
MACRO_INTERFACE("serverbrowser")
2010-05-29 07:25:38 +00:00
public:
/* Constants: Server Browser Sorting
SORT_NAME - Sort by name.
SORT_PING - Sort by ping.
SORT_MAP - Sort by map
SORT_GAMETYPE - Sort by game type. DM, TDM etc.
SORT_NUMPLAYERS - Sort after how many players there are on the server.
*/
enum
{
2010-05-29 07:25:38 +00:00
SORT_NAME = 0,
SORT_PING,
SORT_MAP,
SORT_GAMETYPE,
SORT_NUMPLAYERS,
QUICK_SERVERNAME = 1,
QUICK_PLAYER = 2,
QUICK_MAPNAME = 4,
TYPE_INTERNET = 0,
TYPE_LAN,
TYPE_FAVORITES,
NUM_TYPES,
2010-05-29 07:25:38 +00:00
};
static constexpr const char *COMMUNITY_DDNET = "ddnet";
static constexpr const char *COMMUNITY_NONE = "none";
static constexpr const char *SEARCH_EXCLUDE_TOKEN = ";";
2010-05-29 07:25:38 +00:00
virtual void Refresh(int Type) = 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
virtual bool IsGettingServerlist() const = 0;
virtual bool IsRefreshing() const = 0;
virtual int LoadingProgression() const = 0;
2010-05-29 07:25:38 +00:00
virtual int NumServers() const = 0;
virtual int Players(const CServerInfo &Item) const = 0;
virtual int Max(const CServerInfo &Item) const = 0;
2010-05-29 07:25:38 +00:00
virtual int NumSortedServers() const = 0;
virtual int NumSortedPlayers() const = 0;
2010-05-29 07:25:38 +00:00
virtual const CServerInfo *SortedGet(int Index) const = 0;
virtual const std::vector<CCommunity> &Communities() const = 0;
virtual const CCommunity *Community(const char *pCommunityId) const = 0;
virtual std::vector<const CCommunity *> SelectedCommunities() const = 0;
virtual int64_t DDNetInfoUpdateTime() const = 0;
virtual IFilterList &CommunitiesFilter() = 0;
virtual IFilterList &CountriesFilter() = 0;
virtual IFilterList &TypesFilter() = 0;
virtual const IFilterList &CommunitiesFilter() const = 0;
virtual const IFilterList &CountriesFilter() const = 0;
virtual const IFilterList &TypesFilter() const = 0;
virtual void CleanFilters() = 0;
2014-12-14 15:45:18 +00:00
virtual int GetCurrentType() = 0;
virtual const char *GetTutorialServer() = 0;
2010-05-29 07:25:38 +00:00
};
#endif