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 int m_NumPlayers = 0;
222 bool m_HasFinishes = false;
223 std::unordered_set<CCommunityMap, CCommunityMap::SHash> m_FinishedMaps;
224
225public:
226 CCommunity(const char *pId, const char *pName, std::optional<SHA256_DIGEST> IconSha256, const char *pIconUrl) :
227 m_IconSha256(IconSha256)
228 {
229 str_copy(dst&: m_aId, src: pId);
230 str_copy(dst&: m_aName, src: pName);
231 str_copy(dst&: m_aIconUrl, src: pIconUrl);
232 }
233
234 const char *Id() const { return m_aId; }
235 const char *Name() const { return m_aName; }
236 const char *IconUrl() const { return m_aIconUrl; }
237 const std::optional<SHA256_DIGEST> &IconSha256() const { return m_IconSha256; }
238 const std::vector<CCommunityCountry> &Countries() const { return m_vCountries; }
239 const std::vector<CCommunityType> &Types() const { return m_vTypes; }
240 bool HasCountry(const char *pCountryName) const;
241 bool HasType(const char *pTypeName) const;
242 bool HasRanks() const { return m_HasFinishes; }
243 CServerInfo::ERankState HasRank(const char *pMap) const;
244 int NumPlayers() const { return m_NumPlayers; }
245};
246
247class IFilterList
248{
249public:
250 virtual ~IFilterList() = default;
251 virtual void Add(const char *pElement) = 0;
252 virtual void Remove(const char *pElement) = 0;
253 virtual void Clear() = 0;
254 virtual bool Empty() const = 0;
255 virtual bool Filtered(const char *pElement) const = 0;
256};
257
258class ICommunityCache
259{
260public:
261 virtual ~ICommunityCache() = default;
262 virtual void Update(bool Force) = 0;
263 virtual const std::vector<const CCommunity *> &SelectedCommunities() const = 0;
264 virtual const std::vector<const CCommunityCountry *> &SelectableCountries() const = 0;
265 virtual const std::vector<const CCommunityType *> &SelectableTypes() const = 0;
266 virtual bool AnyRanksAvailable() const = 0;
267 virtual bool CountriesTypesFilterAvailable() const = 0;
268 virtual const char *CountryTypeFilterKey() const = 0;
269};
270
271class IServerBrowser : public IInterface
272{
273 MACRO_INTERFACE("serverbrowser")
274public:
275 /* Constants: Server Browser Sorting
276 SORT_NAME - Sort by name.
277 SORT_PING - Sort by ping.
278 SORT_MAP - Sort by map
279 SORT_GAMETYPE - Sort by game type. DM, TDM etc.
280 SORT_NUMPLAYERS - Sort after how many players there are on the server.
281 SORT_NUMFRIENDS - Sort after how many friends there are on the server.
282 */
283 enum
284 {
285 SORT_NAME = 0,
286 SORT_PING,
287 SORT_MAP,
288 SORT_GAMETYPE,
289 SORT_NUMPLAYERS,
290 SORT_NUMFRIENDS,
291 };
292
293 enum
294 {
295 QUICK_SERVERNAME = 1,
296 QUICK_PLAYER = 2,
297 QUICK_MAPNAME = 4,
298 };
299
300 enum
301 {
302 TYPE_INTERNET = 0,
303 TYPE_LAN,
304 TYPE_FAVORITES,
305 TYPE_FAVORITE_COMMUNITY_1,
306 TYPE_FAVORITE_COMMUNITY_2,
307 TYPE_FAVORITE_COMMUNITY_3,
308 TYPE_FAVORITE_COMMUNITY_4,
309 TYPE_FAVORITE_COMMUNITY_5,
310 NUM_TYPES,
311 };
312
313 enum
314 {
315 LAN_PORT_BEGIN = 8303,
316 LAN_PORT_END = 8310,
317 };
318
319 class CServerEntry
320 {
321 public:
322 int64_t m_RequestTime;
323 bool m_RequestIgnoreInfo;
324 int m_GotInfo;
325 CServerInfo m_Info;
326
327 CServerEntry *m_pPrevReq; // request list
328 CServerEntry *m_pNextReq;
329 };
330
331 static constexpr const char *COMMUNITY_DDNET = "ddnet";
332 static constexpr const char *COMMUNITY_NONE = "none";
333
334 static constexpr const char *COMMUNITY_COUNTRY_NONE = "none";
335 static constexpr const char *COMMUNITY_TYPE_NONE = "None";
336 /**
337 * Special community value for country/type filters that
338 * affect all communities.
339 */
340 static constexpr const char *COMMUNITY_ALL = "all";
341
342 static constexpr const char *SEARCH_EXCLUDE_TOKEN = ";";
343
344 virtual void Refresh(int Type, bool Force = false) = 0;
345 virtual bool IsRefreshing() const = 0;
346 virtual bool IsGettingServerlist() const = 0;
347 virtual bool IsServerlistError() const = 0;
348 virtual int LoadingProgression() const = 0;
349
350 virtual int NumServers() const = 0;
351 virtual const CServerInfo *Get(int Index) const = 0;
352
353 virtual int Players(const CServerInfo &Item) const = 0;
354 virtual int Max(const CServerInfo &Item) const = 0;
355
356 virtual int NumSortedServers() const = 0;
357 virtual int NumSortedPlayers() const = 0;
358 virtual const CServerInfo *SortedGet(int Index) const = 0;
359
360 virtual const std::vector<CCommunity> &Communities() const = 0;
361 virtual const CCommunity *Community(const char *pCommunityId) const = 0;
362 virtual std::vector<const CCommunity *> SelectedCommunities() const = 0;
363 virtual std::vector<const CCommunity *> FavoriteCommunities() const = 0;
364 virtual std::vector<const CCommunity *> CurrentCommunities() const = 0;
365 virtual unsigned CurrentCommunitiesHash() const = 0;
366
367 virtual bool DDNetInfoAvailable() const = 0;
368 virtual std::optional<SHA256_DIGEST> DDNetInfoSha256() const = 0;
369
370 virtual ICommunityCache &CommunityCache() = 0;
371 virtual const ICommunityCache &CommunityCache() const = 0;
372 virtual IFilterList &FavoriteCommunitiesFilter() = 0;
373 virtual IFilterList &CommunitiesFilter() = 0;
374 virtual IFilterList &CountriesFilter() = 0;
375 virtual IFilterList &TypesFilter() = 0;
376 virtual const IFilterList &FavoriteCommunitiesFilter() const = 0;
377 virtual const IFilterList &CommunitiesFilter() const = 0;
378 virtual const IFilterList &CountriesFilter() const = 0;
379 virtual const IFilterList &TypesFilter() const = 0;
380 virtual void CleanFilters() = 0;
381
382 virtual CServerEntry *Find(const NETADDR &Addr) = 0;
383 virtual int GetCurrentType() = 0;
384 virtual const char *GetTutorialServer() = 0;
385};
386
387#endif
388