#pragma once namespace Components { class ServerList : public Component { public: typedef int(SortCallback)(const void*, const void*); struct ServerInfo { Network::Address addr; std::string hostname; std::string mapname; std::string gametype; std::string mod; std::string version; std::size_t hash; int clients; int bots; int maxClients; bool password; int ping; int matchType; int securityLevel; bool hardcore; bool svRunning; bool aimassist; bool voice; }; ServerList(); void preDestroy() override; static void Refresh([[maybe_unused]] const UIScript::Token& token, [[maybe_unused]] const Game::uiInfo_s* info); static void RefreshVisibleList([[maybe_unused]] const UIScript::Token& token, [[maybe_unused]] const Game::uiInfo_s* info); static void RefreshVisibleListInternal([[maybe_unused]] const UIScript::Token& token, [[maybe_unused]] const Game::uiInfo_s* info, bool refresh = false); static void UpdateVisibleList([[maybe_unused]] const UIScript::Token& token, [[maybe_unused]] const Game::uiInfo_s* info); static void InsertRequest(Network::Address address); static void Insert(const Network::Address& address, const Utils::InfoString& info); static ServerInfo* GetCurrentServer(); static bool IsFavouriteList(); static bool IsOfflineList(); static bool IsOnlineList(); static void Frame(); static std::vector* GetList(); static void UpdateVisibleInfo(); static bool GetMasterServer(const char* ip, int port, Game::netadr_t& address); static bool UseMasterServer; private: enum class Column : int { Password, Matchtype, AimAssist, VoiceChat, Hostname, Mapname, Players, Gametype, Mod, Ping, Count }; static constexpr auto* FavouriteFile = "players/favourites.json"; #pragma pack(push, 1) union MasterEntry { char token[7]; struct { uint32_t ip; uint16_t port; }; [[nodiscard]] bool IsEndToken() const noexcept { // End of transmission or file token return (token[0] == 'E' && token[1] == 'O' && (token[2] == 'T' || token[2] == 'F')); } [[nodiscard]] bool HasSeparator() const noexcept { return (token[6] == '\\'); } }; #pragma pack(pop) class Container { public: class ServerContainer { public: bool sent; int sendTime; std::string challenge; Network::Address target; }; bool awatingList; int awaitTime; int sentCount; int sendCount; Network::Address host; std::vector servers; std::recursive_mutex mutex; }; static unsigned int GetServerCount(); static const char* GetServerText(unsigned int index, int column); static const char* GetServerInfoText(ServerInfo* server, int column, bool sorting = false); static void SelectServer(unsigned int index); static void UpdateSource(); static void UpdateGameType(); static void SortList(); static void LoadFavourties(); static void StoreFavourite(const std::string& server); static void RemoveFavourite(const std::string& server); static ServerInfo* GetServer(unsigned int index); static bool CompareVersion(const std::string& version1, const std::string& version2); static bool IsServerDuplicate(const std::vector* list, const ServerInfo& server); static int SortKey; static bool SortAsc; static unsigned int CurrentServer; static Container RefreshContainer; static std::vector OnlineList; static std::vector OfflineList; static std::vector FavouriteList; static std::vector VisibleList; static Dvar::Var UIServerSelected; static Dvar::Var UIServerSelectedMap; static Dvar::Var NETServerQueryLimit; static Dvar::Var NETServerFrames; static bool IsServerListOpen(); }; } template <> struct std::hash { std::size_t operator()(const Components::ServerList::ServerInfo& x) const noexcept { std::size_t hash = 0; hash ^= std::hash()(x.hostname); hash ^= std::hash()(x.mapname); hash ^= std::hash()(x.mod); hash ^= std::hash()(*reinterpret_cast(&x.addr.getIP().bytes[0])); hash ^= x.clients; return hash; } };