mirror of
https://github.com/ddnet/ddnet.git
synced 2024-09-19 17:14:18 +00:00
Merge pull request #7915 from Robyt3/Browser-Community-Tabs
Add tabs for favorite communities, separate country/type filters
This commit is contained in:
commit
f0da0aa4a0
|
@ -1117,6 +1117,7 @@ set(EXPECTED_DATA
|
|||
autoexec_server.cfg
|
||||
blob.png
|
||||
censorlist.txt
|
||||
communityicons/none.png
|
||||
console.png
|
||||
console_bar.png
|
||||
countryflags/AD.png
|
||||
|
|
BIN
data/communityicons/none.png
Normal file
BIN
data/communityicons/none.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.4 KiB |
|
@ -280,7 +280,6 @@ public:
|
|||
virtual SWarning *GetCurWarning() = 0;
|
||||
|
||||
virtual CChecksumData *ChecksumData() = 0;
|
||||
virtual bool InfoTaskRunning() = 0;
|
||||
virtual int UdpConnectivity(int NetType) = 0;
|
||||
|
||||
#if defined(CONF_FAMILY_WINDOWS)
|
||||
|
|
|
@ -499,7 +499,6 @@ public:
|
|||
SWarning *GetCurWarning() override;
|
||||
|
||||
CChecksumData *ChecksumData() override { return &m_Checksum.m_Data; }
|
||||
bool InfoTaskRunning() override { return m_pDDNetInfoTask != nullptr; }
|
||||
int UdpConnectivity(int NetType) override;
|
||||
|
||||
#if defined(CONF_FAMILY_WINDOWS)
|
||||
|
|
|
@ -584,7 +584,13 @@ bool CGraphics_Threaded::LoadPNG(CImageInfo *pImg, const char *pFilename, int St
|
|||
if(File)
|
||||
{
|
||||
io_seek(File, 0, IOSEEK_END);
|
||||
unsigned int FileSize = io_tell(File);
|
||||
long int FileSize = io_tell(File);
|
||||
if(FileSize <= 0)
|
||||
{
|
||||
io_close(File);
|
||||
log_error("game/png", "failed to get file size (%ld). filename='%s'", FileSize, pFilename);
|
||||
return false;
|
||||
}
|
||||
io_seek(File, 0, IOSEEK_START);
|
||||
|
||||
TImageByteBuffer ByteBuffer;
|
||||
|
|
|
@ -50,9 +50,8 @@ bool matchesExactly(const char *a, const char *b)
|
|||
}
|
||||
|
||||
CServerBrowser::CServerBrowser() :
|
||||
m_CommunitiesFilter(g_Config.m_BrFilterExcludeCommunities, sizeof(g_Config.m_BrFilterExcludeCommunities)),
|
||||
m_CountriesFilter(g_Config.m_BrFilterExcludeCountries, sizeof(g_Config.m_BrFilterExcludeCountries)),
|
||||
m_TypesFilter(g_Config.m_BrFilterExcludeTypes, sizeof(g_Config.m_BrFilterExcludeTypes))
|
||||
m_CountriesFilter([this]() { return CurrentCommunities(); }),
|
||||
m_TypesFilter([this]() { return CurrentCommunities(); })
|
||||
{
|
||||
m_ppServerlist = nullptr;
|
||||
m_pSortedServerlist = nullptr;
|
||||
|
@ -108,12 +107,106 @@ void CServerBrowser::OnInit()
|
|||
|
||||
void CServerBrowser::RegisterCommands()
|
||||
{
|
||||
m_pConfigManager->RegisterCallback(CServerBrowser::ConfigSaveCallback, this);
|
||||
m_pConsole->Register("add_favorite_community", "s[community_id]", CFGFLAG_CLIENT, Con_AddFavoriteCommunity, this, "Add a community as a favorite");
|
||||
m_pConsole->Register("remove_favorite_community", "s[community_id]", CFGFLAG_CLIENT, Con_RemoveFavoriteCommunity, this, "Remove a community from the favorites");
|
||||
m_pConsole->Register("add_excluded_community", "s[community_id]", CFGFLAG_CLIENT, Con_AddExcludedCommunity, this, "Add a community to the exclusion filter");
|
||||
m_pConsole->Register("remove_excluded_community", "s[community_id]", CFGFLAG_CLIENT, Con_RemoveExcludedCommunity, this, "Remove a community from the exclusion filter");
|
||||
m_pConsole->Register("add_excluded_country", "s[community_id] s[country_code]", CFGFLAG_CLIENT, Con_AddExcludedCountry, this, "Add a country to the exclusion filter for a specific community");
|
||||
m_pConsole->Register("remove_excluded_country", "s[community_id] s[country_code]", CFGFLAG_CLIENT, Con_RemoveExcludedCountry, this, "Remove a country from the exclusion filter for a specific community");
|
||||
m_pConsole->Register("add_excluded_type", "s[community_id] s[type]", CFGFLAG_CLIENT, Con_AddExcludedType, this, "Add a type to the exclusion filter for a specific community");
|
||||
m_pConsole->Register("remove_excluded_type", "s[community_id] s[type]", CFGFLAG_CLIENT, Con_RemoveExcludedType, this, "Remove a type from the exclusion filter for a specific community");
|
||||
m_pConsole->Register("leak_ip_address_to_all_servers", "", CFGFLAG_CLIENT, Con_LeakIpAddress, this, "Leaks your IP address to all servers by pinging each of them, also acquiring the latency in the process");
|
||||
}
|
||||
|
||||
void CServerBrowser::ConfigSaveCallback(IConfigManager *pConfigManager, void *pUserData)
|
||||
{
|
||||
CServerBrowser *pThis = static_cast<CServerBrowser *>(pUserData);
|
||||
pThis->FavoriteCommunitiesFilter().Save(pConfigManager);
|
||||
pThis->CommunitiesFilter().Save(pConfigManager);
|
||||
pThis->CountriesFilter().Save(pConfigManager);
|
||||
pThis->TypesFilter().Save(pConfigManager);
|
||||
}
|
||||
|
||||
void CServerBrowser::Con_AddFavoriteCommunity(IConsole::IResult *pResult, void *pUserData)
|
||||
{
|
||||
CServerBrowser *pThis = static_cast<CServerBrowser *>(pUserData);
|
||||
const char *pCommunityId = pResult->GetString(0);
|
||||
if(!pThis->ValidateCommunityId(pCommunityId))
|
||||
return;
|
||||
pThis->FavoriteCommunitiesFilter().Add(pCommunityId);
|
||||
}
|
||||
|
||||
void CServerBrowser::Con_RemoveFavoriteCommunity(IConsole::IResult *pResult, void *pUserData)
|
||||
{
|
||||
CServerBrowser *pThis = static_cast<CServerBrowser *>(pUserData);
|
||||
const char *pCommunityId = pResult->GetString(0);
|
||||
if(!pThis->ValidateCommunityId(pCommunityId))
|
||||
return;
|
||||
pThis->FavoriteCommunitiesFilter().Remove(pCommunityId);
|
||||
}
|
||||
|
||||
void CServerBrowser::Con_AddExcludedCommunity(IConsole::IResult *pResult, void *pUserData)
|
||||
{
|
||||
CServerBrowser *pThis = static_cast<CServerBrowser *>(pUserData);
|
||||
const char *pCommunityId = pResult->GetString(0);
|
||||
if(!pThis->ValidateCommunityId(pCommunityId))
|
||||
return;
|
||||
pThis->CommunitiesFilter().Add(pCommunityId);
|
||||
}
|
||||
|
||||
void CServerBrowser::Con_RemoveExcludedCommunity(IConsole::IResult *pResult, void *pUserData)
|
||||
{
|
||||
CServerBrowser *pThis = static_cast<CServerBrowser *>(pUserData);
|
||||
const char *pCommunityId = pResult->GetString(0);
|
||||
if(!pThis->ValidateCommunityId(pCommunityId))
|
||||
return;
|
||||
pThis->CommunitiesFilter().Remove(pCommunityId);
|
||||
}
|
||||
|
||||
void CServerBrowser::Con_AddExcludedCountry(IConsole::IResult *pResult, void *pUserData)
|
||||
{
|
||||
CServerBrowser *pThis = static_cast<CServerBrowser *>(pUserData);
|
||||
const char *pCommunityId = pResult->GetString(0);
|
||||
const char *pCountryName = pResult->GetString(1);
|
||||
if(!pThis->ValidateCommunityId(pCommunityId) || !pThis->ValidateCountryName(pCountryName))
|
||||
return;
|
||||
pThis->CountriesFilter().Add(pCommunityId, pCountryName);
|
||||
}
|
||||
|
||||
void CServerBrowser::Con_RemoveExcludedCountry(IConsole::IResult *pResult, void *pUserData)
|
||||
{
|
||||
CServerBrowser *pThis = static_cast<CServerBrowser *>(pUserData);
|
||||
const char *pCommunityId = pResult->GetString(0);
|
||||
const char *pCountryName = pResult->GetString(1);
|
||||
if(!pThis->ValidateCommunityId(pCommunityId) || !pThis->ValidateCountryName(pCountryName))
|
||||
return;
|
||||
pThis->CountriesFilter().Remove(pCommunityId, pCountryName);
|
||||
}
|
||||
|
||||
void CServerBrowser::Con_AddExcludedType(IConsole::IResult *pResult, void *pUserData)
|
||||
{
|
||||
CServerBrowser *pThis = static_cast<CServerBrowser *>(pUserData);
|
||||
const char *pCommunityId = pResult->GetString(0);
|
||||
const char *pTypeName = pResult->GetString(1);
|
||||
if(!pThis->ValidateCommunityId(pCommunityId) || !pThis->ValidateTypeName(pTypeName))
|
||||
return;
|
||||
pThis->TypesFilter().Add(pCommunityId, pTypeName);
|
||||
}
|
||||
|
||||
void CServerBrowser::Con_RemoveExcludedType(IConsole::IResult *pResult, void *pUserData)
|
||||
{
|
||||
CServerBrowser *pThis = static_cast<CServerBrowser *>(pUserData);
|
||||
const char *pCommunityId = pResult->GetString(0);
|
||||
const char *pTypeName = pResult->GetString(1);
|
||||
if(!pThis->ValidateCommunityId(pCommunityId) || !pThis->ValidateTypeName(pTypeName))
|
||||
return;
|
||||
pThis->TypesFilter().Remove(pCommunityId, pTypeName);
|
||||
}
|
||||
|
||||
void CServerBrowser::Con_LeakIpAddress(IConsole::IResult *pResult, void *pUserData)
|
||||
{
|
||||
CServerBrowser *pThis = (CServerBrowser *)pUserData;
|
||||
CServerBrowser *pThis = static_cast<CServerBrowser *>(pUserData);
|
||||
|
||||
// We only consider the first address of every server.
|
||||
|
||||
|
@ -170,6 +263,50 @@ void CServerBrowser::Con_LeakIpAddress(IConsole::IResult *pResult, void *pUserDa
|
|||
}
|
||||
}
|
||||
|
||||
static bool ValidIdentifier(const char *pId, size_t MaxLength)
|
||||
{
|
||||
if(pId[0] == '\0' || (size_t)str_length(pId) >= MaxLength)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for(int i = 0; pId[i] != '\0'; ++i)
|
||||
{
|
||||
if(pId[i] == '"' || pId[i] == '/' || pId[i] == '\\')
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool ValidateIdentifier(const char *pId, size_t MaxLength, const char *pContext, IConsole *pConsole)
|
||||
{
|
||||
if(!ValidIdentifier(pId, MaxLength))
|
||||
{
|
||||
char aError[32 + IConsole::CMDLINE_LENGTH];
|
||||
str_format(aError, sizeof(aError), "%s '%s' is not valid", pContext, pId);
|
||||
pConsole->Print(IConsole::OUTPUT_LEVEL_STANDARD, "serverbrowser", aError);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CServerBrowser::ValidateCommunityId(const char *pCommunityId) const
|
||||
{
|
||||
return ValidateIdentifier(pCommunityId, CServerInfo::MAX_COMMUNITY_ID_LENGTH, "Community ID", m_pConsole);
|
||||
}
|
||||
|
||||
bool CServerBrowser::ValidateCountryName(const char *pCountryName) const
|
||||
{
|
||||
return ValidateIdentifier(pCountryName, CServerInfo::MAX_COMMUNITY_COUNTRY_LENGTH, "Country name", m_pConsole);
|
||||
}
|
||||
|
||||
bool CServerBrowser::ValidateTypeName(const char *pTypeName) const
|
||||
{
|
||||
return ValidateIdentifier(pTypeName, CServerInfo::MAX_COMMUNITY_TYPE_LENGTH, "Type name", m_pConsole);
|
||||
}
|
||||
|
||||
int CServerBrowser::Players(const CServerInfo &Item) const
|
||||
{
|
||||
return g_Config.m_BrFilterSpectators ? Item.m_NumPlayers : Item.m_NumClients;
|
||||
|
@ -298,13 +435,20 @@ void CServerBrowser::Filter()
|
|||
else if(g_Config.m_BrFilterUnfinishedMap && Info.m_HasRank == CServerInfo::RANK_RANKED)
|
||||
Filtered = true;
|
||||
else
|
||||
{
|
||||
if(!Communities().empty())
|
||||
{
|
||||
if(m_ServerlistType == IServerBrowser::TYPE_INTERNET || m_ServerlistType == IServerBrowser::TYPE_FAVORITES)
|
||||
{
|
||||
Filtered = CommunitiesFilter().Filtered(Info.m_aCommunityId);
|
||||
}
|
||||
if(m_ServerlistType == IServerBrowser::TYPE_INTERNET || m_ServerlistType == IServerBrowser::TYPE_FAVORITES ||
|
||||
(m_ServerlistType >= IServerBrowser::TYPE_FAVORITE_COMMUNITY_1 && m_ServerlistType <= IServerBrowser::TYPE_FAVORITE_COMMUNITY_3))
|
||||
{
|
||||
Filtered = Filtered || CountriesFilter().Filtered(Info.m_aCommunityCountry);
|
||||
Filtered = Filtered || TypesFilter().Filtered(Info.m_aCommunityType);
|
||||
}
|
||||
}
|
||||
|
||||
if(!Filtered && g_Config.m_BrFilterCountry)
|
||||
{
|
||||
|
@ -771,9 +915,9 @@ void CServerBrowser::OnServerInfoUpdate(const NETADDR &Addr, int Token, const CS
|
|||
RequestResort();
|
||||
}
|
||||
|
||||
void CServerBrowser::Refresh(int Type)
|
||||
void CServerBrowser::Refresh(int Type, bool Force)
|
||||
{
|
||||
bool ServerListTypeChanged = m_ServerlistType != Type;
|
||||
bool ServerListTypeChanged = Force || m_ServerlistType != Type;
|
||||
int OldServerListType = m_ServerlistType;
|
||||
m_ServerlistType = Type;
|
||||
secure_random_fill(m_aTokenSeed, sizeof(m_aTokenSeed));
|
||||
|
@ -813,7 +957,7 @@ void CServerBrowser::Refresh(int Type)
|
|||
if(g_Config.m_Debug)
|
||||
m_pConsole->Print(IConsole::OUTPUT_LEVEL_DEBUG, "serverbrowser", "broadcasting for servers");
|
||||
}
|
||||
else if(Type == IServerBrowser::TYPE_FAVORITES || Type == IServerBrowser::TYPE_INTERNET)
|
||||
else
|
||||
{
|
||||
m_pHttp->Refresh();
|
||||
m_pPingCache->Load();
|
||||
|
@ -915,7 +1059,37 @@ void CServerBrowser::UpdateFromHttp()
|
|||
std::function<bool(const NETADDR *, int)> Want = [](const NETADDR *pAddrs, int NumAddrs) { return true; };
|
||||
if(m_ServerlistType == IServerBrowser::TYPE_FAVORITES)
|
||||
{
|
||||
Want = [&](const NETADDR *pAddrs, int NumAddrs) -> bool { return m_pFavorites->IsFavorite(pAddrs, NumAddrs) != TRISTATE::NONE; };
|
||||
Want = [this](const NETADDR *pAddrs, int NumAddrs) -> bool {
|
||||
return m_pFavorites->IsFavorite(pAddrs, NumAddrs) != TRISTATE::NONE;
|
||||
};
|
||||
}
|
||||
else if(m_ServerlistType >= IServerBrowser::TYPE_FAVORITE_COMMUNITY_1 && m_ServerlistType <= IServerBrowser::TYPE_FAVORITE_COMMUNITY_3)
|
||||
{
|
||||
const size_t CommunityIndex = m_ServerlistType - IServerBrowser::TYPE_FAVORITE_COMMUNITY_1;
|
||||
std::vector<const CCommunity *> vpFavoriteCommunities = FavoriteCommunities();
|
||||
dbg_assert(CommunityIndex < vpFavoriteCommunities.size(), "Invalid community index");
|
||||
const CCommunity *pWantedCommunity = vpFavoriteCommunities[CommunityIndex];
|
||||
const bool IsNoneCommunity = str_comp(pWantedCommunity->Id(), COMMUNITY_NONE) == 0;
|
||||
Want = [this, pWantedCommunity, IsNoneCommunity](const NETADDR *pAddrs, int NumAddrs) -> bool {
|
||||
for(int AddressIndex = 0; AddressIndex < NumAddrs; AddressIndex++)
|
||||
{
|
||||
const auto CommunityServer = m_CommunityServersByAddr.find(pAddrs[AddressIndex]);
|
||||
if(CommunityServer != m_CommunityServersByAddr.end())
|
||||
{
|
||||
if(IsNoneCommunity)
|
||||
{
|
||||
// Servers with community "none" are not present in m_CommunityServersByAddr, so we ignore
|
||||
// any server that is found in this map to determine only the servers without community.
|
||||
return false;
|
||||
}
|
||||
else if(str_comp(CommunityServer->second.CommunityId(), pWantedCommunity->Id()) == 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return IsNoneCommunity;
|
||||
};
|
||||
}
|
||||
|
||||
for(int i = 0; i < NumServers; i++)
|
||||
|
@ -1233,14 +1407,12 @@ void CServerBrowser::LoadDDNetServers()
|
|||
|
||||
if(!m_pDDNetInfo)
|
||||
{
|
||||
CleanFilters();
|
||||
return;
|
||||
}
|
||||
|
||||
const json_value &Communities = (*m_pDDNetInfo)["communities"];
|
||||
if(Communities.type != json_array)
|
||||
{
|
||||
CleanFilters();
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1382,11 +1554,6 @@ void CServerBrowser::UpdateServerRank(CServerInfo *pInfo) const
|
|||
|
||||
const char *CServerBrowser::GetTutorialServer()
|
||||
{
|
||||
// Use internet tab as default after joining tutorial, also makes sure Find() actually works.
|
||||
// Note that when no server info has been loaded yet, this will not return a result immediately.
|
||||
m_pConfigManager->Reset("ui_page");
|
||||
Refresh(IServerBrowser::TYPE_INTERNET);
|
||||
|
||||
const CCommunity *pCommunity = Community(COMMUNITY_DDNET);
|
||||
if(pCommunity == nullptr)
|
||||
return nullptr;
|
||||
|
@ -1397,10 +1564,10 @@ const char *CServerBrowser::GetTutorialServer()
|
|||
{
|
||||
for(const auto &Server : Country.Servers())
|
||||
{
|
||||
CServerEntry *pEntry = Find(Server.Address());
|
||||
if(!pEntry)
|
||||
if(str_comp(Server.TypeName(), "Tutorial") != 0)
|
||||
continue;
|
||||
if(str_find(pEntry->m_Info.m_aName, "(Tutorial)") == 0)
|
||||
const CServerEntry *pEntry = Find(Server.Address());
|
||||
if(!pEntry)
|
||||
continue;
|
||||
if(pEntry->m_Info.m_NumPlayers > pEntry->m_Info.m_MaxPlayers - 10)
|
||||
continue;
|
||||
|
@ -1433,6 +1600,20 @@ int CServerBrowser::LoadingProgression() const
|
|||
return 100.0f * Loaded / Servers;
|
||||
}
|
||||
|
||||
bool CCommunity::HasCountry(const char *pCountryName) const
|
||||
{
|
||||
return std::find_if(Countries().begin(), Countries().end(), [pCountryName](const auto &Elem) {
|
||||
return str_comp(Elem.Name(), pCountryName) == 0;
|
||||
}) != Countries().end();
|
||||
}
|
||||
|
||||
bool CCommunity::HasType(const char *pTypeName) const
|
||||
{
|
||||
return std::find_if(Types().begin(), Types().end(), [pTypeName](const auto &Elem) {
|
||||
return str_comp(Elem.Name(), pTypeName) == 0;
|
||||
}) != Types().end();
|
||||
}
|
||||
|
||||
CServerInfo::ERankState CCommunity::HasRank(const char *pMap) const
|
||||
{
|
||||
if(!HasRanks())
|
||||
|
@ -1467,121 +1648,474 @@ std::vector<const CCommunity *> CServerBrowser::SelectedCommunities() const
|
|||
return vpSelected;
|
||||
}
|
||||
|
||||
void CFilterList::Add(const char *pElement)
|
||||
std::vector<const CCommunity *> CServerBrowser::FavoriteCommunities() const
|
||||
{
|
||||
if(Filtered(pElement))
|
||||
return;
|
||||
|
||||
if(m_pFilter[0] != '\0')
|
||||
str_append(m_pFilter, ",", m_FilterSize);
|
||||
str_append(m_pFilter, pElement, m_FilterSize);
|
||||
// This is done differently than SelectedCommunities because the favorite
|
||||
// communities should be returned in the order specified by the user.
|
||||
std::vector<const CCommunity *> vpFavorites;
|
||||
for(const auto &CommunityId : FavoriteCommunitiesFilter().Entries())
|
||||
{
|
||||
const CCommunity *pCommunity = Community(CommunityId.Id());
|
||||
if(pCommunity)
|
||||
{
|
||||
vpFavorites.push_back(pCommunity);
|
||||
}
|
||||
}
|
||||
return vpFavorites;
|
||||
}
|
||||
|
||||
void CFilterList::Remove(const char *pElement)
|
||||
std::vector<const CCommunity *> CServerBrowser::CurrentCommunities() const
|
||||
{
|
||||
if(!Filtered(pElement))
|
||||
return;
|
||||
|
||||
// rewrite exclude/filter list
|
||||
char aBuf[512];
|
||||
|
||||
str_copy(aBuf, m_pFilter);
|
||||
m_pFilter[0] = '\0';
|
||||
|
||||
char aToken[512];
|
||||
for(const char *pTok = aBuf; (pTok = str_next_token(pTok, ",", aToken, sizeof(aToken)));)
|
||||
if(m_ServerlistType == IServerBrowser::TYPE_INTERNET || m_ServerlistType == IServerBrowser::TYPE_FAVORITES)
|
||||
{
|
||||
if(str_comp_nocase(pElement, aToken) != 0)
|
||||
return SelectedCommunities();
|
||||
}
|
||||
else if(m_ServerlistType >= IServerBrowser::TYPE_FAVORITE_COMMUNITY_1 && m_ServerlistType <= IServerBrowser::TYPE_FAVORITE_COMMUNITY_3)
|
||||
{
|
||||
if(m_pFilter[0] != '\0')
|
||||
str_append(m_pFilter, ",", m_FilterSize);
|
||||
str_append(m_pFilter, aToken, m_FilterSize);
|
||||
const size_t CommunityIndex = m_ServerlistType - IServerBrowser::TYPE_FAVORITE_COMMUNITY_1;
|
||||
std::vector<const CCommunity *> vpFavoriteCommunities = FavoriteCommunities();
|
||||
dbg_assert(CommunityIndex < vpFavoriteCommunities.size(), "Invalid favorite community serverbrowser type");
|
||||
return {vpFavoriteCommunities[CommunityIndex]};
|
||||
}
|
||||
else
|
||||
{
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
unsigned CServerBrowser::CurrentCommunitiesHash() const
|
||||
{
|
||||
std::vector<const CCommunity *> vpCommunities = CurrentCommunities();
|
||||
unsigned Hash = 5381;
|
||||
for(const CCommunity *pCommunity : CurrentCommunities())
|
||||
{
|
||||
Hash = (Hash << 5) + Hash + str_quickhash(pCommunity->Id());
|
||||
}
|
||||
return Hash;
|
||||
}
|
||||
|
||||
void CFavoriteCommunityFilterList::Add(const char *pCommunityId)
|
||||
{
|
||||
// Remove community if it's already a favorite, so it will be added again at
|
||||
// the end of the list, to allow setting the entire list easier with binds.
|
||||
Remove(pCommunityId);
|
||||
|
||||
// Ensure maximum number of favorite communities, by removing least-recently
|
||||
// added communities from the beginning. One more than the maximum is removed
|
||||
// to make room for the new community.
|
||||
constexpr size_t MaxFavoriteCommunities = 3;
|
||||
if(m_vEntries.size() >= MaxFavoriteCommunities)
|
||||
{
|
||||
m_vEntries.erase(m_vEntries.begin(), m_vEntries.begin() + (MaxFavoriteCommunities - 1));
|
||||
}
|
||||
m_vEntries.emplace_back(pCommunityId);
|
||||
}
|
||||
|
||||
void CFavoriteCommunityFilterList::Remove(const char *pCommunityId)
|
||||
{
|
||||
auto FoundCommunity = std::find(m_vEntries.begin(), m_vEntries.end(), CCommunityId(pCommunityId));
|
||||
if(FoundCommunity != m_vEntries.end())
|
||||
{
|
||||
m_vEntries.erase(FoundCommunity);
|
||||
}
|
||||
}
|
||||
|
||||
void CFavoriteCommunityFilterList::Clear()
|
||||
{
|
||||
m_vEntries.clear();
|
||||
}
|
||||
|
||||
bool CFavoriteCommunityFilterList::Filtered(const char *pCommunityId) const
|
||||
{
|
||||
return std::find(m_vEntries.begin(), m_vEntries.end(), CCommunityId(pCommunityId)) != m_vEntries.end();
|
||||
}
|
||||
|
||||
bool CFavoriteCommunityFilterList::Empty() const
|
||||
{
|
||||
return m_vEntries.empty();
|
||||
}
|
||||
|
||||
void CFavoriteCommunityFilterList::Clean(const std::vector<CCommunity> &vAllowedCommunities)
|
||||
{
|
||||
auto It = std::remove_if(m_vEntries.begin(), m_vEntries.end(), [&](const auto &Community) {
|
||||
return std::find_if(vAllowedCommunities.begin(), vAllowedCommunities.end(), [&](const CCommunity &AllowedCommunity) {
|
||||
return str_comp(Community.Id(), AllowedCommunity.Id()) == 0;
|
||||
}) == vAllowedCommunities.end();
|
||||
});
|
||||
m_vEntries.erase(It, m_vEntries.end());
|
||||
}
|
||||
|
||||
void CFavoriteCommunityFilterList::Save(IConfigManager *pConfigManager) const
|
||||
{
|
||||
char aBuf[32 + CServerInfo::MAX_COMMUNITY_ID_LENGTH];
|
||||
for(const auto &FavoriteCommunity : m_vEntries)
|
||||
{
|
||||
str_copy(aBuf, "add_favorite_community \"");
|
||||
str_append(aBuf, FavoriteCommunity.Id());
|
||||
str_append(aBuf, "\"");
|
||||
pConfigManager->WriteLine(aBuf);
|
||||
}
|
||||
}
|
||||
|
||||
const std::vector<CCommunityId> &CFavoriteCommunityFilterList::Entries() const
|
||||
{
|
||||
return m_vEntries;
|
||||
}
|
||||
|
||||
void CExcludedCommunityFilterList::Add(const char *pCommunityId)
|
||||
{
|
||||
m_Entries.emplace(pCommunityId);
|
||||
}
|
||||
|
||||
void CExcludedCommunityFilterList::Remove(const char *pCommunityId)
|
||||
{
|
||||
m_Entries.erase(CCommunityId(pCommunityId));
|
||||
}
|
||||
|
||||
void CExcludedCommunityFilterList::Clear()
|
||||
{
|
||||
m_Entries.clear();
|
||||
}
|
||||
|
||||
bool CExcludedCommunityFilterList::Filtered(const char *pCommunityId) const
|
||||
{
|
||||
return std::find(m_Entries.begin(), m_Entries.end(), CCommunityId(pCommunityId)) != m_Entries.end();
|
||||
}
|
||||
|
||||
bool CExcludedCommunityFilterList::Empty() const
|
||||
{
|
||||
return m_Entries.empty();
|
||||
}
|
||||
|
||||
void CExcludedCommunityFilterList::Clean(const std::vector<CCommunity> &vAllowedCommunities)
|
||||
{
|
||||
for(auto It = m_Entries.begin(); It != m_Entries.end();)
|
||||
{
|
||||
const bool Found = std::find_if(vAllowedCommunities.begin(), vAllowedCommunities.end(), [&](const CCommunity &AllowedCommunity) {
|
||||
return str_comp(It->Id(), AllowedCommunity.Id()) == 0;
|
||||
}) != vAllowedCommunities.end();
|
||||
if(Found)
|
||||
{
|
||||
++It;
|
||||
}
|
||||
else
|
||||
{
|
||||
It = m_Entries.erase(It);
|
||||
}
|
||||
}
|
||||
// Prevent filter that would exclude all allowed communities
|
||||
if(m_Entries.size() == vAllowedCommunities.size())
|
||||
{
|
||||
m_Entries.clear();
|
||||
}
|
||||
}
|
||||
|
||||
void CExcludedCommunityFilterList::Save(IConfigManager *pConfigManager) const
|
||||
{
|
||||
char aBuf[32 + CServerInfo::MAX_COMMUNITY_ID_LENGTH];
|
||||
for(const auto &ExcludedCommunity : m_Entries)
|
||||
{
|
||||
str_copy(aBuf, "add_excluded_community \"");
|
||||
str_append(aBuf, ExcludedCommunity.Id());
|
||||
str_append(aBuf, "\"");
|
||||
pConfigManager->WriteLine(aBuf);
|
||||
}
|
||||
}
|
||||
|
||||
void CExcludedCommunityCountryFilterList::Add(const char *pCountryName)
|
||||
{
|
||||
for(const CCommunity *pCommunity : m_CurrentCommunitiesGetter())
|
||||
{
|
||||
if(pCommunity->HasCountry(pCountryName))
|
||||
{
|
||||
Add(pCommunity->Id(), pCountryName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CFilterList::Clear()
|
||||
void CExcludedCommunityCountryFilterList::Add(const char *pCommunityId, const char *pCountryName)
|
||||
{
|
||||
m_pFilter[0] = '\0';
|
||||
CCommunityId CommunityId(pCommunityId);
|
||||
if(m_Entries.find(CommunityId) == m_Entries.end())
|
||||
{
|
||||
m_Entries[CommunityId] = {};
|
||||
}
|
||||
m_Entries[CommunityId].emplace(pCountryName);
|
||||
}
|
||||
|
||||
bool CFilterList::Filtered(const char *pElement) const
|
||||
void CExcludedCommunityCountryFilterList::Remove(const char *pCountryName)
|
||||
{
|
||||
for(const CCommunity *pCommunity : m_CurrentCommunitiesGetter())
|
||||
{
|
||||
Remove(pCommunity->Id(), pCountryName);
|
||||
}
|
||||
}
|
||||
|
||||
void CExcludedCommunityCountryFilterList::Remove(const char *pCommunityId, const char *pCountryName)
|
||||
{
|
||||
auto CommunityEntry = m_Entries.find(CCommunityId(pCommunityId));
|
||||
if(CommunityEntry != m_Entries.end())
|
||||
{
|
||||
CommunityEntry->second.erase(pCountryName);
|
||||
}
|
||||
}
|
||||
|
||||
void CExcludedCommunityCountryFilterList::Clear()
|
||||
{
|
||||
for(const CCommunity *pCommunity : m_CurrentCommunitiesGetter())
|
||||
{
|
||||
auto CommunityEntry = m_Entries.find(pCommunity->Id());
|
||||
if(CommunityEntry != m_Entries.end())
|
||||
{
|
||||
CommunityEntry->second.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool CExcludedCommunityCountryFilterList::Filtered(const char *pCountryName) const
|
||||
{
|
||||
// If the needle is not defined, we exclude it if there is any other
|
||||
// exclusion, i.e. we only show those elements when the filter is empty.
|
||||
if(pElement[0] == '\0')
|
||||
if(pCountryName[0] == '\0')
|
||||
return !Empty();
|
||||
|
||||
// Special case: "*element" means anything except that element is excluded.
|
||||
// Necessary because the default filter cannot exclude unknown elements,
|
||||
// but we want to select only the DDNet community by default.
|
||||
if(m_pFilter[0] == '*')
|
||||
return str_comp(m_pFilter + 1, pElement) != 0;
|
||||
const auto Communities = m_CurrentCommunitiesGetter();
|
||||
return std::none_of(Communities.begin(), Communities.end(), [&](const CCommunity *pCommunity) {
|
||||
if(!pCommunity->HasCountry(pCountryName))
|
||||
return false;
|
||||
|
||||
// Comma separated list of excluded elements.
|
||||
return str_in_list(m_pFilter, ",", pElement);
|
||||
auto CommunityEntry = m_Entries.find(CCommunityId(pCommunity->Id()));
|
||||
if(CommunityEntry == m_Entries.end())
|
||||
return true;
|
||||
|
||||
const auto &CountryEntries = CommunityEntry->second;
|
||||
if(CountryEntries.find(CCommunityCountryName(pCountryName)) == CountryEntries.end())
|
||||
return true;
|
||||
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
bool CFilterList::Empty() const
|
||||
bool CExcludedCommunityCountryFilterList::Empty() const
|
||||
{
|
||||
return m_pFilter[0] == '\0';
|
||||
for(const CCommunity *pCommunity : m_CurrentCommunitiesGetter())
|
||||
{
|
||||
auto CommunityEntry = m_Entries.find(CCommunityId(pCommunity->Id()));
|
||||
return CommunityEntry == m_Entries.end() || CommunityEntry->second.empty();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void CFilterList::Clean(const std::vector<const char *> &vpAllowedElements)
|
||||
void CExcludedCommunityCountryFilterList::Clean(const std::vector<CCommunity> &vAllowedCommunities)
|
||||
{
|
||||
size_t NumFiltered = 0;
|
||||
char aNewList[512];
|
||||
aNewList[0] = '\0';
|
||||
|
||||
for(const char *pElement : vpAllowedElements)
|
||||
for(auto It = m_Entries.begin(); It != m_Entries.end();)
|
||||
{
|
||||
if(Filtered(pElement))
|
||||
const bool Found = std::find_if(vAllowedCommunities.begin(), vAllowedCommunities.end(), [&](const CCommunity &AllowedCommunity) {
|
||||
return str_comp(It->first.Id(), AllowedCommunity.Id()) == 0;
|
||||
}) != vAllowedCommunities.end();
|
||||
if(Found)
|
||||
{
|
||||
if(aNewList[0] != '\0')
|
||||
str_append(aNewList, ",");
|
||||
str_append(aNewList, pElement);
|
||||
++NumFiltered;
|
||||
++It;
|
||||
}
|
||||
}
|
||||
|
||||
// Prevent filter that would exclude all allowed elements
|
||||
if(NumFiltered == vpAllowedElements.size())
|
||||
m_pFilter[0] = '\0';
|
||||
else
|
||||
str_copy(m_pFilter, aNewList, m_FilterSize);
|
||||
{
|
||||
It = m_Entries.erase(It);
|
||||
}
|
||||
}
|
||||
|
||||
for(const CCommunity &AllowedCommunity : vAllowedCommunities)
|
||||
{
|
||||
auto CommunityEntry = m_Entries.find(CCommunityId(AllowedCommunity.Id()));
|
||||
if(CommunityEntry != m_Entries.end())
|
||||
{
|
||||
auto &CountryEntries = CommunityEntry->second;
|
||||
for(auto It = CountryEntries.begin(); It != CountryEntries.end();)
|
||||
{
|
||||
if(AllowedCommunity.HasCountry(It->Name()))
|
||||
{
|
||||
++It;
|
||||
}
|
||||
else
|
||||
{
|
||||
It = CountryEntries.erase(It);
|
||||
}
|
||||
}
|
||||
// Prevent filter that would exclude all allowed countries
|
||||
if(CountryEntries.size() == AllowedCommunity.Countries().size())
|
||||
{
|
||||
CountryEntries.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CExcludedCommunityCountryFilterList::Save(IConfigManager *pConfigManager) const
|
||||
{
|
||||
char aBuf[32 + CServerInfo::MAX_COMMUNITY_ID_LENGTH + CServerInfo::MAX_COMMUNITY_COUNTRY_LENGTH];
|
||||
for(const auto &[Community, Countries] : m_Entries)
|
||||
{
|
||||
for(const auto &Country : Countries)
|
||||
{
|
||||
str_copy(aBuf, "add_excluded_country \"");
|
||||
str_append(aBuf, Community.Id());
|
||||
str_append(aBuf, "\" \"");
|
||||
str_append(aBuf, Country.Name());
|
||||
str_append(aBuf, "\"");
|
||||
pConfigManager->WriteLine(aBuf);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CExcludedCommunityTypeFilterList::Add(const char *pTypeName)
|
||||
{
|
||||
for(const CCommunity *pCommunity : m_CurrentCommunitiesGetter())
|
||||
{
|
||||
if(pCommunity->HasType(pTypeName))
|
||||
{
|
||||
Add(pCommunity->Id(), pTypeName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CExcludedCommunityTypeFilterList::Add(const char *pCommunityId, const char *pTypeName)
|
||||
{
|
||||
CCommunityId CommunityId(pCommunityId);
|
||||
if(m_Entries.find(CommunityId) == m_Entries.end())
|
||||
{
|
||||
m_Entries[CommunityId] = {};
|
||||
}
|
||||
m_Entries[CommunityId].emplace(pTypeName);
|
||||
}
|
||||
|
||||
void CExcludedCommunityTypeFilterList::Remove(const char *pTypeName)
|
||||
{
|
||||
for(const CCommunity *pCommunity : m_CurrentCommunitiesGetter())
|
||||
{
|
||||
Remove(pCommunity->Id(), pTypeName);
|
||||
}
|
||||
}
|
||||
|
||||
void CExcludedCommunityTypeFilterList::Remove(const char *pCommunityId, const char *pTypeName)
|
||||
{
|
||||
auto CommunityEntry = m_Entries.find(CCommunityId(pCommunityId));
|
||||
if(CommunityEntry != m_Entries.end())
|
||||
{
|
||||
CommunityEntry->second.erase(pTypeName);
|
||||
}
|
||||
}
|
||||
|
||||
void CExcludedCommunityTypeFilterList::Clear()
|
||||
{
|
||||
for(const CCommunity *pCommunity : m_CurrentCommunitiesGetter())
|
||||
{
|
||||
auto CommunityEntry = m_Entries.find(pCommunity->Id());
|
||||
if(CommunityEntry != m_Entries.end())
|
||||
{
|
||||
CommunityEntry->second.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool CExcludedCommunityTypeFilterList::Filtered(const char *pTypeName) const
|
||||
{
|
||||
// If the needle is not defined, we exclude it if there is any other
|
||||
// exclusion, i.e. we only show those elements when the filter is empty.
|
||||
if(pTypeName[0] == '\0')
|
||||
return !Empty();
|
||||
|
||||
const auto Communities = m_CurrentCommunitiesGetter();
|
||||
return std::none_of(Communities.begin(), Communities.end(), [&](const CCommunity *pCommunity) {
|
||||
if(!pCommunity->HasType(pTypeName))
|
||||
return false;
|
||||
|
||||
auto CommunityEntry = m_Entries.find(CCommunityId(pCommunity->Id()));
|
||||
if(CommunityEntry == m_Entries.end())
|
||||
return true;
|
||||
|
||||
const auto &TypeEntries = CommunityEntry->second;
|
||||
return TypeEntries.find(CCommunityTypeName(pTypeName)) == TypeEntries.end();
|
||||
});
|
||||
}
|
||||
|
||||
bool CExcludedCommunityTypeFilterList::Empty() const
|
||||
{
|
||||
for(const CCommunity *pCommunity : m_CurrentCommunitiesGetter())
|
||||
{
|
||||
auto CommunityEntry = m_Entries.find(CCommunityId(pCommunity->Id()));
|
||||
return CommunityEntry == m_Entries.end() || CommunityEntry->second.empty();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void CExcludedCommunityTypeFilterList::Clean(const std::vector<CCommunity> &vAllowedCommunities)
|
||||
{
|
||||
for(auto It = m_Entries.begin(); It != m_Entries.end();)
|
||||
{
|
||||
const bool Found = std::find_if(vAllowedCommunities.begin(), vAllowedCommunities.end(), [&](const CCommunity &AllowedCommunity) {
|
||||
return str_comp(It->first.Id(), AllowedCommunity.Id()) == 0;
|
||||
}) != vAllowedCommunities.end();
|
||||
if(Found)
|
||||
{
|
||||
++It;
|
||||
}
|
||||
else
|
||||
{
|
||||
It = m_Entries.erase(It);
|
||||
}
|
||||
}
|
||||
|
||||
for(const CCommunity &AllowedCommunity : vAllowedCommunities)
|
||||
{
|
||||
auto CommunityEntry = m_Entries.find(CCommunityId(AllowedCommunity.Id()));
|
||||
if(CommunityEntry != m_Entries.end())
|
||||
{
|
||||
auto &TypeEntries = CommunityEntry->second;
|
||||
for(auto It = TypeEntries.begin(); It != TypeEntries.end();)
|
||||
{
|
||||
if(AllowedCommunity.HasType(It->Name()))
|
||||
{
|
||||
++It;
|
||||
}
|
||||
else
|
||||
{
|
||||
It = TypeEntries.erase(It);
|
||||
}
|
||||
}
|
||||
// Prevent filter that would exclude all allowed countries
|
||||
if(TypeEntries.size() == AllowedCommunity.Types().size())
|
||||
{
|
||||
TypeEntries.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CExcludedCommunityTypeFilterList::Save(IConfigManager *pConfigManager) const
|
||||
{
|
||||
char aBuf[32 + CServerInfo::MAX_COMMUNITY_ID_LENGTH + CServerInfo::MAX_COMMUNITY_TYPE_LENGTH];
|
||||
for(const auto &[Community, Types] : m_Entries)
|
||||
{
|
||||
for(const auto &Type : Types)
|
||||
{
|
||||
str_copy(aBuf, "add_excluded_type \"");
|
||||
str_append(aBuf, Community.Id());
|
||||
str_append(aBuf, "\" \"");
|
||||
str_append(aBuf, Type.Name());
|
||||
str_append(aBuf, "\"");
|
||||
pConfigManager->WriteLine(aBuf);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CServerBrowser::CleanFilters()
|
||||
{
|
||||
CommunitiesFilterClean();
|
||||
CountriesFilterClean();
|
||||
TypesFilterClean();
|
||||
}
|
||||
|
||||
void CServerBrowser::CommunitiesFilterClean()
|
||||
{
|
||||
std::vector<const char *> vpCommunityNames;
|
||||
for(const auto &Community : Communities())
|
||||
vpCommunityNames.push_back(Community.Id());
|
||||
m_CommunitiesFilter.Clean(vpCommunityNames);
|
||||
}
|
||||
|
||||
void CServerBrowser::CountriesFilterClean()
|
||||
{
|
||||
std::vector<const char *> vpCountryNames;
|
||||
for(const CCommunity *pCommunity : SelectedCommunities())
|
||||
for(const auto &Country : pCommunity->Countries())
|
||||
vpCountryNames.push_back(Country.Name());
|
||||
m_CountriesFilter.Clean(vpCountryNames);
|
||||
}
|
||||
|
||||
void CServerBrowser::TypesFilterClean()
|
||||
{
|
||||
std::vector<const char *> vpTypeNames;
|
||||
for(const CCommunity *pCommunity : SelectedCommunities())
|
||||
for(const auto &Type : pCommunity->Types())
|
||||
vpTypeNames.push_back(Type.Name());
|
||||
m_TypesFilter.Clean(vpTypeNames);
|
||||
// Keep filters if we failed to load any communities
|
||||
if(Communities().empty())
|
||||
return;
|
||||
FavoriteCommunitiesFilter().Clean(Communities());
|
||||
CommunitiesFilter().Clean(Communities());
|
||||
CountriesFilter().Clean(Communities());
|
||||
TypesFilter().Clean(Communities());
|
||||
}
|
||||
|
||||
bool CServerBrowser::IsRegistered(const NETADDR &Addr)
|
||||
|
|
|
@ -9,7 +9,9 @@
|
|||
#include <engine/serverbrowser.h>
|
||||
#include <engine/shared/memheap.h>
|
||||
|
||||
#include <functional>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
|
||||
typedef struct _json_value json_value;
|
||||
class CNetClient;
|
||||
|
@ -23,6 +25,87 @@ class IServerBrowserPingCache;
|
|||
class IStorage;
|
||||
class IHttp;
|
||||
|
||||
class CCommunityId
|
||||
{
|
||||
char m_aId[CServerInfo::MAX_COMMUNITY_ID_LENGTH];
|
||||
|
||||
public:
|
||||
CCommunityId(const char *pCommunityId)
|
||||
{
|
||||
str_copy(m_aId, pCommunityId);
|
||||
}
|
||||
|
||||
const char *Id() const { return m_aId; }
|
||||
|
||||
bool operator==(const CCommunityId &Other) const
|
||||
{
|
||||
return str_comp(Id(), Other.Id()) == 0;
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct std::hash<CCommunityId>
|
||||
{
|
||||
size_t operator()(const CCommunityId &Elem) const noexcept
|
||||
{
|
||||
return str_quickhash(Elem.Id());
|
||||
}
|
||||
};
|
||||
|
||||
class CCommunityCountryName
|
||||
{
|
||||
char m_aName[CServerInfo::MAX_COMMUNITY_COUNTRY_LENGTH];
|
||||
|
||||
public:
|
||||
CCommunityCountryName(const char *pCountryName)
|
||||
{
|
||||
str_copy(m_aName, pCountryName);
|
||||
}
|
||||
|
||||
const char *Name() const { return m_aName; }
|
||||
|
||||
bool operator==(const CCommunityCountryName &Other) const
|
||||
{
|
||||
return str_comp(Name(), Other.Name()) == 0;
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct std::hash<CCommunityCountryName>
|
||||
{
|
||||
size_t operator()(const CCommunityCountryName &Elem) const noexcept
|
||||
{
|
||||
return str_quickhash(Elem.Name());
|
||||
}
|
||||
};
|
||||
|
||||
class CCommunityTypeName
|
||||
{
|
||||
char m_aName[CServerInfo::MAX_COMMUNITY_TYPE_LENGTH];
|
||||
|
||||
public:
|
||||
CCommunityTypeName(const char *pTypeName)
|
||||
{
|
||||
str_copy(m_aName, pTypeName);
|
||||
}
|
||||
|
||||
const char *Name() const { return m_aName; }
|
||||
|
||||
bool operator==(const CCommunityTypeName &Other) const
|
||||
{
|
||||
return str_comp(Name(), Other.Name()) == 0;
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct std::hash<CCommunityTypeName>
|
||||
{
|
||||
size_t operator()(const CCommunityTypeName &Elem) const noexcept
|
||||
{
|
||||
return str_quickhash(Elem.Name());
|
||||
}
|
||||
};
|
||||
|
||||
class CCommunityServer
|
||||
{
|
||||
char m_aCommunityId[CServerInfo::MAX_COMMUNITY_ID_LENGTH];
|
||||
|
@ -42,23 +125,81 @@ public:
|
|||
const char *TypeName() const { return m_aTypeName; }
|
||||
};
|
||||
|
||||
class CFilterList : public IFilterList
|
||||
class CFavoriteCommunityFilterList : public IFilterList
|
||||
{
|
||||
char *m_pFilter;
|
||||
size_t m_FilterSize;
|
||||
|
||||
public:
|
||||
CFilterList(char *pFilter, size_t FilterSize) :
|
||||
m_pFilter(pFilter), m_FilterSize(FilterSize)
|
||||
void Add(const char *pCommunityId) override;
|
||||
void Remove(const char *pCommunityId) override;
|
||||
void Clear() override;
|
||||
bool Filtered(const char *pCommunityId) const override;
|
||||
bool Empty() const override;
|
||||
void Clean(const std::vector<CCommunity> &vAllowedCommunities);
|
||||
void Save(IConfigManager *pConfigManager) const;
|
||||
const std::vector<CCommunityId> &Entries() const;
|
||||
|
||||
private:
|
||||
std::vector<CCommunityId> m_vEntries;
|
||||
};
|
||||
|
||||
class CExcludedCommunityFilterList : public IFilterList
|
||||
{
|
||||
public:
|
||||
void Add(const char *pCommunityId) override;
|
||||
void Remove(const char *pCommunityId) override;
|
||||
void Clear() override;
|
||||
bool Filtered(const char *pCommunityId) const override;
|
||||
bool Empty() const override;
|
||||
void Clean(const std::vector<CCommunity> &vAllowedCommunities);
|
||||
void Save(IConfigManager *pConfigManager) const;
|
||||
|
||||
private:
|
||||
std::unordered_set<CCommunityId> m_Entries;
|
||||
};
|
||||
|
||||
class CExcludedCommunityCountryFilterList : public IFilterList
|
||||
{
|
||||
public:
|
||||
CExcludedCommunityCountryFilterList(std::function<std::vector<const CCommunity *>()> CurrentCommunitiesGetter) :
|
||||
m_CurrentCommunitiesGetter(CurrentCommunitiesGetter)
|
||||
{
|
||||
}
|
||||
|
||||
void Add(const char *pElement) override;
|
||||
void Remove(const char *pElement) override;
|
||||
void Add(const char *pCountryName) override;
|
||||
void Add(const char *pCommunityId, const char *pCountryName);
|
||||
void Remove(const char *pCountryName) override;
|
||||
void Remove(const char *pCommunityId, const char *pCountryName);
|
||||
void Clear() override;
|
||||
bool Filtered(const char *pElement) const override;
|
||||
bool Filtered(const char *pCountryName) const override;
|
||||
bool Empty() const override;
|
||||
void Clean(const std::vector<const char *> &vpAllowedElements);
|
||||
void Clean(const std::vector<CCommunity> &vAllowedCommunities);
|
||||
void Save(IConfigManager *pConfigManager) const;
|
||||
|
||||
private:
|
||||
std::function<std::vector<const CCommunity *>()> m_CurrentCommunitiesGetter;
|
||||
std::unordered_map<CCommunityId, std::unordered_set<CCommunityCountryName>> m_Entries;
|
||||
};
|
||||
|
||||
class CExcludedCommunityTypeFilterList : public IFilterList
|
||||
{
|
||||
public:
|
||||
CExcludedCommunityTypeFilterList(std::function<std::vector<const CCommunity *>()> CurrentCommunitiesGetter) :
|
||||
m_CurrentCommunitiesGetter(CurrentCommunitiesGetter)
|
||||
{
|
||||
}
|
||||
|
||||
void Add(const char *pTypeName) override;
|
||||
void Add(const char *pCommunityId, const char *pTypeName);
|
||||
void Remove(const char *pTypeName) override;
|
||||
void Remove(const char *pCommunityId, const char *pTypeName);
|
||||
void Clear() override;
|
||||
bool Filtered(const char *pTypeName) const override;
|
||||
bool Empty() const override;
|
||||
void Clean(const std::vector<CCommunity> &vAllowedCommunities);
|
||||
void Save(IConfigManager *pConfigManager) const;
|
||||
|
||||
private:
|
||||
std::function<std::vector<const CCommunity *>()> m_CurrentCommunitiesGetter;
|
||||
std::unordered_map<CCommunityId, std::unordered_set<CCommunityTypeName>> m_Entries;
|
||||
};
|
||||
|
||||
class CServerBrowser : public IServerBrowser
|
||||
|
@ -80,7 +221,7 @@ public:
|
|||
virtual ~CServerBrowser();
|
||||
|
||||
// interface functions
|
||||
void Refresh(int Type) override;
|
||||
void Refresh(int Type, bool Force = false) override;
|
||||
bool IsRefreshing() const override;
|
||||
bool IsGettingServerlist() const override;
|
||||
int LoadingProgression() const override;
|
||||
|
@ -106,20 +247,23 @@ public:
|
|||
const std::vector<CCommunity> &Communities() const override;
|
||||
const CCommunity *Community(const char *pCommunityId) const override;
|
||||
std::vector<const CCommunity *> SelectedCommunities() const override;
|
||||
std::vector<const CCommunity *> FavoriteCommunities() const override;
|
||||
std::vector<const CCommunity *> CurrentCommunities() const override;
|
||||
unsigned CurrentCommunitiesHash() const override;
|
||||
|
||||
bool DDNetInfoAvailable() const override { return m_pDDNetInfo != nullptr; }
|
||||
int64_t DDNetInfoUpdateTime() const override { return m_DDNetInfoUpdateTime; }
|
||||
|
||||
CFilterList &CommunitiesFilter() override { return m_CommunitiesFilter; }
|
||||
CFilterList &CountriesFilter() override { return m_CountriesFilter; }
|
||||
CFilterList &TypesFilter() override { return m_TypesFilter; }
|
||||
const CFilterList &CommunitiesFilter() const override { return m_CommunitiesFilter; }
|
||||
const CFilterList &CountriesFilter() const override { return m_CountriesFilter; }
|
||||
const CFilterList &TypesFilter() const override { return m_TypesFilter; }
|
||||
CFavoriteCommunityFilterList &FavoriteCommunitiesFilter() override { return m_FavoriteCommunitiesFilter; }
|
||||
CExcludedCommunityFilterList &CommunitiesFilter() override { return m_CommunitiesFilter; }
|
||||
CExcludedCommunityCountryFilterList &CountriesFilter() override { return m_CountriesFilter; }
|
||||
CExcludedCommunityTypeFilterList &TypesFilter() override { return m_TypesFilter; }
|
||||
const CFavoriteCommunityFilterList &FavoriteCommunitiesFilter() const override { return m_FavoriteCommunitiesFilter; }
|
||||
const CExcludedCommunityFilterList &CommunitiesFilter() const override { return m_CommunitiesFilter; }
|
||||
const CExcludedCommunityCountryFilterList &CountriesFilter() const override { return m_CountriesFilter; }
|
||||
const CExcludedCommunityTypeFilterList &TypesFilter() const override { return m_TypesFilter; }
|
||||
void CleanFilters() override;
|
||||
|
||||
void CommunitiesFilterClean();
|
||||
void CountriesFilterClean();
|
||||
void TypesFilterClean();
|
||||
|
||||
//
|
||||
void Update();
|
||||
void OnServerInfoUpdate(const NETADDR &Addr, int Token, const CServerInfo *pInfo);
|
||||
|
@ -162,9 +306,10 @@ private:
|
|||
|
||||
int m_OwnLocation = CServerInfo::LOC_UNKNOWN;
|
||||
|
||||
CFilterList m_CommunitiesFilter;
|
||||
CFilterList m_CountriesFilter;
|
||||
CFilterList m_TypesFilter;
|
||||
CFavoriteCommunityFilterList m_FavoriteCommunitiesFilter;
|
||||
CExcludedCommunityFilterList m_CommunitiesFilter;
|
||||
CExcludedCommunityCountryFilterList m_CountriesFilter;
|
||||
CExcludedCommunityTypeFilterList m_TypesFilter;
|
||||
|
||||
json_value *m_pDDNetInfo;
|
||||
int64_t m_DDNetInfoUpdateTime;
|
||||
|
@ -217,8 +362,21 @@ private:
|
|||
void RequestImpl(const NETADDR &Addr, CServerEntry *pEntry, int *pBasicToken, int *pToken, bool RandomToken) const;
|
||||
|
||||
void RegisterCommands();
|
||||
static void ConfigSaveCallback(IConfigManager *pConfigManager, void *pUserData);
|
||||
static void Con_AddFavoriteCommunity(IConsole::IResult *pResult, void *pUserData);
|
||||
static void Con_RemoveFavoriteCommunity(IConsole::IResult *pResult, void *pUserData);
|
||||
static void Con_AddExcludedCommunity(IConsole::IResult *pResult, void *pUserData);
|
||||
static void Con_RemoveExcludedCommunity(IConsole::IResult *pResult, void *pUserData);
|
||||
static void Con_AddExcludedCountry(IConsole::IResult *pResult, void *pUserData);
|
||||
static void Con_RemoveExcludedCountry(IConsole::IResult *pResult, void *pUserData);
|
||||
static void Con_AddExcludedType(IConsole::IResult *pResult, void *pUserData);
|
||||
static void Con_RemoveExcludedType(IConsole::IResult *pResult, void *pUserData);
|
||||
static void Con_LeakIpAddress(IConsole::IResult *pResult, void *pUserData);
|
||||
|
||||
bool ValidateCommunityId(const char *pCommunityId) const;
|
||||
bool ValidateCountryName(const char *pCountryName) const;
|
||||
bool ValidateTypeName(const char *pTypeName) const;
|
||||
|
||||
void SetInfo(CServerEntry *pEntry, const CServerInfo &Info) const;
|
||||
void SetLatency(NETADDR Addr, int Latency);
|
||||
|
||||
|
|
|
@ -231,6 +231,8 @@ public:
|
|||
const SHA256_DIGEST &IconSha256() const { return m_IconSha256; }
|
||||
const std::vector<CCommunityCountry> &Countries() const { return m_vCountries; }
|
||||
const std::vector<CCommunityType> &Types() const { return m_vTypes; }
|
||||
bool HasCountry(const char *pCountryName) const;
|
||||
bool HasType(const char *pTypeName) const;
|
||||
bool HasRanks() const { return m_HasFinishes; }
|
||||
CServerInfo::ERankState HasRank(const char *pMap) const;
|
||||
};
|
||||
|
@ -271,6 +273,9 @@ public:
|
|||
TYPE_INTERNET = 0,
|
||||
TYPE_LAN,
|
||||
TYPE_FAVORITES,
|
||||
TYPE_FAVORITE_COMMUNITY_1,
|
||||
TYPE_FAVORITE_COMMUNITY_2,
|
||||
TYPE_FAVORITE_COMMUNITY_3,
|
||||
NUM_TYPES,
|
||||
};
|
||||
|
||||
|
@ -279,7 +284,7 @@ public:
|
|||
|
||||
static constexpr const char *SEARCH_EXCLUDE_TOKEN = ";";
|
||||
|
||||
virtual void Refresh(int Type) = 0;
|
||||
virtual void Refresh(int Type, bool Force = false) = 0;
|
||||
virtual bool IsGettingServerlist() const = 0;
|
||||
virtual bool IsRefreshing() const = 0;
|
||||
virtual int LoadingProgression() const = 0;
|
||||
|
@ -296,11 +301,18 @@ public:
|
|||
virtual const std::vector<CCommunity> &Communities() const = 0;
|
||||
virtual const CCommunity *Community(const char *pCommunityId) const = 0;
|
||||
virtual std::vector<const CCommunity *> SelectedCommunities() const = 0;
|
||||
virtual std::vector<const CCommunity *> FavoriteCommunities() const = 0;
|
||||
virtual std::vector<const CCommunity *> CurrentCommunities() const = 0;
|
||||
virtual unsigned CurrentCommunitiesHash() const = 0;
|
||||
|
||||
virtual bool DDNetInfoAvailable() const = 0;
|
||||
virtual int64_t DDNetInfoUpdateTime() const = 0;
|
||||
|
||||
virtual IFilterList &FavoriteCommunitiesFilter() = 0;
|
||||
virtual IFilterList &CommunitiesFilter() = 0;
|
||||
virtual IFilterList &CountriesFilter() = 0;
|
||||
virtual IFilterList &TypesFilter() = 0;
|
||||
virtual const IFilterList &FavoriteCommunitiesFilter() const = 0;
|
||||
virtual const IFilterList &CommunitiesFilter() const = 0;
|
||||
virtual const IFilterList &CountriesFilter() const = 0;
|
||||
virtual const IFilterList &TypesFilter() const = 0;
|
||||
|
|
|
@ -134,7 +134,7 @@ MACRO_CONFIG_INT(ClPlayerDefaultEyes, player_default_eyes, 0, 0, 5, CFGFLAG_CLIE
|
|||
MACRO_CONFIG_STR(ClSkinPrefix, cl_skin_prefix, 12, "", CFGFLAG_CLIENT | CFGFLAG_SAVE, "Replace the skins by skins with this prefix (e.g. kitty, santa)")
|
||||
MACRO_CONFIG_INT(ClFatSkins, cl_fat_skins, 0, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Enable fat skins")
|
||||
|
||||
MACRO_CONFIG_INT(UiPage, ui_page, 6, 6, 10, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Interface page")
|
||||
MACRO_CONFIG_INT(UiPage, ui_page, 6, 6, 11, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Interface page")
|
||||
MACRO_CONFIG_INT(UiSettingsPage, ui_settings_page, 0, 0, 9, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Interface settings page")
|
||||
MACRO_CONFIG_INT(UiToolboxPage, ui_toolbox_page, 0, 0, 2, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Toolbox page")
|
||||
MACRO_CONFIG_STR(UiServerAddress, ui_server_address, 1024, "localhost:8303", CFGFLAG_CLIENT | CFGFLAG_SAVE | CFGFLAG_INSENSITIVE, "Interface server address")
|
||||
|
@ -286,9 +286,6 @@ MACRO_CONFIG_INT(BrFilterConnectingPlayers, br_filter_connecting_players, 1, 0,
|
|||
MACRO_CONFIG_STR(BrFilterServerAddress, br_filter_serveraddress, 128, "", CFGFLAG_SAVE | CFGFLAG_CLIENT, "Server address to filter")
|
||||
MACRO_CONFIG_INT(BrFilterUnfinishedMap, br_filter_unfinished_map, 0, 0, 1, CFGFLAG_SAVE | CFGFLAG_CLIENT, "Show only servers with unfinished maps")
|
||||
|
||||
MACRO_CONFIG_STR(BrFilterExcludeCommunities, br_filter_exclude_communities, 512, "", CFGFLAG_SAVE | CFGFLAG_CLIENT, "Filter out servers by community")
|
||||
MACRO_CONFIG_STR(BrFilterExcludeCountries, br_filter_exclude_countries, 512, "", CFGFLAG_SAVE | CFGFLAG_CLIENT, "Filter out communities' servers by country")
|
||||
MACRO_CONFIG_STR(BrFilterExcludeTypes, br_filter_exclude_types, 512, "", CFGFLAG_SAVE | CFGFLAG_CLIENT, "Filter out communities' servers by gametype")
|
||||
MACRO_CONFIG_INT(BrIndicateFinished, br_indicate_finished, 1, 0, 1, CFGFLAG_SAVE | CFGFLAG_CLIENT, "Show whether you have finished a DDNet map (transmits your player name to info.ddnet.org/info)")
|
||||
MACRO_CONFIG_STR(BrLocation, br_location, 16, "auto", CFGFLAG_SAVE | CFGFLAG_CLIENT, "Override location for ping estimation, available: auto, af, as, as:cn, eu, na, oc, sa (Automatic, Africa, Asia, China, Europe, North America, Oceania/Australia, South America")
|
||||
MACRO_CONFIG_STR(BrCachedBestServerinfoUrl, br_cached_best_serverinfo_url, 256, "", CFGFLAG_SAVE | CFGFLAG_CLIENT, "Do not set this variable, instead create a ddnet-serverlist-urls.cfg next to settings_ddnet.cfg to specify all possible serverlist URLs")
|
||||
|
|
|
@ -59,7 +59,6 @@ CMenus::CMenus()
|
|||
m_ActivePage = PAGE_INTERNET;
|
||||
m_MenuPage = 0;
|
||||
m_GamePage = PAGE_GAME;
|
||||
m_JoinTutorial = false;
|
||||
|
||||
m_NeedRestartGraphics = false;
|
||||
m_NeedRestartSound = false;
|
||||
|
@ -157,7 +156,7 @@ int CMenus::DoButton_Menu(CButtonContainer *pButtonContainer, const char *pText,
|
|||
return UI()->DoButtonLogic(pButtonContainer, Checked, pRect);
|
||||
}
|
||||
|
||||
int CMenus::DoButton_MenuTab(CButtonContainer *pButtonContainer, const char *pText, int Checked, const CUIRect *pRect, int Corners, SUIAnimator *pAnimator, const ColorRGBA *pDefaultColor, const ColorRGBA *pActiveColor, const ColorRGBA *pHoverColor, float EdgeRounding)
|
||||
int CMenus::DoButton_MenuTab(CButtonContainer *pButtonContainer, const char *pText, int Checked, const CUIRect *pRect, int Corners, SUIAnimator *pAnimator, const ColorRGBA *pDefaultColor, const ColorRGBA *pActiveColor, const ColorRGBA *pHoverColor, float EdgeRounding, const SCommunityIcon *pCommunityIcon)
|
||||
{
|
||||
const bool MouseInside = UI()->HotItem() == pButtonContainer;
|
||||
CUIRect Rect = *pRect;
|
||||
|
@ -230,9 +229,18 @@ int CMenus::DoButton_MenuTab(CButtonContainer *pButtonContainer, const char *pTe
|
|||
}
|
||||
}
|
||||
|
||||
CUIRect Temp;
|
||||
Rect.HMargin(2.0f, &Temp);
|
||||
UI()->DoLabel(&Temp, pText, Temp.h * CUI::ms_FontmodHeight, TEXTALIGN_MC);
|
||||
if(pCommunityIcon)
|
||||
{
|
||||
CUIRect CommunityIcon;
|
||||
Rect.Margin(2.0f, &CommunityIcon);
|
||||
RenderCommunityIcon(pCommunityIcon, CommunityIcon, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
CUIRect Label;
|
||||
Rect.HMargin(2.0f, &Label);
|
||||
UI()->DoLabel(&Label, pText, Label.h * CUI::ms_FontmodHeight, TEXTALIGN_MC);
|
||||
}
|
||||
|
||||
return UI()->DoButtonLogic(pButtonContainer, Checked, pRect);
|
||||
}
|
||||
|
@ -250,6 +258,24 @@ int CMenus::DoButton_GridHeader(const void *pID, const char *pText, int Checked,
|
|||
return UI()->DoButtonLogic(pID, Checked, pRect);
|
||||
}
|
||||
|
||||
int CMenus::DoButton_Favorite(const void *pButtonId, const void *pParentId, bool Checked, const CUIRect *pRect)
|
||||
{
|
||||
if(Checked || (pParentId != nullptr && UI()->HotItem() == pParentId) || UI()->HotItem() == pButtonId)
|
||||
{
|
||||
TextRender()->SetFontPreset(EFontPreset::ICON_FONT);
|
||||
TextRender()->SetRenderFlags(ETextRenderFlags::TEXT_RENDER_FLAG_ONLY_ADVANCE_WIDTH | ETextRenderFlags::TEXT_RENDER_FLAG_NO_X_BEARING | ETextRenderFlags::TEXT_RENDER_FLAG_NO_Y_BEARING | ETextRenderFlags::TEXT_RENDER_FLAG_NO_PIXEL_ALIGMENT | ETextRenderFlags::TEXT_RENDER_FLAG_NO_OVERSIZE);
|
||||
const float Alpha = UI()->HotItem() == pButtonId ? 0.2f : 0.0f;
|
||||
TextRender()->TextColor(Checked ? ColorRGBA(1.0f, 0.85f, 0.3f, 0.8f + Alpha) : ColorRGBA(0.5f, 0.5f, 0.5f, 0.8f + Alpha));
|
||||
SLabelProperties Props;
|
||||
Props.m_MaxWidth = pRect->w;
|
||||
UI()->DoLabel(pRect, FONT_ICON_STAR, 12.0f, TEXTALIGN_MC, Props);
|
||||
TextRender()->TextColor(TextRender()->DefaultTextColor());
|
||||
TextRender()->SetRenderFlags(0);
|
||||
TextRender()->SetFontPreset(EFontPreset::DEFAULT_FONT);
|
||||
}
|
||||
return UI()->DoButtonLogic(pButtonId, 0, pRect);
|
||||
}
|
||||
|
||||
int CMenus::DoButton_CheckBox_Common(const void *pID, const char *pText, const char *pBoxText, const CUIRect *pRect)
|
||||
{
|
||||
CUIRect Box, Label;
|
||||
|
@ -573,7 +599,7 @@ void CMenus::RenderMenubar(CUIRect Box)
|
|||
{
|
||||
if(ServerBrowser()->GetCurrentType() != IServerBrowser::TYPE_INTERNET)
|
||||
{
|
||||
if(ServerBrowser()->GetCurrentType() != IServerBrowser::TYPE_FAVORITES)
|
||||
if(ServerBrowser()->GetCurrentType() == IServerBrowser::TYPE_LAN)
|
||||
Client()->RequestDDNetInfo();
|
||||
ServerBrowser()->Refresh(IServerBrowser::TYPE_INTERNET);
|
||||
}
|
||||
|
@ -597,7 +623,7 @@ void CMenus::RenderMenubar(CUIRect Box)
|
|||
{
|
||||
if(ServerBrowser()->GetCurrentType() != IServerBrowser::TYPE_FAVORITES)
|
||||
{
|
||||
if(ServerBrowser()->GetCurrentType() != IServerBrowser::TYPE_INTERNET)
|
||||
if(ServerBrowser()->GetCurrentType() == IServerBrowser::TYPE_LAN)
|
||||
Client()->RequestDDNetInfo();
|
||||
ServerBrowser()->Refresh(IServerBrowser::TYPE_FAVORITES);
|
||||
}
|
||||
|
@ -605,6 +631,33 @@ void CMenus::RenderMenubar(CUIRect Box)
|
|||
}
|
||||
GameClient()->m_Tooltips.DoToolTip(&s_FavoritesButton, &Button, Localize("Favorites"));
|
||||
|
||||
size_t FavoriteCommunityIndex = 0;
|
||||
static CButtonContainer s_aFavoriteCommunityButtons[3];
|
||||
static_assert(std::size(s_aFavoriteCommunityButtons) == (size_t)PAGE_FAVORITE_COMMUNITY_3 - PAGE_FAVORITE_COMMUNITY_1 + 1);
|
||||
static_assert(std::size(s_aFavoriteCommunityButtons) == (size_t)BIT_TAB_FAVORITE_COMMUNITY_3 - BIT_TAB_FAVORITE_COMMUNITY_1 + 1);
|
||||
static_assert(std::size(s_aFavoriteCommunityButtons) == (size_t)IServerBrowser::TYPE_FAVORITE_COMMUNITY_3 - IServerBrowser::TYPE_FAVORITE_COMMUNITY_1 + 1);
|
||||
for(const CCommunity *pCommunity : ServerBrowser()->FavoriteCommunities())
|
||||
{
|
||||
Box.VSplitLeft(75.0f, &Button, &Box);
|
||||
const int Page = PAGE_FAVORITE_COMMUNITY_1 + FavoriteCommunityIndex;
|
||||
if(DoButton_MenuTab(&s_aFavoriteCommunityButtons[FavoriteCommunityIndex], FONT_ICON_ELLIPSIS, m_ActivePage == Page, &Button, IGraphics::CORNER_T, &m_aAnimatorsBigPage[BIT_TAB_FAVORITE_COMMUNITY_1 + FavoriteCommunityIndex], nullptr, nullptr, nullptr, 10.0f, FindCommunityIcon(pCommunity->Id())))
|
||||
{
|
||||
const int BrowserType = IServerBrowser::TYPE_FAVORITE_COMMUNITY_1 + FavoriteCommunityIndex;
|
||||
if(ServerBrowser()->GetCurrentType() != BrowserType)
|
||||
{
|
||||
if(ServerBrowser()->GetCurrentType() == IServerBrowser::TYPE_LAN)
|
||||
Client()->RequestDDNetInfo();
|
||||
ServerBrowser()->Refresh(BrowserType);
|
||||
}
|
||||
NewPage = Page;
|
||||
}
|
||||
GameClient()->m_Tooltips.DoToolTip(&s_aFavoriteCommunityButtons[FavoriteCommunityIndex], &Button, pCommunity->Name());
|
||||
|
||||
++FavoriteCommunityIndex;
|
||||
if(FavoriteCommunityIndex >= std::size(s_aFavoriteCommunityButtons))
|
||||
break;
|
||||
}
|
||||
|
||||
TextRender()->SetRenderFlags(0);
|
||||
TextRender()->SetFontPreset(EFontPreset::DEFAULT_FONT);
|
||||
}
|
||||
|
@ -790,10 +843,22 @@ void CMenus::OnInit()
|
|||
if(g_Config.m_ClShowWelcome)
|
||||
{
|
||||
m_Popup = POPUP_LANGUAGE;
|
||||
str_copy(g_Config.m_BrFilterExcludeCommunities, "*ddnet");
|
||||
m_CreateDefaultFavoriteCommunities = true;
|
||||
}
|
||||
|
||||
if(g_Config.m_UiPage >= PAGE_FAVORITE_COMMUNITY_1 && g_Config.m_UiPage <= PAGE_FAVORITE_COMMUNITY_3 &&
|
||||
(size_t)(g_Config.m_UiPage - PAGE_FAVORITE_COMMUNITY_1) >= ServerBrowser()->FavoriteCommunities().size())
|
||||
{
|
||||
// Reset page to internet when there is no favorite community for this page.
|
||||
g_Config.m_UiPage = PAGE_INTERNET;
|
||||
}
|
||||
|
||||
if(g_Config.m_ClSkipStartMenu)
|
||||
{
|
||||
m_ShowStart = false;
|
||||
}
|
||||
|
||||
SetMenuPage(g_Config.m_UiPage);
|
||||
|
||||
m_RefreshButton.Init(UI(), -1);
|
||||
m_ConnectButton.Init(UI(), -1);
|
||||
|
@ -802,7 +867,15 @@ void CMenus::OnInit()
|
|||
Console()->Chain("remove_favorite", ConchainFavoritesUpdate, this);
|
||||
Console()->Chain("add_friend", ConchainFriendlistUpdate, this);
|
||||
Console()->Chain("remove_friend", ConchainFriendlistUpdate, this);
|
||||
Console()->Chain("br_filter_exclude_communities", ConchainCommunitiesUpdate, this);
|
||||
|
||||
Console()->Chain("add_excluded_community", ConchainCommunitiesUpdate, this);
|
||||
Console()->Chain("remove_excluded_community", ConchainCommunitiesUpdate, this);
|
||||
Console()->Chain("add_excluded_country", ConchainCommunitiesUpdate, this);
|
||||
Console()->Chain("remove_excluded_country", ConchainCommunitiesUpdate, this);
|
||||
Console()->Chain("add_excluded_type", ConchainCommunitiesUpdate, this);
|
||||
Console()->Chain("remove_excluded_type", ConchainCommunitiesUpdate, this);
|
||||
|
||||
Console()->Chain("ui_page", ConchainUiPageUpdate, this);
|
||||
|
||||
Console()->Chain("snd_enable", ConchainUpdateMusicState, this);
|
||||
Console()->Chain("snd_enable_music", ConchainUpdateMusicState, this);
|
||||
|
@ -928,15 +1001,32 @@ void CMenus::Render()
|
|||
static int s_Frame = 0;
|
||||
if(s_Frame == 0)
|
||||
{
|
||||
SetMenuPage(g_Config.m_UiPage);
|
||||
RefreshBrowserTab(g_Config.m_UiPage);
|
||||
s_Frame++;
|
||||
}
|
||||
else if(s_Frame == 1)
|
||||
{
|
||||
UpdateMusicState();
|
||||
RefreshBrowserTab(g_Config.m_UiPage);
|
||||
s_Frame++;
|
||||
}
|
||||
else
|
||||
{
|
||||
UpdateCommunityIcons();
|
||||
}
|
||||
|
||||
// Initially add DDNet as favorite community and select its tab.
|
||||
// This must be delayed until the DDNet info is available.
|
||||
if(m_CreateDefaultFavoriteCommunities && ServerBrowser()->DDNetInfoAvailable())
|
||||
{
|
||||
m_CreateDefaultFavoriteCommunities = false;
|
||||
if(ServerBrowser()->Community(IServerBrowser::COMMUNITY_DDNET) != nullptr)
|
||||
{
|
||||
ServerBrowser()->FavoriteCommunitiesFilter().Clear();
|
||||
ServerBrowser()->FavoriteCommunitiesFilter().Add(IServerBrowser::COMMUNITY_DDNET);
|
||||
SetMenuPage(PAGE_FAVORITE_COMMUNITY_1);
|
||||
ServerBrowser()->Refresh(IServerBrowser::TYPE_FAVORITE_COMMUNITY_1);
|
||||
}
|
||||
}
|
||||
|
||||
if(Client()->State() == IClient::STATE_ONLINE || Client()->State() == IClient::STATE_DEMOPLAYBACK)
|
||||
{
|
||||
|
@ -965,13 +1055,18 @@ void CMenus::Render()
|
|||
|
||||
if(m_Popup == POPUP_NONE)
|
||||
{
|
||||
if(m_JoinTutorial && !Client()->InfoTaskRunning() && !ServerBrowser()->IsGettingServerlist())
|
||||
if(m_JoinTutorial && ServerBrowser()->DDNetInfoAvailable() && !ServerBrowser()->IsGettingServerlist())
|
||||
{
|
||||
m_JoinTutorial = false;
|
||||
// This is only reached on first launch, when the DDNet community tab has been created and
|
||||
// activated by default, so the server info for the tutorial server should be available.
|
||||
const char *pAddr = ServerBrowser()->GetTutorialServer();
|
||||
if(pAddr)
|
||||
{
|
||||
Client()->Connect(pAddr);
|
||||
}
|
||||
}
|
||||
|
||||
if(m_ShowStart && Client()->State() == IClient::STATE_OFFLINE)
|
||||
{
|
||||
m_pBackground->ChangePosition(CMenuBackground::POS_START);
|
||||
|
@ -1047,10 +1142,16 @@ void CMenus::Render()
|
|||
m_pBackground->ChangePosition(CMenuBackground::POS_BROWSER_FAVORITES);
|
||||
RenderServerbrowser(MainView);
|
||||
}
|
||||
else if(m_MenuPage >= PAGE_FAVORITE_COMMUNITY_1 && m_MenuPage <= PAGE_FAVORITE_COMMUNITY_3)
|
||||
{
|
||||
m_pBackground->ChangePosition(m_MenuPage - PAGE_FAVORITE_COMMUNITY_1 + CMenuBackground::POS_BROWSER_CUSTOM0);
|
||||
RenderServerbrowser(MainView);
|
||||
}
|
||||
else if(m_MenuPage == PAGE_SETTINGS)
|
||||
{
|
||||
RenderSettings(MainView);
|
||||
}
|
||||
|
||||
// do tab bar
|
||||
RenderMenubar(TabBar);
|
||||
}
|
||||
}
|
||||
|
@ -2179,11 +2280,8 @@ const CMenus::CMenuImage *CMenus::FindMenuImage(const char *pName)
|
|||
|
||||
void CMenus::SetMenuPage(int NewPage)
|
||||
{
|
||||
if(NewPage == PAGE_DDNET_LEGACY || NewPage == PAGE_KOG_LEGACY)
|
||||
NewPage = PAGE_INTERNET;
|
||||
|
||||
m_MenuPage = NewPage;
|
||||
if(NewPage >= PAGE_INTERNET && NewPage <= PAGE_FAVORITES)
|
||||
if(NewPage >= PAGE_INTERNET && NewPage <= PAGE_FAVORITE_COMMUNITY_3)
|
||||
g_Config.m_UiPage = NewPage;
|
||||
}
|
||||
|
||||
|
@ -2203,4 +2301,9 @@ void CMenus::RefreshBrowserTab(int UiPage)
|
|||
Client()->RequestDDNetInfo();
|
||||
ServerBrowser()->Refresh(IServerBrowser::TYPE_FAVORITES);
|
||||
}
|
||||
else if(UiPage >= PAGE_FAVORITE_COMMUNITY_1 && UiPage <= PAGE_FAVORITE_COMMUNITY_3)
|
||||
{
|
||||
Client()->RequestDDNetInfo();
|
||||
ServerBrowser()->Refresh(UiPage - PAGE_FAVORITE_COMMUNITY_1 + IServerBrowser::TYPE_FAVORITE_COMMUNITY_1);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -45,6 +45,14 @@ public:
|
|||
virtual bool OnInput(const IInput::CEvent &Event) override;
|
||||
};
|
||||
|
||||
struct SCommunityIcon
|
||||
{
|
||||
char m_aCommunityId[CServerInfo::MAX_COMMUNITY_ID_LENGTH];
|
||||
SHA256_DIGEST m_Sha256;
|
||||
IGraphics::CTextureHandle m_OrgTexture;
|
||||
IGraphics::CTextureHandle m_GreyTexture;
|
||||
};
|
||||
|
||||
class CMenus : public CComponent
|
||||
{
|
||||
static ColorRGBA ms_GuiColor;
|
||||
|
@ -61,7 +69,7 @@ class CMenus : public CComponent
|
|||
int DoButton_FontIcon(CButtonContainer *pButtonContainer, const char *pText, int Checked, const CUIRect *pRect, int Corners = IGraphics::CORNER_ALL, bool Enabled = true);
|
||||
int DoButton_Toggle(const void *pID, int Checked, const CUIRect *pRect, bool Active);
|
||||
int DoButton_Menu(CButtonContainer *pButtonContainer, const char *pText, int Checked, const CUIRect *pRect, const char *pImageName = nullptr, int Corners = IGraphics::CORNER_ALL, float Rounding = 5.0f, float FontFactor = 0.0f, ColorRGBA Color = ColorRGBA(1.0f, 1.0f, 1.0f, 0.5f));
|
||||
int DoButton_MenuTab(CButtonContainer *pButtonContainer, const char *pText, int Checked, const CUIRect *pRect, int Corners, SUIAnimator *pAnimator = nullptr, const ColorRGBA *pDefaultColor = nullptr, const ColorRGBA *pActiveColor = nullptr, const ColorRGBA *pHoverColor = nullptr, float EdgeRounding = 10.0f);
|
||||
int DoButton_MenuTab(CButtonContainer *pButtonContainer, const char *pText, int Checked, const CUIRect *pRect, int Corners, SUIAnimator *pAnimator = nullptr, const ColorRGBA *pDefaultColor = nullptr, const ColorRGBA *pActiveColor = nullptr, const ColorRGBA *pHoverColor = nullptr, float EdgeRounding = 10.0f, const SCommunityIcon *pCommunityIcon = nullptr);
|
||||
|
||||
int DoButton_CheckBox_Common(const void *pID, const char *pText, const char *pBoxText, const CUIRect *pRect);
|
||||
int DoButton_CheckBox(const void *pID, const char *pText, int Checked, const CUIRect *pRect);
|
||||
|
@ -72,6 +80,7 @@ class CMenus : public CComponent
|
|||
ColorHSLA DoButton_ColorPicker(const CUIRect *pRect, unsigned int *pHslaColor, bool Alpha);
|
||||
void DoLaserPreview(const CUIRect *pRect, ColorHSLA OutlineColor, ColorHSLA InnerColor, const int LaserType);
|
||||
int DoButton_GridHeader(const void *pID, const char *pText, int Checked, const CUIRect *pRect);
|
||||
int DoButton_Favorite(const void *pButtonId, const void *pParentId, bool Checked, const CUIRect *pRect);
|
||||
|
||||
int DoKeyReader(const void *pID, const CUIRect *pRect, int Key, int ModifierCombination, int *pNewModifierCombination);
|
||||
|
||||
|
@ -158,7 +167,9 @@ protected:
|
|||
int m_ActivePage;
|
||||
bool m_ShowStart;
|
||||
bool m_MenuActive;
|
||||
bool m_JoinTutorial;
|
||||
|
||||
bool m_JoinTutorial = false;
|
||||
bool m_CreateDefaultFavoriteCommunities = false;
|
||||
|
||||
char m_aNextServer[256];
|
||||
|
||||
|
@ -492,10 +503,12 @@ protected:
|
|||
static void ConchainFriendlistUpdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
|
||||
static void ConchainFavoritesUpdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
|
||||
static void ConchainCommunitiesUpdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
|
||||
static void ConchainUiPageUpdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
|
||||
struct SCommunityCache
|
||||
{
|
||||
int64_t m_UpdateTime = 0;
|
||||
bool m_PageWithCommunities;
|
||||
int m_LastPage = 0;
|
||||
unsigned m_SelectedCommunitiesHash;
|
||||
std::vector<const CCommunity *> m_vpSelectedCommunities;
|
||||
std::vector<const CCommunityCountry *> m_vpSelectableCountries;
|
||||
std::vector<const CCommunityType *> m_vpSelectableTypes;
|
||||
|
@ -521,7 +534,7 @@ protected:
|
|||
public:
|
||||
const char *CommunityId() const { return m_aCommunityId; }
|
||||
bool Success() const { return m_Success; }
|
||||
SHA256_DIGEST &&Sha256() { return std::move(m_Sha256); }
|
||||
const SHA256_DIGEST &Sha256() const { return m_Sha256; }
|
||||
};
|
||||
|
||||
class CCommunityIconLoadJob : public IJob, public CAbstractCommunityIconJob
|
||||
|
@ -535,7 +548,7 @@ protected:
|
|||
CCommunityIconLoadJob(CMenus *pMenus, const char *pCommunityId, int StorageType);
|
||||
~CCommunityIconLoadJob();
|
||||
|
||||
CImageInfo &&ImageInfo() { return std::move(m_ImageInfo); }
|
||||
CImageInfo &ImageInfo() { return m_ImageInfo; }
|
||||
};
|
||||
|
||||
class CCommunityIconDownloadJob : public CHttpRequest, public CAbstractCommunityIconJob
|
||||
|
@ -544,13 +557,6 @@ protected:
|
|||
CCommunityIconDownloadJob(CMenus *pMenus, const char *pCommunityId, const char *pUrl, const SHA256_DIGEST &Sha256);
|
||||
};
|
||||
|
||||
struct SCommunityIcon
|
||||
{
|
||||
char m_aCommunityId[CServerInfo::MAX_COMMUNITY_ID_LENGTH];
|
||||
SHA256_DIGEST m_Sha256;
|
||||
IGraphics::CTextureHandle m_OrgTexture;
|
||||
IGraphics::CTextureHandle m_GreyTexture;
|
||||
};
|
||||
std::vector<SCommunityIcon> m_vCommunityIcons;
|
||||
std::deque<std::shared_ptr<CCommunityIconLoadJob>> m_CommunityIconLoadJobs;
|
||||
std::deque<std::shared_ptr<CCommunityIconDownloadJob>> m_CommunityIconDownloadJobs;
|
||||
|
@ -558,7 +564,7 @@ protected:
|
|||
static int CommunityIconScan(const char *pName, int IsDir, int DirType, void *pUser);
|
||||
const SCommunityIcon *FindCommunityIcon(const char *pCommunityId);
|
||||
bool LoadCommunityIconFile(const char *pPath, int DirType, CImageInfo &Info, SHA256_DIGEST &Sha256);
|
||||
void LoadCommunityIconFinish(const char *pCommunityId, CImageInfo &&Info, SHA256_DIGEST &&Sha256);
|
||||
void LoadCommunityIconFinish(const char *pCommunityId, CImageInfo &Info, const SHA256_DIGEST &Sha256);
|
||||
void RenderCommunityIcon(const SCommunityIcon *pIcon, CUIRect Rect, bool Active);
|
||||
void UpdateCommunityIcons();
|
||||
|
||||
|
@ -633,8 +639,9 @@ public:
|
|||
PAGE_INTERNET,
|
||||
PAGE_LAN,
|
||||
PAGE_FAVORITES,
|
||||
PAGE_DDNET_LEGACY, // removed, redirects to PAGE_INTERNET
|
||||
PAGE_KOG_LEGACY, // removed, redirects to PAGE_INTERNET
|
||||
PAGE_FAVORITE_COMMUNITY_1,
|
||||
PAGE_FAVORITE_COMMUNITY_2,
|
||||
PAGE_FAVORITE_COMMUNITY_3,
|
||||
PAGE_DEMOS,
|
||||
PAGE_SETTINGS,
|
||||
PAGE_NETWORK,
|
||||
|
@ -659,6 +666,9 @@ public:
|
|||
BIG_TAB_INTERNET,
|
||||
BIG_TAB_LAN,
|
||||
BIG_TAB_FAVORITES,
|
||||
BIT_TAB_FAVORITE_COMMUNITY_1,
|
||||
BIT_TAB_FAVORITE_COMMUNITY_2,
|
||||
BIT_TAB_FAVORITE_COMMUNITY_3,
|
||||
BIG_TAB_DEMOS,
|
||||
|
||||
BIG_TAB_LENGTH,
|
||||
|
|
|
@ -701,21 +701,6 @@ void CMenus::RenderServerbrowserFilters(CUIRect View)
|
|||
if(DoButton_CheckBox(&g_Config.m_BrFilterConnectingPlayers, Localize("Filter connecting players"), g_Config.m_BrFilterConnectingPlayers, &Button))
|
||||
g_Config.m_BrFilterConnectingPlayers ^= 1;
|
||||
|
||||
// community filter
|
||||
if((g_Config.m_UiPage == PAGE_INTERNET || g_Config.m_UiPage == PAGE_FAVORITES) && !ServerBrowser()->Communities().empty())
|
||||
{
|
||||
CUIRect Row;
|
||||
View.HSplitTop(6.0f, nullptr, &View);
|
||||
View.HSplitTop(19.0f, &Row, &View);
|
||||
Row.Draw(ColorRGBA(0.0f, 0.0f, 0.0f, 0.3f), IGraphics::CORNER_T, 4.0f);
|
||||
UI()->DoLabel(&Row, Localize("Communities"), 12.0f, TEXTALIGN_MC);
|
||||
|
||||
View.HSplitTop(4.0f * 17.0f + CScrollRegion::HEIGHT_MAGIC_FIX, &Row, &View);
|
||||
View.HSplitTop(3.0f, nullptr, &View);
|
||||
Row.Draw(ColorRGBA(0.0f, 0.0f, 0.0f, 0.15f), IGraphics::CORNER_B, 4.0f);
|
||||
RenderServerbrowserCommunitiesFilter(Row);
|
||||
}
|
||||
|
||||
// map finish filters
|
||||
if(m_CommunityCache.m_AnyRanksAvailable)
|
||||
{
|
||||
|
@ -801,15 +786,26 @@ void CMenus::ResetServerbrowserFilters()
|
|||
g_Config.m_BrFilterGametype[0] = '\0';
|
||||
g_Config.m_BrFilterGametypeStrict = 0;
|
||||
g_Config.m_BrFilterConnectingPlayers = 1;
|
||||
g_Config.m_BrFilterUnfinishedMap = 0;
|
||||
g_Config.m_BrFilterServerAddress[0] = '\0';
|
||||
ConfigManager()->Reset("br_filter_exclude_communities");
|
||||
ConfigManager()->Reset("br_filter_exclude_countries");
|
||||
ConfigManager()->Reset("br_filter_exclude_types");
|
||||
Client()->ServerBrowserUpdate();
|
||||
|
||||
if(g_Config.m_UiPage != PAGE_LAN)
|
||||
{
|
||||
if(m_CommunityCache.m_AnyRanksAvailable)
|
||||
{
|
||||
g_Config.m_BrFilterUnfinishedMap = 0;
|
||||
}
|
||||
if(g_Config.m_UiPage == PAGE_INTERNET || g_Config.m_UiPage == PAGE_FAVORITES)
|
||||
{
|
||||
ServerBrowser()->CommunitiesFilter().Clear();
|
||||
}
|
||||
ServerBrowser()->CountriesFilter().Clear();
|
||||
ServerBrowser()->TypesFilter().Clear();
|
||||
UpdateCommunityCache(true);
|
||||
}
|
||||
|
||||
Client()->ServerBrowserUpdate();
|
||||
}
|
||||
|
||||
void CMenus::RenderServerbrowserDDNetFilter(CUIRect View,
|
||||
IFilterList &Filter,
|
||||
float ItemHeight, int MaxItems, int ItemsPerRow,
|
||||
|
@ -914,11 +910,18 @@ void CMenus::RenderServerbrowserDDNetFilter(CUIRect View,
|
|||
|
||||
void CMenus::RenderServerbrowserCommunitiesFilter(CUIRect View)
|
||||
{
|
||||
CUIRect Tab;
|
||||
View.HSplitTop(19.0f, &Tab, &View);
|
||||
Tab.Draw(ColorRGBA(0.0f, 0.0f, 0.0f, 0.3f), IGraphics::CORNER_T, 4.0f);
|
||||
UI()->DoLabel(&Tab, Localize("Communities"), 12.0f, TEXTALIGN_MC);
|
||||
View.Draw(ColorRGBA(0.0f, 0.0f, 0.0f, 0.15f), IGraphics::CORNER_B, 4.0f);
|
||||
|
||||
const int MaxEntries = ServerBrowser()->Communities().size();
|
||||
const int EntriesPerRow = 1;
|
||||
|
||||
static CScrollRegion s_ScrollRegion;
|
||||
static std::vector<unsigned char> s_vItemIds;
|
||||
static std::vector<unsigned char> s_vFavoriteButtonIds;
|
||||
|
||||
const float ItemHeight = 13.0f;
|
||||
const float Spacing = 2.0f;
|
||||
|
@ -932,12 +935,14 @@ void CMenus::RenderServerbrowserCommunitiesFilter(CUIRect View)
|
|||
const auto &&RenderItem = [&](int ItemIndex, CUIRect Item, const void *pItemId, bool Active) {
|
||||
const float Alpha = (Active ? 0.9f : 0.2f) + (UI()->HotItem() == pItemId ? 0.1f : 0.0f);
|
||||
|
||||
CUIRect Icon, Label;
|
||||
CUIRect Icon, Label, FavoriteButton;
|
||||
Item.VSplitRight(Item.h, &Item, &FavoriteButton);
|
||||
Item.Margin(Spacing, &Item);
|
||||
Item.VSplitLeft(Item.h * 2.0f, &Icon, &Label);
|
||||
Label.VSplitLeft(Spacing, nullptr, &Label);
|
||||
|
||||
const SCommunityIcon *pIcon = FindCommunityIcon(GetItemName(ItemIndex));
|
||||
const char *pItemName = GetItemName(ItemIndex);
|
||||
const SCommunityIcon *pIcon = FindCommunityIcon(pItemName);
|
||||
if(pIcon != nullptr)
|
||||
{
|
||||
RenderCommunityIcon(pIcon, Icon, Active);
|
||||
|
@ -946,8 +951,22 @@ void CMenus::RenderServerbrowserCommunitiesFilter(CUIRect View)
|
|||
TextRender()->TextColor(1.0f, 1.0f, 1.0f, Alpha);
|
||||
UI()->DoLabel(&Label, GetItemDisplayName(ItemIndex), Label.h * CUI::ms_FontmodHeight, TEXTALIGN_ML);
|
||||
TextRender()->TextColor(TextRender()->DefaultTextColor());
|
||||
|
||||
const bool Favorite = ServerBrowser()->FavoriteCommunitiesFilter().Filtered(pItemName);
|
||||
if(DoButton_Favorite(&s_vFavoriteButtonIds[ItemIndex], pItemId, Favorite, &FavoriteButton))
|
||||
{
|
||||
if(Favorite)
|
||||
{
|
||||
ServerBrowser()->FavoriteCommunitiesFilter().Remove(pItemName);
|
||||
}
|
||||
else
|
||||
{
|
||||
ServerBrowser()->FavoriteCommunitiesFilter().Add(pItemName);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
s_vFavoriteButtonIds.resize(MaxEntries);
|
||||
RenderServerbrowserDDNetFilter(View, ServerBrowser()->CommunitiesFilter(), ItemHeight + 2.0f * Spacing, MaxEntries, EntriesPerRow, s_ScrollRegion, s_vItemIds, true, GetItemName, RenderItem);
|
||||
}
|
||||
|
||||
|
@ -1656,17 +1675,16 @@ void CMenus::RenderServerbrowserToolBox(CUIRect ToolBox)
|
|||
void CMenus::RenderServerbrowser(CUIRect MainView)
|
||||
{
|
||||
UpdateCommunityCache(false);
|
||||
UpdateCommunityIcons();
|
||||
|
||||
/*
|
||||
+-----------------+ +--tabs--+
|
||||
+---------------------------+ +---communities---+
|
||||
| | | |
|
||||
| | | |
|
||||
| server list | | tool |
|
||||
| | +------tabs-------+
|
||||
| server list | | |
|
||||
| | | tool |
|
||||
| | | box |
|
||||
| | | |
|
||||
+-----------------+ | |
|
||||
status box +--------+
|
||||
+---------------------------+ | |
|
||||
status box +-----------------+
|
||||
*/
|
||||
|
||||
CUIRect ServerList, StatusBox, ToolBox, TabBar;
|
||||
|
@ -1674,6 +1692,15 @@ void CMenus::RenderServerbrowser(CUIRect MainView)
|
|||
MainView.Margin(10.0f, &MainView);
|
||||
MainView.VSplitRight(205.0f, &ServerList, &ToolBox);
|
||||
ServerList.VSplitRight(5.0f, &ServerList, nullptr);
|
||||
|
||||
if((g_Config.m_UiPage == PAGE_INTERNET || g_Config.m_UiPage == PAGE_FAVORITES) && !ServerBrowser()->Communities().empty())
|
||||
{
|
||||
CUIRect CommunityFilter;
|
||||
ToolBox.HSplitTop(19.0f + 4.0f * 17.0f + CScrollRegion::HEIGHT_MAGIC_FIX, &CommunityFilter, &ToolBox);
|
||||
ToolBox.HSplitTop(8.0f, nullptr, &ToolBox);
|
||||
RenderServerbrowserCommunitiesFilter(CommunityFilter);
|
||||
}
|
||||
|
||||
ToolBox.HSplitTop(24.0f, &TabBar, &ToolBox);
|
||||
ServerList.HSplitBottom(65.0f, &ServerList, &StatusBox);
|
||||
|
||||
|
@ -1762,28 +1789,70 @@ void CMenus::ConchainCommunitiesUpdate(IConsole::IResult *pResult, void *pUserDa
|
|||
{
|
||||
pfnCallback(pResult, pCallbackUserData);
|
||||
CMenus *pThis = static_cast<CMenus *>(pUserData);
|
||||
if(pResult->NumArguments() >= 1 && (g_Config.m_UiPage == PAGE_INTERNET || g_Config.m_UiPage == PAGE_FAVORITES))
|
||||
if(pResult->NumArguments() >= 1 && (g_Config.m_UiPage == PAGE_INTERNET || g_Config.m_UiPage == PAGE_FAVORITES || (g_Config.m_UiPage >= PAGE_FAVORITE_COMMUNITY_1 && g_Config.m_UiPage <= PAGE_FAVORITE_COMMUNITY_3)))
|
||||
{
|
||||
pThis->UpdateCommunityCache(true);
|
||||
pThis->Client()->ServerBrowserUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
void CMenus::ConchainUiPageUpdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData)
|
||||
{
|
||||
const int OldPage = g_Config.m_UiPage;
|
||||
pfnCallback(pResult, pCallbackUserData);
|
||||
CMenus *pThis = static_cast<CMenus *>(pUserData);
|
||||
if(pResult->NumArguments() >= 1)
|
||||
{
|
||||
if(g_Config.m_UiPage >= PAGE_FAVORITE_COMMUNITY_1 && g_Config.m_UiPage <= PAGE_FAVORITE_COMMUNITY_3 &&
|
||||
(size_t)(g_Config.m_UiPage - PAGE_FAVORITE_COMMUNITY_1) >= pThis->ServerBrowser()->FavoriteCommunities().size())
|
||||
{
|
||||
// Reset page to internet when there is no favorite community for this page.
|
||||
g_Config.m_UiPage = PAGE_INTERNET;
|
||||
}
|
||||
|
||||
pThis->SetMenuPage(g_Config.m_UiPage);
|
||||
|
||||
if(!pThis->m_ShowStart && g_Config.m_UiPage != OldPage)
|
||||
{
|
||||
pThis->RefreshBrowserTab(g_Config.m_UiPage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CMenus::UpdateCommunityCache(bool Force)
|
||||
{
|
||||
const bool PageWithCommunities = g_Config.m_UiPage == PAGE_INTERNET || g_Config.m_UiPage == PAGE_FAVORITES;
|
||||
if(!Force && m_CommunityCache.m_UpdateTime != 0 && m_CommunityCache.m_UpdateTime == ServerBrowser()->DDNetInfoUpdateTime() && m_CommunityCache.m_PageWithCommunities == PageWithCommunities)
|
||||
if(g_Config.m_UiPage >= PAGE_FAVORITE_COMMUNITY_1 && g_Config.m_UiPage <= PAGE_FAVORITE_COMMUNITY_3 &&
|
||||
(size_t)(g_Config.m_UiPage - PAGE_FAVORITE_COMMUNITY_1) >= ServerBrowser()->FavoriteCommunities().size())
|
||||
{
|
||||
// Reset page to internet when there is no favorite community for this page,
|
||||
// i.e. when favorite community is removed via console while the page is open.
|
||||
SetMenuPage(PAGE_INTERNET);
|
||||
RefreshBrowserTab(g_Config.m_UiPage);
|
||||
}
|
||||
|
||||
const unsigned CommunitiesHash = ServerBrowser()->CurrentCommunitiesHash();
|
||||
const bool PageChanged = m_CommunityCache.m_LastPage != 0 && m_CommunityCache.m_LastPage != g_Config.m_UiPage;
|
||||
const bool CurrentCommunitiesChanged = m_CommunityCache.m_LastPage != 0 && m_CommunityCache.m_LastPage == g_Config.m_UiPage && m_CommunityCache.m_SelectedCommunitiesHash != CommunitiesHash;
|
||||
if(CurrentCommunitiesChanged && g_Config.m_UiPage >= PAGE_FAVORITE_COMMUNITY_1 && g_Config.m_UiPage <= PAGE_FAVORITE_COMMUNITY_3)
|
||||
{
|
||||
// Favorite community was changed while its page is active,
|
||||
// refresh to get correct serverlist for updated community.
|
||||
ServerBrowser()->Refresh(g_Config.m_UiPage - PAGE_FAVORITE_COMMUNITY_1 + IServerBrowser::TYPE_FAVORITE_COMMUNITY_1, true);
|
||||
}
|
||||
|
||||
if(!Force && m_CommunityCache.m_UpdateTime != 0 &&
|
||||
m_CommunityCache.m_UpdateTime == ServerBrowser()->DDNetInfoUpdateTime() &&
|
||||
!CurrentCommunitiesChanged && !PageChanged)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ServerBrowser()->CleanFilters();
|
||||
|
||||
m_CommunityCache.m_UpdateTime = ServerBrowser()->DDNetInfoUpdateTime();
|
||||
m_CommunityCache.m_PageWithCommunities = PageWithCommunities;
|
||||
|
||||
if(m_CommunityCache.m_PageWithCommunities)
|
||||
m_CommunityCache.m_vpSelectedCommunities = ServerBrowser()->SelectedCommunities();
|
||||
else
|
||||
m_CommunityCache.m_vpSelectedCommunities.clear();
|
||||
m_CommunityCache.m_LastPage = g_Config.m_UiPage;
|
||||
m_CommunityCache.m_SelectedCommunitiesHash = CommunitiesHash;
|
||||
m_CommunityCache.m_vpSelectedCommunities = ServerBrowser()->CurrentCommunities();
|
||||
|
||||
m_CommunityCache.m_vpSelectableCountries.clear();
|
||||
m_CommunityCache.m_vpSelectableTypes.clear();
|
||||
|
@ -1866,7 +1935,7 @@ int CMenus::CommunityIconScan(const char *pName, int IsDir, int DirType, void *p
|
|||
return 0;
|
||||
}
|
||||
|
||||
const CMenus::SCommunityIcon *CMenus::FindCommunityIcon(const char *pCommunityId)
|
||||
const SCommunityIcon *CMenus::FindCommunityIcon(const char *pCommunityId)
|
||||
{
|
||||
auto Icon = std::find_if(m_vCommunityIcons.begin(), m_vCommunityIcons.end(), [pCommunityId](const SCommunityIcon &Element) {
|
||||
return str_comp(Element.m_aCommunityId, pCommunityId) == 0;
|
||||
|
@ -1900,7 +1969,7 @@ bool CMenus::LoadCommunityIconFile(const char *pPath, int DirType, CImageInfo &I
|
|||
return true;
|
||||
}
|
||||
|
||||
void CMenus::LoadCommunityIconFinish(const char *pCommunityId, CImageInfo &&Info, SHA256_DIGEST &&Sha256)
|
||||
void CMenus::LoadCommunityIconFinish(const char *pCommunityId, CImageInfo &Info, const SHA256_DIGEST &Sha256)
|
||||
{
|
||||
SCommunityIcon CommunityIcon;
|
||||
str_copy(CommunityIcon.m_aCommunityId, pCommunityId);
|
||||
|
@ -1979,14 +2048,14 @@ void CMenus::UpdateCommunityIcons()
|
|||
{
|
||||
std::shared_ptr<CCommunityIconLoadJob> pLoadJob = std::make_shared<CCommunityIconLoadJob>(this, pJob->CommunityId(), IStorage::TYPE_SAVE);
|
||||
Engine()->AddJob(pLoadJob);
|
||||
m_CommunityIconLoadJobs.emplace_back(std::move(pLoadJob));
|
||||
m_CommunityIconLoadJobs.push_back(pLoadJob);
|
||||
}
|
||||
m_CommunityIconDownloadJobs.pop_front();
|
||||
}
|
||||
}
|
||||
|
||||
// Rescan for changed communities only when necessary
|
||||
if(m_CommunityIconsUpdateTime != 0 && m_CommunityIconsUpdateTime == ServerBrowser()->DDNetInfoUpdateTime())
|
||||
if(!ServerBrowser()->DDNetInfoAvailable() || (m_CommunityIconsUpdateTime != 0 && m_CommunityIconsUpdateTime == ServerBrowser()->DDNetInfoUpdateTime()))
|
||||
return;
|
||||
m_CommunityIconsUpdateTime = ServerBrowser()->DDNetInfoUpdateTime();
|
||||
|
||||
|
|
|
@ -837,9 +837,9 @@ void CMenus::RenderInGameNetwork(CUIRect MainView)
|
|||
static CButtonContainer s_InternetButton;
|
||||
if(DoButton_MenuTab(&s_InternetButton, FONT_ICON_EARTH_AMERICAS, g_Config.m_UiPage == PAGE_INTERNET, &Button, IGraphics::CORNER_NONE))
|
||||
{
|
||||
if(g_Config.m_UiPage != PAGE_INTERNET)
|
||||
if(ServerBrowser()->GetCurrentType() != IServerBrowser::TYPE_INTERNET)
|
||||
{
|
||||
if(g_Config.m_UiPage != PAGE_FAVORITES)
|
||||
if(ServerBrowser()->GetCurrentType() == IServerBrowser::TYPE_LAN)
|
||||
Client()->RequestDDNetInfo();
|
||||
ServerBrowser()->Refresh(IServerBrowser::TYPE_INTERNET);
|
||||
}
|
||||
|
@ -851,7 +851,7 @@ void CMenus::RenderInGameNetwork(CUIRect MainView)
|
|||
static CButtonContainer s_LanButton;
|
||||
if(DoButton_MenuTab(&s_LanButton, FONT_ICON_NETWORK_WIRED, g_Config.m_UiPage == PAGE_LAN, &Button, IGraphics::CORNER_NONE))
|
||||
{
|
||||
if(g_Config.m_UiPage != PAGE_LAN)
|
||||
if(ServerBrowser()->GetCurrentType() != IServerBrowser::TYPE_LAN)
|
||||
ServerBrowser()->Refresh(IServerBrowser::TYPE_LAN);
|
||||
NewPage = PAGE_LAN;
|
||||
}
|
||||
|
@ -861,9 +861,9 @@ void CMenus::RenderInGameNetwork(CUIRect MainView)
|
|||
static CButtonContainer s_FavoritesButton;
|
||||
if(DoButton_MenuTab(&s_FavoritesButton, FONT_ICON_STAR, g_Config.m_UiPage == PAGE_FAVORITES, &Button, IGraphics::CORNER_NONE))
|
||||
{
|
||||
if(g_Config.m_UiPage != PAGE_FAVORITES)
|
||||
if(ServerBrowser()->GetCurrentType() != IServerBrowser::TYPE_FAVORITES)
|
||||
{
|
||||
if(g_Config.m_UiPage != PAGE_INTERNET)
|
||||
if(ServerBrowser()->GetCurrentType() == IServerBrowser::TYPE_LAN)
|
||||
Client()->RequestDDNetInfo();
|
||||
ServerBrowser()->Refresh(IServerBrowser::TYPE_FAVORITES);
|
||||
}
|
||||
|
@ -871,6 +871,31 @@ void CMenus::RenderInGameNetwork(CUIRect MainView)
|
|||
}
|
||||
GameClient()->m_Tooltips.DoToolTip(&s_FavoritesButton, &Button, Localize("Favorites"));
|
||||
|
||||
size_t FavoriteCommunityIndex = 0;
|
||||
static CButtonContainer s_aFavoriteCommunityButtons[3];
|
||||
static_assert(std::size(s_aFavoriteCommunityButtons) == (size_t)PAGE_FAVORITE_COMMUNITY_3 - PAGE_FAVORITE_COMMUNITY_1 + 1);
|
||||
for(const CCommunity *pCommunity : ServerBrowser()->FavoriteCommunities())
|
||||
{
|
||||
TabBar.VSplitLeft(75.0f, &Button, &TabBar);
|
||||
const int Page = PAGE_FAVORITE_COMMUNITY_1 + FavoriteCommunityIndex;
|
||||
if(DoButton_MenuTab(&s_aFavoriteCommunityButtons[FavoriteCommunityIndex], FONT_ICON_ELLIPSIS, g_Config.m_UiPage == Page, &Button, IGraphics::CORNER_NONE, nullptr, nullptr, nullptr, nullptr, 10.0f, FindCommunityIcon(pCommunity->Id())))
|
||||
{
|
||||
const int BrowserType = IServerBrowser::TYPE_FAVORITE_COMMUNITY_1 + FavoriteCommunityIndex;
|
||||
if(ServerBrowser()->GetCurrentType() != BrowserType)
|
||||
{
|
||||
if(ServerBrowser()->GetCurrentType() == IServerBrowser::TYPE_LAN)
|
||||
Client()->RequestDDNetInfo();
|
||||
ServerBrowser()->Refresh(BrowserType);
|
||||
}
|
||||
NewPage = Page;
|
||||
}
|
||||
GameClient()->m_Tooltips.DoToolTip(&s_aFavoriteCommunityButtons[FavoriteCommunityIndex], &Button, pCommunity->Name());
|
||||
|
||||
++FavoriteCommunityIndex;
|
||||
if(FavoriteCommunityIndex >= std::size(s_aFavoriteCommunityButtons))
|
||||
break;
|
||||
}
|
||||
|
||||
TextRender()->SetRenderFlags(0);
|
||||
TextRender()->SetFontPreset(EFontPreset::DEFAULT_FONT);
|
||||
|
||||
|
|
|
@ -801,19 +801,6 @@ void CMenus::RenderSettingsTee(CUIRect MainView)
|
|||
m_SkinListNeedsUpdate = false;
|
||||
}
|
||||
|
||||
const auto &&RenderFavIcon = [&](const CUIRect &FavIcon, bool AsFav, bool Hot) {
|
||||
TextRender()->SetFontPreset(EFontPreset::ICON_FONT);
|
||||
TextRender()->SetRenderFlags(ETextRenderFlags::TEXT_RENDER_FLAG_ONLY_ADVANCE_WIDTH | ETextRenderFlags::TEXT_RENDER_FLAG_NO_X_BEARING | ETextRenderFlags::TEXT_RENDER_FLAG_NO_Y_BEARING | ETextRenderFlags::TEXT_RENDER_FLAG_NO_PIXEL_ALIGMENT | ETextRenderFlags::TEXT_RENDER_FLAG_NO_OVERSIZE);
|
||||
TextRender()->TextColor(AsFav ? ColorRGBA(1.0f, 0.85f, 0.3f, 0.8f + (Hot ? 0.2f : 0.0f)) : ColorRGBA(0.5f, 0.5f, 0.5f, 0.8f + (Hot ? 0.2f : 0.0f)));
|
||||
TextRender()->TextOutlineColor(TextRender()->DefaultTextOutlineColor());
|
||||
SLabelProperties Props;
|
||||
Props.m_MaxWidth = FavIcon.w;
|
||||
UI()->DoLabel(&FavIcon, FONT_ICON_STAR, 12.0f, TEXTALIGN_MR, Props);
|
||||
TextRender()->TextColor(TextRender()->DefaultTextColor());
|
||||
TextRender()->SetRenderFlags(0);
|
||||
TextRender()->SetFontPreset(EFontPreset::DEFAULT_FONT);
|
||||
};
|
||||
|
||||
int OldSelected = -1;
|
||||
s_ListBox.DoStart(50.0f, s_vSkinList.size(), 4, 1, OldSelected, &MainView);
|
||||
for(size_t i = 0; i < s_vSkinList.size(); ++i)
|
||||
|
@ -861,11 +848,7 @@ void CMenus::RenderSettingsTee(CUIRect MainView)
|
|||
CUIRect FavIcon;
|
||||
Item.m_Rect.HSplitTop(20.0f, &FavIcon, nullptr);
|
||||
FavIcon.VSplitRight(20.0f, nullptr, &FavIcon);
|
||||
if(IsFav || UI()->HotItem() == pSkinToBeDraw || UI()->HotItem() == &pSkinToBeDraw->m_Metrics.m_Body)
|
||||
{
|
||||
RenderFavIcon(FavIcon, IsFav, UI()->HotItem() == &pSkinToBeDraw->m_Metrics.m_Body);
|
||||
}
|
||||
if(UI()->DoButtonLogic(&pSkinToBeDraw->m_Metrics.m_Body, 0, &FavIcon))
|
||||
if(DoButton_Favorite(&pSkinToBeDraw->m_Metrics.m_Body, pSkinToBeDraw, IsFav, &FavIcon))
|
||||
{
|
||||
if(IsFav)
|
||||
{
|
||||
|
|
|
@ -67,6 +67,10 @@ void CMenus::RenderStartMenu(CUIRect MainView)
|
|||
if(DoButton_Menu(&s_TutorialButton, Localize("Tutorial"), 0, &Button, 0, IGraphics::CORNER_ALL, 5.0f, 0.0f, ColorRGBA(0.0f, 0.0f, 0.0f, 0.25f)) ||
|
||||
(s_JoinTutorialTime != 0.0f && Client()->LocalTime() >= s_JoinTutorialTime))
|
||||
{
|
||||
// Activate internet tab before joining tutorial to make sure the server info
|
||||
// for the tutorial servers is available.
|
||||
SetMenuPage(PAGE_INTERNET);
|
||||
RefreshBrowserTab(IServerBrowser::TYPE_INTERNET);
|
||||
const char *pAddr = ServerBrowser()->GetTutorialServer();
|
||||
if(pAddr)
|
||||
{
|
||||
|
@ -187,12 +191,11 @@ void CMenus::RenderStartMenu(CUIRect MainView)
|
|||
static CButtonContainer s_PlayButton;
|
||||
if(DoButton_Menu(&s_PlayButton, Localize("Play", "Start menu"), 0, &Button, g_Config.m_ClShowStartMenuImages ? "play_game" : 0, IGraphics::CORNER_ALL, Rounding, 0.5f, ColorRGBA(0.0f, 0.0f, 0.0f, 0.25f)) || UI()->ConsumeHotkey(CUI::HOTKEY_ENTER) || CheckHotKey(KEY_P))
|
||||
{
|
||||
NewPage = g_Config.m_UiPage >= PAGE_INTERNET && g_Config.m_UiPage <= PAGE_FAVORITES ? g_Config.m_UiPage : PAGE_INTERNET;
|
||||
NewPage = g_Config.m_UiPage >= PAGE_INTERNET && g_Config.m_UiPage <= PAGE_FAVORITE_COMMUNITY_3 ? g_Config.m_UiPage : PAGE_INTERNET;
|
||||
}
|
||||
|
||||
// render version
|
||||
CUIRect VersionUpdate, CurVersion;
|
||||
MainView.HSplitBottom(30.0f, 0, 0);
|
||||
MainView.HSplitBottom(20.0f, 0, &VersionUpdate);
|
||||
|
||||
VersionUpdate.VSplitRight(50.0f, &CurVersion, 0);
|
||||
|
|
|
@ -12,7 +12,13 @@ int DilateFile(const char *pFilename)
|
|||
if(File)
|
||||
{
|
||||
io_seek(File, 0, IOSEEK_END);
|
||||
unsigned int FileSize = io_tell(File);
|
||||
long int FileSize = io_tell(File);
|
||||
if(FileSize <= 0)
|
||||
{
|
||||
io_close(File);
|
||||
dbg_msg("dilate", "failed to get file size (%ld). filename='%s'", FileSize, pFilename);
|
||||
return false;
|
||||
}
|
||||
io_seek(File, 0, IOSEEK_START);
|
||||
TImageByteBuffer ByteBuffer;
|
||||
SImageByteBuffer ImageByteBuffer(&ByteBuffer);
|
||||
|
|
|
@ -31,7 +31,13 @@ int LoadPNG(CImageInfo *pImg, const char *pFilename)
|
|||
if(File)
|
||||
{
|
||||
io_seek(File, 0, IOSEEK_END);
|
||||
unsigned int FileSize = io_tell(File);
|
||||
long int FileSize = io_tell(File);
|
||||
if(FileSize <= 0)
|
||||
{
|
||||
io_close(File);
|
||||
dbg_msg("map_convert_07", "failed to get file size (%ld). filename='%s'", FileSize, pFilename);
|
||||
return false;
|
||||
}
|
||||
io_seek(File, 0, IOSEEK_START);
|
||||
TImageByteBuffer ByteBuffer;
|
||||
SImageByteBuffer ImageByteBuffer(&ByteBuffer);
|
||||
|
|
|
@ -319,7 +319,13 @@ bool LoadPNG(CImageInfo *pImg, const char *pFilename)
|
|||
}
|
||||
|
||||
io_seek(File, 0, IOSEEK_END);
|
||||
unsigned int FileSize = io_tell(File);
|
||||
long int FileSize = io_tell(File);
|
||||
if(FileSize <= 0)
|
||||
{
|
||||
io_close(File);
|
||||
dbg_msg("map_create_pixelart", "ERROR: Failed to get file size (%ld). filename='%s'", FileSize, pFilename);
|
||||
return false;
|
||||
}
|
||||
io_seek(File, 0, IOSEEK_START);
|
||||
TImageByteBuffer ByteBuffer;
|
||||
SImageByteBuffer ImageByteBuffer(&ByteBuffer);
|
||||
|
|
|
@ -29,7 +29,12 @@ bool LoadPNG(CImageInfo *pImg, const char *pFilename)
|
|||
if(File)
|
||||
{
|
||||
io_seek(File, 0, IOSEEK_END);
|
||||
unsigned int FileSize = io_tell(File);
|
||||
long int FileSize = io_tell(File);
|
||||
if(FileSize <= 0)
|
||||
{
|
||||
io_close(File);
|
||||
return false;
|
||||
}
|
||||
io_seek(File, 0, IOSEEK_START);
|
||||
TImageByteBuffer ByteBuffer;
|
||||
SImageByteBuffer ImageByteBuffer(&ByteBuffer);
|
||||
|
|
Loading…
Reference in a new issue