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