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