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};
125
126class CCommunityCountryServer
127{
128 NETADDR m_Address;
129 char m_aTypeName[CServerInfo::MAX_COMMUNITY_TYPE_LENGTH];
130
131public:
132 CCommunityCountryServer(NETADDR Address, const char *pTypeName) :
133 m_Address(Address)
134 {
135 str_copy(dst&: m_aTypeName, src: pTypeName);
136 }
137
138 NETADDR Address() const { return m_Address; }
139 const char *TypeName() const { return m_aTypeName; }
140};
141
142class CCommunityCountry
143{
144 friend class CServerBrowser;
145
146 char m_aName[CServerInfo::MAX_COMMUNITY_COUNTRY_LENGTH];
147 int m_FlagId;
148 std::vector<CCommunityCountryServer> m_vServers;
149
150public:
151 CCommunityCountry(const char *pName, int FlagId) :
152 m_FlagId(FlagId)
153 {
154 str_copy(dst&: m_aName, src: pName);
155 }
156
157 const char *Name() const { return m_aName; }
158 int FlagId() const { return m_FlagId; }
159 const std::vector<CCommunityCountryServer> &Servers() const { return m_vServers; }
160};
161
162class CCommunityType
163{
164 char m_aName[CServerInfo::MAX_COMMUNITY_TYPE_LENGTH];
165
166public:
167 CCommunityType(const char *pName)
168 {
169 str_copy(dst&: m_aName, src: pName);
170 }
171
172 const char *Name() const { return m_aName; }
173};
174
175class CCommunityMap
176{
177 char m_aName[MAX_MAP_LENGTH];
178
179public:
180 CCommunityMap(const char *pName)
181 {
182 str_copy(dst&: m_aName, src: pName);
183 }
184
185 const char *Name() const { return m_aName; }
186
187 bool operator==(const CCommunityMap &Other) const
188 {
189 return str_comp(a: Name(), b: Other.Name()) == 0;
190 }
191
192 bool operator!=(const CCommunityMap &Other) const
193 {
194 return !(*this == Other);
195 }
196
197 struct SHash
198 {
199 size_t operator()(const CCommunityMap &Map) const
200 {
201 return str_quickhash(str: Map.Name());
202 }
203 };
204};
205
206class CCommunity
207{
208 friend class CServerBrowser;
209
210 char m_aId[CServerInfo::MAX_COMMUNITY_ID_LENGTH];
211 char m_aName[64];
212 SHA256_DIGEST m_IconSha256;
213 char m_aIconUrl[128];
214 std::vector<CCommunityCountry> m_vCountries;
215 std::vector<CCommunityType> m_vTypes;
216 bool m_HasFinishes = false;
217 std::unordered_set<CCommunityMap, CCommunityMap::SHash> m_FinishedMaps;
218
219public:
220 CCommunity(const char *pId, const char *pName, SHA256_DIGEST IconSha256, const char *pIconUrl) :
221 m_IconSha256(IconSha256)
222 {
223 str_copy(dst&: m_aId, src: pId);
224 str_copy(dst&: m_aName, src: pName);
225 str_copy(dst&: m_aIconUrl, src: pIconUrl);
226 }
227
228 const char *Id() const { return m_aId; }
229 const char *Name() const { return m_aName; }
230 const char *IconUrl() const { return m_aIconUrl; }
231 const SHA256_DIGEST &IconSha256() const { return m_IconSha256; }
232 const std::vector<CCommunityCountry> &Countries() const { return m_vCountries; }
233 const std::vector<CCommunityType> &Types() const { return m_vTypes; }
234 bool HasCountry(const char *pCountryName) const;
235 bool HasType(const char *pTypeName) const;
236 bool HasRanks() const { return m_HasFinishes; }
237 CServerInfo::ERankState HasRank(const char *pMap) const;
238};
239
240class IFilterList
241{
242public:
243 virtual void Add(const char *pElement) = 0;
244 virtual void Remove(const char *pElement) = 0;
245 virtual void Clear() = 0;
246 virtual bool Empty() const = 0;
247 virtual bool Filtered(const char *pElement) const = 0;
248};
249
250class ICommunityCache
251{
252public:
253 virtual void Update(bool Force) = 0;
254 virtual const std::vector<const CCommunity *> &SelectedCommunities() const = 0;
255 virtual const std::vector<const CCommunityCountry *> &SelectableCountries() const = 0;
256 virtual const std::vector<const CCommunityType *> &SelectableTypes() const = 0;
257 virtual bool AnyRanksAvailable() const = 0;
258 virtual bool CountriesTypesFilterAvailable() const = 0;
259 virtual const char *CountryTypeFilterKey() const = 0;
260};
261
262class IServerBrowser : public IInterface
263{
264 MACRO_INTERFACE("serverbrowser")
265public:
266 /* Constants: Server Browser Sorting
267 SORT_NAME - Sort by name.
268 SORT_PING - Sort by ping.
269 SORT_MAP - Sort by map
270 SORT_GAMETYPE - Sort by game type. DM, TDM etc.
271 SORT_NUMPLAYERS - Sort after how many players there are on the server.
272 SORT_NUMFRIENDS - Sort after how many friends there are on the server.
273 */
274 enum
275 {
276 SORT_NAME = 0,
277 SORT_PING,
278 SORT_MAP,
279 SORT_GAMETYPE,
280 SORT_NUMPLAYERS,
281 SORT_NUMFRIENDS,
282
283 QUICK_SERVERNAME = 1,
284 QUICK_PLAYER = 2,
285 QUICK_MAPNAME = 4,
286
287 TYPE_INTERNET = 0,
288 TYPE_LAN,
289 TYPE_FAVORITES,
290 TYPE_FAVORITE_COMMUNITY_1,
291 TYPE_FAVORITE_COMMUNITY_2,
292 TYPE_FAVORITE_COMMUNITY_3,
293 TYPE_FAVORITE_COMMUNITY_4,
294 TYPE_FAVORITE_COMMUNITY_5,
295 NUM_TYPES,
296 };
297
298 static constexpr const char *COMMUNITY_DDNET = "ddnet";
299 static constexpr const char *COMMUNITY_NONE = "none";
300 /**
301 * Special community value for country/type filters that
302 * affect all communities.
303 */
304 static constexpr const char *COMMUNITY_ALL = "all";
305
306 static constexpr const char *SEARCH_EXCLUDE_TOKEN = ";";
307
308 virtual void Refresh(int Type, bool Force = false) = 0;
309 virtual bool IsGettingServerlist() const = 0;
310 virtual bool IsRefreshing() const = 0;
311 virtual int LoadingProgression() const = 0;
312
313 virtual int NumServers() const = 0;
314
315 virtual int Players(const CServerInfo &Item) const = 0;
316 virtual int Max(const CServerInfo &Item) const = 0;
317
318 virtual int NumSortedServers() const = 0;
319 virtual int NumSortedPlayers() const = 0;
320 virtual const CServerInfo *SortedGet(int Index) const = 0;
321
322 virtual const std::vector<CCommunity> &Communities() const = 0;
323 virtual const CCommunity *Community(const char *pCommunityId) const = 0;
324 virtual std::vector<const CCommunity *> SelectedCommunities() const = 0;
325 virtual std::vector<const CCommunity *> FavoriteCommunities() const = 0;
326 virtual std::vector<const CCommunity *> CurrentCommunities() const = 0;
327 virtual unsigned CurrentCommunitiesHash() const = 0;
328
329 virtual bool DDNetInfoAvailable() const = 0;
330 virtual SHA256_DIGEST DDNetInfoSha256() const = 0;
331
332 virtual ICommunityCache &CommunityCache() = 0;
333 virtual const ICommunityCache &CommunityCache() const = 0;
334 virtual IFilterList &FavoriteCommunitiesFilter() = 0;
335 virtual IFilterList &CommunitiesFilter() = 0;
336 virtual IFilterList &CountriesFilter() = 0;
337 virtual IFilterList &TypesFilter() = 0;
338 virtual const IFilterList &FavoriteCommunitiesFilter() const = 0;
339 virtual const IFilterList &CommunitiesFilter() const = 0;
340 virtual const IFilterList &CountriesFilter() const = 0;
341 virtual const IFilterList &TypesFilter() const = 0;
342 virtual void CleanFilters() = 0;
343
344 virtual int GetCurrentType() = 0;
345 virtual const char *GetTutorialServer() = 0;
346};
347
348#endif
349