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