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_CLIENT_SERVERBROWSER_H
4#define ENGINE_CLIENT_SERVERBROWSER_H
5
6#include <base/hash.h>
7#include <base/system.h>
8
9#include <engine/console.h>
10#include <engine/serverbrowser.h>
11#include <engine/shared/memheap.h>
12
13#include <functional>
14#include <map>
15#include <set>
16
17typedef struct _json_value json_value;
18class CNetClient;
19class IConfigManager;
20class IConsole;
21class IEngine;
22class IFavorites;
23class IFriends;
24class IServerBrowserHttp;
25class IServerBrowserPingCache;
26class IStorage;
27class IHttp;
28
29class CCommunityId
30{
31 char m_aId[CServerInfo::MAX_COMMUNITY_ID_LENGTH];
32
33public:
34 CCommunityId(const char *pCommunityId)
35 {
36 str_copy(dst&: m_aId, src: pCommunityId);
37 }
38
39 const char *Id() const { return m_aId; }
40
41 bool operator==(const CCommunityId &Other) const
42 {
43 return str_comp(a: Id(), b: Other.Id()) == 0;
44 }
45
46 bool operator<(const CCommunityId &Other) const
47 {
48 return str_comp(a: Id(), b: Other.Id()) < 0;
49 }
50};
51
52template<>
53struct std::hash<CCommunityId>
54{
55 size_t operator()(const CCommunityId &Elem) const noexcept
56 {
57 return str_quickhash(str: Elem.Id());
58 }
59};
60
61class CCommunityCountryName
62{
63 char m_aName[CServerInfo::MAX_COMMUNITY_COUNTRY_LENGTH];
64
65public:
66 CCommunityCountryName(const char *pCountryName)
67 {
68 str_copy(dst&: m_aName, src: pCountryName);
69 }
70
71 const char *Name() const { return m_aName; }
72
73 bool operator==(const CCommunityCountryName &Other) const
74 {
75 return str_comp(a: Name(), b: Other.Name()) == 0;
76 }
77
78 bool operator<(const CCommunityCountryName &Other) const
79 {
80 return str_comp(a: Name(), b: Other.Name()) < 0;
81 }
82};
83
84template<>
85struct std::hash<CCommunityCountryName>
86{
87 size_t operator()(const CCommunityCountryName &Elem) const noexcept
88 {
89 return str_quickhash(str: Elem.Name());
90 }
91};
92
93class CCommunityTypeName
94{
95 char m_aName[CServerInfo::MAX_COMMUNITY_TYPE_LENGTH];
96
97public:
98 CCommunityTypeName(const char *pTypeName)
99 {
100 str_copy(dst&: m_aName, src: pTypeName);
101 }
102
103 const char *Name() const { return m_aName; }
104
105 bool operator==(const CCommunityTypeName &Other) const
106 {
107 return str_comp(a: Name(), b: Other.Name()) == 0;
108 }
109
110 bool operator<(const CCommunityTypeName &Other) const
111 {
112 return str_comp(a: Name(), b: Other.Name()) < 0;
113 }
114};
115
116template<>
117struct std::hash<CCommunityTypeName>
118{
119 size_t operator()(const CCommunityTypeName &Elem) const noexcept
120 {
121 return str_quickhash(str: Elem.Name());
122 }
123};
124
125class CCommunityServer
126{
127 char m_aCommunityId[CServerInfo::MAX_COMMUNITY_ID_LENGTH];
128 char m_aCountryName[CServerInfo::MAX_COMMUNITY_COUNTRY_LENGTH];
129 char m_aTypeName[CServerInfo::MAX_COMMUNITY_TYPE_LENGTH];
130
131public:
132 CCommunityServer(const char *pCommunityId, const char *pCountryName, const char *pTypeName)
133 {
134 str_copy(dst&: m_aCommunityId, src: pCommunityId);
135 str_copy(dst&: m_aCountryName, src: pCountryName);
136 str_copy(dst&: m_aTypeName, src: pTypeName);
137 }
138
139 const char *CommunityId() const { return m_aCommunityId; }
140 const char *CountryName() const { return m_aCountryName; }
141 const char *TypeName() const { return m_aTypeName; }
142};
143
144class CFavoriteCommunityFilterList : public IFilterList
145{
146public:
147 void Add(const char *pCommunityId) override;
148 void Remove(const char *pCommunityId) override;
149 void Clear() override;
150 bool Filtered(const char *pCommunityId) const override;
151 bool Empty() const override;
152 void Clean(const std::vector<CCommunity> &vAllowedCommunities);
153 void Save(IConfigManager *pConfigManager) const;
154 const std::vector<CCommunityId> &Entries() const;
155
156private:
157 std::vector<CCommunityId> m_vEntries;
158};
159
160class CExcludedCommunityFilterList : public IFilterList
161{
162public:
163 void Add(const char *pCommunityId) override;
164 void Remove(const char *pCommunityId) override;
165 void Clear() override;
166 bool Filtered(const char *pCommunityId) const override;
167 bool Empty() const override;
168 void Clean(const std::vector<CCommunity> &vAllowedCommunities);
169 void Save(IConfigManager *pConfigManager) const;
170
171private:
172 std::set<CCommunityId> m_Entries;
173};
174
175class CExcludedCommunityCountryFilterList : public IFilterList
176{
177public:
178 CExcludedCommunityCountryFilterList(const ICommunityCache *pCommunityCache) :
179 m_pCommunityCache(pCommunityCache)
180 {
181 }
182
183 void Add(const char *pCountryName) override;
184 void Add(const char *pCommunityId, const char *pCountryName);
185 void Remove(const char *pCountryName) override;
186 void Remove(const char *pCommunityId, const char *pCountryName);
187 void Clear() override;
188 bool Filtered(const char *pCountryName) const override;
189 bool Empty() const override;
190 void Clean(const std::vector<CCommunity> &vAllowedCommunities);
191 void Save(IConfigManager *pConfigManager) const;
192
193private:
194 const ICommunityCache *m_pCommunityCache;
195 std::map<CCommunityId, std::set<CCommunityCountryName>> m_Entries;
196};
197
198class CExcludedCommunityTypeFilterList : public IFilterList
199{
200public:
201 CExcludedCommunityTypeFilterList(const ICommunityCache *pCommunityCache) :
202 m_pCommunityCache(pCommunityCache)
203 {
204 }
205
206 void Add(const char *pTypeName) override;
207 void Add(const char *pCommunityId, const char *pTypeName);
208 void Remove(const char *pTypeName) override;
209 void Remove(const char *pCommunityId, const char *pTypeName);
210 void Clear() override;
211 bool Filtered(const char *pTypeName) const override;
212 bool Empty() const override;
213 void Clean(const std::vector<CCommunity> &vAllowedCommunities);
214 void Save(IConfigManager *pConfigManager) const;
215
216private:
217 const ICommunityCache *m_pCommunityCache;
218 std::map<CCommunityId, std::set<CCommunityTypeName>> m_Entries;
219};
220
221class CCommunityCache : public ICommunityCache
222{
223 IServerBrowser *m_pServerBrowser;
224 SHA256_DIGEST m_InfoSha256 = SHA256_ZEROED;
225 int m_LastType = IServerBrowser::NUM_TYPES; // initial value does not appear normally, marking uninitialized cache
226 unsigned m_SelectedCommunitiesHash = 0;
227 std::vector<const CCommunity *> m_vpSelectedCommunities;
228 std::vector<const CCommunityCountry *> m_vpSelectableCountries;
229 std::vector<const CCommunityType *> m_vpSelectableTypes;
230 bool m_AnyRanksAvailable = false;
231 bool m_CountryTypesFilterAvailable = false;
232 const char *m_pCountryTypeFilterKey = IServerBrowser::COMMUNITY_ALL;
233
234public:
235 CCommunityCache(IServerBrowser *pServerBrowser) :
236 m_pServerBrowser(pServerBrowser)
237 {
238 }
239
240 void Update(bool Force) override;
241 const std::vector<const CCommunity *> &SelectedCommunities() const override { return m_vpSelectedCommunities; }
242 const std::vector<const CCommunityCountry *> &SelectableCountries() const override { return m_vpSelectableCountries; }
243 const std::vector<const CCommunityType *> &SelectableTypes() const override { return m_vpSelectableTypes; }
244 bool AnyRanksAvailable() const override { return m_AnyRanksAvailable; }
245 bool CountriesTypesFilterAvailable() const override { return m_CountryTypesFilterAvailable; }
246 const char *CountryTypeFilterKey() const override { return m_pCountryTypeFilterKey; }
247};
248
249class CServerBrowser : public IServerBrowser
250{
251public:
252 CServerBrowser();
253 ~CServerBrowser() override;
254
255 // interface functions
256 void Refresh(int Type, bool Force = false) override;
257 bool IsRefreshing() const override;
258 bool IsGettingServerlist() const override;
259 bool IsServerlistError() const override;
260 int LoadingProgression() const override;
261 void RequestResort() { m_NeedResort = true; }
262
263 int NumServers() const override { return m_NumServers; }
264 int Players(const CServerInfo &Item) const override;
265 int Max(const CServerInfo &Item) const override;
266 int NumSortedServers() const override { return m_NumSortedServers; }
267 int NumSortedPlayers() const override { return m_NumSortedPlayers; }
268 const CServerInfo *SortedGet(int Index) const override;
269
270 const json_value *LoadDDNetInfo();
271 void LoadDDNetInfoJson();
272 void LoadDDNetLocation();
273 void LoadDDNetServers();
274 void UpdateServerFilteredPlayers(CServerInfo *pInfo) const;
275 void UpdateServerFriends(CServerInfo *pInfo) const;
276 void UpdateServerCommunity(CServerInfo *pInfo) const;
277 void UpdateServerRank(CServerInfo *pInfo) const;
278 void ValidateServerlistType();
279 const char *GetTutorialServer() override;
280
281 const std::vector<CCommunity> &Communities() const override;
282 const CCommunity *Community(const char *pCommunityId) const override;
283 std::vector<const CCommunity *> SelectedCommunities() const override;
284 std::vector<const CCommunity *> FavoriteCommunities() const override;
285 std::vector<const CCommunity *> CurrentCommunities() const override;
286 unsigned CurrentCommunitiesHash() const override;
287
288 bool DDNetInfoAvailable() const override { return m_pDDNetInfo != nullptr; }
289 SHA256_DIGEST DDNetInfoSha256() const override { return m_DDNetInfoSha256; }
290
291 ICommunityCache &CommunityCache() override { return m_CommunityCache; }
292 const ICommunityCache &CommunityCache() const override { return m_CommunityCache; }
293 CFavoriteCommunityFilterList &FavoriteCommunitiesFilter() override { return m_FavoriteCommunitiesFilter; }
294 CExcludedCommunityFilterList &CommunitiesFilter() override { return m_CommunitiesFilter; }
295 CExcludedCommunityCountryFilterList &CountriesFilter() override { return m_CountriesFilter; }
296 CExcludedCommunityTypeFilterList &TypesFilter() override { return m_TypesFilter; }
297 const CFavoriteCommunityFilterList &FavoriteCommunitiesFilter() const override { return m_FavoriteCommunitiesFilter; }
298 const CExcludedCommunityFilterList &CommunitiesFilter() const override { return m_CommunitiesFilter; }
299 const CExcludedCommunityCountryFilterList &CountriesFilter() const override { return m_CountriesFilter; }
300 const CExcludedCommunityTypeFilterList &TypesFilter() const override { return m_TypesFilter; }
301 void CleanFilters() override;
302
303 //
304 void Update();
305 void OnServerInfoUpdate(const NETADDR &Addr, int Token, const CServerInfo *pInfo);
306 void SetHttpInfo(const CServerInfo *pInfo);
307 void RequestCurrentServer(const NETADDR &Addr) const;
308 void RequestCurrentServerWithRandomToken(const NETADDR &Addr, int *pBasicToken, int *pToken) const;
309 void SetCurrentServerPing(const NETADDR &Addr, int Ping);
310
311 void SetBaseInfo(class CNetClient *pClient, const char *pNetVersion);
312 void OnInit();
313
314 void QueueRequest(CServerEntry *pEntry);
315 CServerEntry *Find(const NETADDR &Addr) override;
316 int GetCurrentType() override { return m_ServerlistType; }
317 bool IsRegistered(const NETADDR &Addr);
318
319private:
320 CNetClient *m_pNetClient = nullptr;
321 IConfigManager *m_pConfigManager = nullptr;
322 IConsole *m_pConsole = nullptr;
323 IEngine *m_pEngine = nullptr;
324 IFriends *m_pFriends = nullptr;
325 IFavorites *m_pFavorites = nullptr;
326 IStorage *m_pStorage = nullptr;
327 IHttp *m_pHttpClient = nullptr;
328 char m_aNetVersion[128];
329
330 bool m_RefreshingHttp = false;
331 IServerBrowserHttp *m_pHttp = nullptr;
332 IServerBrowserPingCache *m_pPingCache = nullptr;
333 const char *m_pHttpPrevBestUrl = nullptr;
334
335 CHeap m_ServerlistHeap;
336 CServerEntry **m_ppServerlist;
337 int *m_pSortedServerlist;
338 std::unordered_map<NETADDR, int> m_ByAddr;
339
340 std::vector<CCommunity> m_vCommunities;
341 std::unordered_map<NETADDR, CCommunityServer> m_CommunityServersByAddr;
342
343 int m_OwnLocation = CServerInfo::LOC_UNKNOWN;
344
345 CCommunityCache m_CommunityCache;
346 CFavoriteCommunityFilterList m_FavoriteCommunitiesFilter;
347 CExcludedCommunityFilterList m_CommunitiesFilter;
348 CExcludedCommunityCountryFilterList m_CountriesFilter;
349 CExcludedCommunityTypeFilterList m_TypesFilter;
350
351 json_value *m_pDDNetInfo = nullptr;
352 SHA256_DIGEST m_DDNetInfoSha256 = SHA256_ZEROED;
353
354 CServerEntry *m_pFirstReqServer; // request list
355 CServerEntry *m_pLastReqServer;
356 int m_NumRequests;
357
358 bool m_NeedResort;
359 int m_Sorthash;
360
361 // used instead of g_Config.br_max_requests to get more servers
362 int m_CurrentMaxRequests;
363
364 int m_NumSortedServers;
365 int m_NumSortedServersCapacity;
366 int m_NumSortedPlayers;
367 int m_NumServers;
368 int m_NumServerCapacity;
369
370 int m_ServerlistType;
371 int64_t m_BroadcastTime;
372 unsigned char m_aTokenSeed[16];
373
374 int GenerateToken(const NETADDR &Addr) const;
375 static int GetBasicToken(int Token);
376 static int GetExtraToken(int Token);
377
378 // sorting criteria
379 bool SortCompareName(int Index1, int Index2) const;
380 bool SortCompareMap(int Index1, int Index2) const;
381 bool SortComparePing(int Index1, int Index2) const;
382 bool SortCompareGametype(int Index1, int Index2) const;
383 bool SortCompareNumPlayers(int Index1, int Index2) const;
384 bool SortCompareNumClients(int Index1, int Index2) const;
385 bool SortCompareNumFriends(int Index1, int Index2) const;
386 bool SortCompareNumPlayersAndPing(int Index1, int Index2) const;
387
388 //
389 void Filter();
390 void Sort();
391 int SortHash() const;
392
393 void CleanUp();
394
395 void UpdateFromHttp();
396 CServerEntry *Add(const NETADDR *pAddrs, int NumAddrs);
397 CServerEntry *ReplaceEntry(CServerEntry *pEntry, const NETADDR *pAddrs, int NumAddrs);
398
399 void RemoveRequest(CServerEntry *pEntry);
400
401 void RequestImpl(const NETADDR &Addr, CServerEntry *pEntry, int *pBasicToken, int *pToken, bool RandomToken) const;
402
403 void RegisterCommands();
404 static void ConfigSaveCallback(IConfigManager *pConfigManager, void *pUserData);
405 static void Con_AddFavoriteCommunity(IConsole::IResult *pResult, void *pUserData);
406 static void Con_RemoveFavoriteCommunity(IConsole::IResult *pResult, void *pUserData);
407 static void Con_AddExcludedCommunity(IConsole::IResult *pResult, void *pUserData);
408 static void Con_RemoveExcludedCommunity(IConsole::IResult *pResult, void *pUserData);
409 static void Con_AddExcludedCountry(IConsole::IResult *pResult, void *pUserData);
410 static void Con_RemoveExcludedCountry(IConsole::IResult *pResult, void *pUserData);
411 static void Con_AddExcludedType(IConsole::IResult *pResult, void *pUserData);
412 static void Con_RemoveExcludedType(IConsole::IResult *pResult, void *pUserData);
413 static void Con_LeakIpAddress(IConsole::IResult *pResult, void *pUserData);
414
415 bool ValidateCommunityId(const char *pCommunityId) const;
416 bool ValidateCountryName(const char *pCountryName) const;
417 bool ValidateTypeName(const char *pTypeName) const;
418
419 void SetInfo(CServerEntry *pEntry, const CServerInfo &Info) const;
420 void SetLatency(NETADDR Addr, int Latency);
421
422 static bool ParseCommunityFinishes(CCommunity *pCommunity, const json_value &Finishes);
423 static bool ParseCommunityServers(CCommunity *pCommunity, const json_value &Servers);
424};
425
426#endif
427