1/* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */
2/* If you are missing that file, acquire a complete release at teeworlds.com. */
3#ifndef ENGINE_SERVERBROWSER_H
4#define ENGINE_SERVERBROWSER_H
5
6#include <base/hash.h>
7#include <base/system.h>
8
9#include <engine/map.h>
10#include <engine/shared/protocol.h>
11
12#include "kernel.h"
13
14#include <unordered_set>
15#include <vector>
16
17static constexpr const char *DDNET_INFO_FILE = "ddnet-info.json";
18static constexpr const char *DDNET_INFO_URL = "https://info.ddnet.org/info";
19
20class CUIElement;
21
22class CServerInfo
23{
24public:
25 enum
26 {
27 LOC_UNKNOWN = 0,
28 LOC_AFRICA,
29 LOC_ASIA,
30 LOC_AUSTRALIA,
31 LOC_EUROPE,
32 LOC_NORTH_AMERICA,
33 LOC_SOUTH_AMERICA,
34 // Special case China because it has an exceptionally bad
35 // connection to the outside due to the Great Firewall of
36 // China:
37 // https://en.wikipedia.org/w/index.php?title=Great_Firewall&oldid=1019589632
38 LOC_CHINA,
39 NUM_LOCS,
40 };
41
42 enum EClientScoreKind
43 {
44 CLIENT_SCORE_KIND_UNSPECIFIED,
45 CLIENT_SCORE_KIND_POINTS,
46 CLIENT_SCORE_KIND_TIME,
47 CLIENT_SCORE_KIND_TIME_BACKCOMPAT,
48 };
49
50 enum ERankState
51 {
52 RANK_UNAVAILABLE,
53 RANK_RANKED,
54 RANK_UNRANKED,
55 };
56
57 enum
58 {
59 MAX_COMMUNITY_ID_LENGTH = 32,
60 MAX_COMMUNITY_COUNTRY_LENGTH = 32,
61 MAX_COMMUNITY_TYPE_LENGTH = 32,
62 };
63
64 class CClient
65 {
66 public:
67 char m_aName[MAX_NAME_LENGTH];
68 char m_aClan[MAX_CLAN_LENGTH];
69 int m_Country;
70 int m_Score;
71 bool m_Player;
72 bool m_Afk;
73
74 // skin info
75 char m_aSkin[24 + 1];
76 bool m_CustomSkinColors;
77 int m_CustomSkinColorBody;
78 int m_CustomSkinColorFeet;
79
80 int m_FriendState;
81 };
82
83 int m_ServerIndex;
84
85 int m_Type;
86 uint64_t m_ReceivedPackets;
87 int m_NumReceivedClients;
88
89 int m_NumAddresses;
90 NETADDR m_aAddresses[MAX_SERVER_ADDRESSES];
91
92 int m_QuickSearchHit;
93 int m_FriendState;
94 int m_FriendNum;
95
96 int m_MaxClients;
97 int m_NumClients;
98 int m_MaxPlayers;
99 int m_NumPlayers;
100 int m_Flags;
101 EClientScoreKind m_ClientScoreKind;
102 TRISTATE m_Favorite;
103 TRISTATE m_FavoriteAllowPing;
104 char m_aCommunityId[MAX_COMMUNITY_ID_LENGTH];
105 char m_aCommunityCountry[MAX_COMMUNITY_COUNTRY_LENGTH];
106 char m_aCommunityType[MAX_COMMUNITY_TYPE_LENGTH];
107 int m_Location;
108 bool m_LatencyIsEstimated;
109 int m_Latency; // in ms
110 ERankState m_HasRank;
111 char m_aGameType[16];
112 char m_aName[64];
113 char m_aMap[MAX_MAP_LENGTH];
114 int m_MapCrc;
115 int m_MapSize;
116 char m_aVersion[32];
117 char m_aAddress[MAX_SERVER_ADDRESSES * NETADDR_MAXSTRSIZE];
118 CClient m_aClients[SERVERINFO_MAX_CLIENTS];
119 int m_NumFilteredPlayers;
120 bool m_RequiresLogin;
121
122 static int EstimateLatency(int Loc1, int Loc2);
123 static bool ParseLocation(int *pResult, const char *pString);
124 void InfoToString(char *pBuffer, int BufferSize) const;
125};
126
127class CCommunityCountryServer
128{
129 NETADDR m_Address;
130 char m_aTypeName[CServerInfo::MAX_COMMUNITY_TYPE_LENGTH];
131
132public:
133 CCommunityCountryServer(NETADDR Address, const char *pTypeName) :
134 m_Address(Address)
135 {
136 str_copy(dst&: m_aTypeName, src: pTypeName);
137 }
138
139 NETADDR Address() const { return m_Address; }
140 const char *TypeName() const { return m_aTypeName; }
141};
142
143class CCommunityCountry
144{
145 friend class CServerBrowser;
146
147 char m_aName[CServerInfo::MAX_COMMUNITY_COUNTRY_LENGTH];
148 int m_FlagId;
149 std::vector<CCommunityCountryServer> m_vServers;
150
151public:
152 CCommunityCountry(const char *pName, int FlagId) :
153 m_FlagId(FlagId)
154 {
155 str_copy(dst&: m_aName, src: pName);
156 }
157
158 const char *Name() const { return m_aName; }
159 int FlagId() const { return m_FlagId; }
160 const std::vector<CCommunityCountryServer> &Servers() const { return m_vServers; }
161};
162
163class CCommunityType
164{
165 char m_aName[CServerInfo::MAX_COMMUNITY_TYPE_LENGTH];
166
167public:
168 CCommunityType(const char *pName)
169 {
170 str_copy(dst&: m_aName, src: pName);
171 }
172
173 const char *Name() const { return m_aName; }
174};
175
176class CCommunityMap
177{
178 char m_aName[MAX_MAP_LENGTH];
179
180public:
181 CCommunityMap(const char *pName)
182 {
183 str_copy(dst&: m_aName, src: pName);
184 }
185
186 const char *Name() const { return m_aName; }
187
188 bool operator==(const CCommunityMap &Other) const
189 {
190 return str_comp(a: Name(), b: Other.Name()) == 0;
191 }
192
193 bool operator!=(const CCommunityMap &Other) const
194 {
195 return !(*this == Other);
196 }
197
198 struct SHash
199 {
200 size_t operator()(const CCommunityMap &Map) const
201 {
202 return str_quickhash(str: Map.Name());
203 }
204 };
205};
206
207class CCommunity
208{
209 friend class CServerBrowser;
210
211 char m_aId[CServerInfo::MAX_COMMUNITY_ID_LENGTH];
212 char m_aName[64];
213 SHA256_DIGEST m_IconSha256;
214 char m_aIconUrl[128];
215 std::vector<CCommunityCountry> m_vCountries;
216 std::vector<CCommunityType> m_vTypes;
217 bool m_HasFinishes = false;
218 std::unordered_set<CCommunityMap, CCommunityMap::SHash> m_FinishedMaps;
219
220public:
221 CCommunity(const char *pId, const char *pName, SHA256_DIGEST IconSha256, const char *pIconUrl) :
222 m_IconSha256(IconSha256)
223 {
224 str_copy(dst&: m_aId, src: pId);
225 str_copy(dst&: m_aName, src: pName);
226 str_copy(dst&: m_aIconUrl, src: pIconUrl);
227 }
228
229 const char *Id() const { return m_aId; }
230 const char *Name() const { return m_aName; }
231 const char *IconUrl() const { return m_aIconUrl; }
232 const SHA256_DIGEST &IconSha256() const { return m_IconSha256; }
233 const std::vector<CCommunityCountry> &Countries() const { return m_vCountries; }
234 const std::vector<CCommunityType> &Types() const { return m_vTypes; }
235 bool HasCountry(const char *pCountryName) const;
236 bool HasType(const char *pTypeName) const;
237 bool HasRanks() const { return m_HasFinishes; }
238 CServerInfo::ERankState HasRank(const char *pMap) const;
239};
240
241class IFilterList
242{
243public:
244 virtual void Add(const char *pElement) = 0;
245 virtual void Remove(const char *pElement) = 0;
246 virtual void Clear() = 0;
247 virtual bool Empty() const = 0;
248 virtual bool Filtered(const char *pElement) const = 0;
249};
250
251class ICommunityCache
252{
253public:
254 virtual void Update(bool Force) = 0;
255 virtual const std::vector<const CCommunity *> &SelectedCommunities() const = 0;
256 virtual const std::vector<const CCommunityCountry *> &SelectableCountries() const = 0;
257 virtual const std::vector<const CCommunityType *> &SelectableTypes() const = 0;
258 virtual bool AnyRanksAvailable() const = 0;
259 virtual bool CountriesTypesFilterAvailable() const = 0;
260 virtual const char *CountryTypeFilterKey() const = 0;
261};
262
263class IServerBrowser : public IInterface
264{
265 MACRO_INTERFACE("serverbrowser")
266public:
267 /* Constants: Server Browser Sorting
268 SORT_NAME - Sort by name.
269 SORT_PING - Sort by ping.
270 SORT_MAP - Sort by map
271 SORT_GAMETYPE - Sort by game type. DM, TDM etc.
272 SORT_NUMPLAYERS - Sort after how many players there are on the server.
273 SORT_NUMFRIENDS - Sort after how many friends there are on the server.
274 */
275 enum
276 {
277 SORT_NAME = 0,
278 SORT_PING,
279 SORT_MAP,
280 SORT_GAMETYPE,
281 SORT_NUMPLAYERS,
282 SORT_NUMFRIENDS,
283
284 QUICK_SERVERNAME = 1,
285 QUICK_PLAYER = 2,
286 QUICK_MAPNAME = 4,
287
288 TYPE_INTERNET = 0,
289 TYPE_LAN,
290 TYPE_FAVORITES,
291 TYPE_FAVORITE_COMMUNITY_1,
292 TYPE_FAVORITE_COMMUNITY_2,
293 TYPE_FAVORITE_COMMUNITY_3,
294 TYPE_FAVORITE_COMMUNITY_4,
295 TYPE_FAVORITE_COMMUNITY_5,
296 NUM_TYPES,
297 };
298
299 static constexpr const char *COMMUNITY_DDNET = "ddnet";
300 static constexpr const char *COMMUNITY_NONE = "none";
301 /**
302 * Special community value for country/type filters that
303 * affect all communities.
304 */
305 static constexpr const char *COMMUNITY_ALL = "all";
306
307 static constexpr const char *SEARCH_EXCLUDE_TOKEN = ";";
308
309 virtual void Refresh(int Type, bool Force = false) = 0;
310 virtual bool IsGettingServerlist() const = 0;
311 virtual bool IsRefreshing() const = 0;
312 virtual int LoadingProgression() const = 0;
313
314 virtual int NumServers() const = 0;
315
316 virtual int Players(const CServerInfo &Item) const = 0;
317 virtual int Max(const CServerInfo &Item) const = 0;
318
319 virtual int NumSortedServers() const = 0;
320 virtual int NumSortedPlayers() const = 0;
321 virtual const CServerInfo *SortedGet(int Index) const = 0;
322
323 virtual const std::vector<CCommunity> &Communities() const = 0;
324 virtual const CCommunity *Community(const char *pCommunityId) const = 0;
325 virtual std::vector<const CCommunity *> SelectedCommunities() const = 0;
326 virtual std::vector<const CCommunity *> FavoriteCommunities() const = 0;
327 virtual std::vector<const CCommunity *> CurrentCommunities() const = 0;
328 virtual unsigned CurrentCommunitiesHash() const = 0;
329
330 virtual bool DDNetInfoAvailable() const = 0;
331 virtual SHA256_DIGEST DDNetInfoSha256() const = 0;
332
333 virtual ICommunityCache &CommunityCache() = 0;
334 virtual const ICommunityCache &CommunityCache() const = 0;
335 virtual IFilterList &FavoriteCommunitiesFilter() = 0;
336 virtual IFilterList &CommunitiesFilter() = 0;
337 virtual IFilterList &CountriesFilter() = 0;
338 virtual IFilterList &TypesFilter() = 0;
339 virtual const IFilterList &FavoriteCommunitiesFilter() const = 0;
340 virtual const IFilterList &CommunitiesFilter() const = 0;
341 virtual const IFilterList &CountriesFilter() const = 0;
342 virtual const IFilterList &TypesFilter() const = 0;
343 virtual void CleanFilters() = 0;
344
345 virtual int GetCurrentType() = 0;
346 virtual const char *GetTutorialServer() = 0;
347};
348
349#endif
350