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