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