Track country/type filters separately for internet/favorites tabs

For the internet/favorite tabs, instead of combining the country/type filters of all communities in one view, track the country/type filters separately for these tabs using the new, reserved community name `all`. This should make the filters less confusing to use, as changing the country/type filters in one tab will not influence the other tabs anymore. Though the country/type filters of the internet and favorite tabs are still combined, as this is also the case for the community filter.

However, this made it possible to select country/type filters that exclude all servers, by first excluding some countries/types and then changing the selected communities so all selectable countries/types are excluded. To prevent this, the filters will now include all countries/types, if they would otherwise exclude all selectable countries/types.

To do this more efficiently, the community cache is moved from the menus to the engine serverbrowser. To avoid using the UI page in the engine serverbrowser, the serverbrowser type is instead used to detect if the community cache should be updated. This required additional changes in the menus to ensure that the UI page and the serverbrowser type stay in sync with each other, which would otherwise cause incorrect server entries to be shown for one frame when switching tabs. The serverbrowser type is now refreshed immediately when the menu page is changed with the `CMenus::SetMenuPage` function, which allowed removing duplicate code for the server browser tab buttons. The `CMenus::RefreshBrowserTab` function does not take the page to be refreshed as argument anymore, as it always was only used to refresh the current page. Instead, a `bool` argument is used to specify whether the refresh should be forced even if the server browser type has not changed.

Closes #8158.
This commit is contained in:
Robert Müller 2024-04-10 21:40:02 +02:00
parent 394ed87ac9
commit 01a995e5ec
8 changed files with 323 additions and 219 deletions

View file

@ -53,8 +53,9 @@ bool matchesExactly(const char *a, const char *b)
} }
CServerBrowser::CServerBrowser() : CServerBrowser::CServerBrowser() :
m_CountriesFilter([this]() { return CurrentCommunities(); }), m_CommunityCache(this),
m_TypesFilter([this]() { return CurrentCommunities(); }) m_CountriesFilter(&m_CommunityCache),
m_TypesFilter(&m_CommunityCache)
{ {
m_ppServerlist = nullptr; m_ppServerlist = nullptr;
m_pSortedServerlist = nullptr; m_pSortedServerlist = nullptr;
@ -1728,6 +1729,78 @@ unsigned CServerBrowser::CurrentCommunitiesHash() const
return Hash; return Hash;
} }
void CCommunityCache::Update(bool Force)
{
const unsigned CommunitiesHash = m_pServerBrowser->CurrentCommunitiesHash();
const bool TypeChanged = m_LastType != m_pServerBrowser->GetCurrentType();
const bool CurrentCommunitiesChanged = m_LastType == m_pServerBrowser->GetCurrentType() && m_SelectedCommunitiesHash != CommunitiesHash;
if(CurrentCommunitiesChanged && m_pServerBrowser->GetCurrentType() >= IServerBrowser::TYPE_FAVORITE_COMMUNITY_1 && m_pServerBrowser->GetCurrentType() <= IServerBrowser::TYPE_FAVORITE_COMMUNITY_5)
{
// Favorite community was changed while its type is active,
// refresh to get correct serverlist for updated community.
m_pServerBrowser->Refresh(m_pServerBrowser->GetCurrentType(), true);
}
if(!Force && m_InfoSha256 == m_pServerBrowser->DDNetInfoSha256() &&
!CurrentCommunitiesChanged && !TypeChanged)
{
return;
}
m_InfoSha256 = m_pServerBrowser->DDNetInfoSha256();
m_LastType = m_pServerBrowser->GetCurrentType();
m_SelectedCommunitiesHash = CommunitiesHash;
m_vpSelectedCommunities = m_pServerBrowser->CurrentCommunities();
m_vpSelectableCountries.clear();
m_vpSelectableTypes.clear();
for(const CCommunity *pCommunity : m_vpSelectedCommunities)
{
for(const auto &Country : pCommunity->Countries())
{
const auto ExistingCountry = std::find_if(m_vpSelectableCountries.begin(), m_vpSelectableCountries.end(), [&](const CCommunityCountry *pOther) {
return str_comp(Country.Name(), pOther->Name()) == 0 && Country.FlagId() == pOther->FlagId();
});
if(ExistingCountry == m_vpSelectableCountries.end())
{
m_vpSelectableCountries.push_back(&Country);
}
}
for(const auto &Type : pCommunity->Types())
{
const auto ExistingType = std::find_if(m_vpSelectableTypes.begin(), m_vpSelectableTypes.end(), [&](const CCommunityType *pOther) {
return str_comp(Type.Name(), pOther->Name()) == 0;
});
if(ExistingType == m_vpSelectableTypes.end())
{
m_vpSelectableTypes.push_back(&Type);
}
}
}
m_AnyRanksAvailable = std::any_of(m_vpSelectedCommunities.begin(), m_vpSelectedCommunities.end(), [](const CCommunity *pCommunity) {
return pCommunity->HasRanks();
});
// Country/type filters not shown if there are no countries and types, or if only the none-community is selected
m_CountryTypesFilterAvailable = (!m_vpSelectableCountries.empty() || !m_vpSelectableTypes.empty()) &&
(m_vpSelectedCommunities.size() != 1 || str_comp(m_vpSelectedCommunities[0]->Id(), IServerBrowser::COMMUNITY_NONE) != 0);
if(m_pServerBrowser->GetCurrentType() >= IServerBrowser::TYPE_FAVORITE_COMMUNITY_1 && m_pServerBrowser->GetCurrentType() <= IServerBrowser::TYPE_FAVORITE_COMMUNITY_5)
{
const size_t CommunityIndex = m_pServerBrowser->GetCurrentType() - IServerBrowser::TYPE_FAVORITE_COMMUNITY_1;
std::vector<const CCommunity *> vpFavoriteCommunities = m_pServerBrowser->FavoriteCommunities();
dbg_assert(CommunityIndex < vpFavoriteCommunities.size(), "Invalid favorite community serverbrowser type");
m_pCountryTypeFilterKey = vpFavoriteCommunities[CommunityIndex]->Id();
}
else
{
m_pCountryTypeFilterKey = IServerBrowser::COMMUNITY_ALL;
}
m_pServerBrowser->CleanFilters();
}
void CFavoriteCommunityFilterList::Add(const char *pCommunityId) void CFavoriteCommunityFilterList::Add(const char *pCommunityId)
{ {
// Remove community if it's already a favorite, so it will be added again at // Remove community if it's already a favorite, so it will be added again at
@ -1797,6 +1870,14 @@ const std::vector<CCommunityId> &CFavoriteCommunityFilterList::Entries() const
return m_vEntries; return m_vEntries;
} }
template<typename TNamedElement, typename TElementName>
static bool IsSubsetEquals(const std::vector<const TNamedElement *> &vpLeft, const std::unordered_set<TElementName> &Right)
{
return vpLeft.size() <= Right.size() && std::all_of(vpLeft.begin(), vpLeft.end(), [&](const TNamedElement *pElem) {
return Right.count(TElementName(pElem->Name())) > 0;
});
}
void CExcludedCommunityFilterList::Add(const char *pCommunityId) void CExcludedCommunityFilterList::Add(const char *pCommunityId)
{ {
m_Entries.emplace(pCommunityId); m_Entries.emplace(pCommunityId);
@ -1859,13 +1940,18 @@ void CExcludedCommunityFilterList::Save(IConfigManager *pConfigManager) const
void CExcludedCommunityCountryFilterList::Add(const char *pCountryName) void CExcludedCommunityCountryFilterList::Add(const char *pCountryName)
{ {
for(const CCommunity *pCommunity : m_CurrentCommunitiesGetter()) // Handle special case that all selectable entries are currently filtered,
// where adding more entries to the exclusion list would have no effect.
auto CommunityEntry = m_Entries.find(m_pCommunityCache->CountryTypeFilterKey());
if(CommunityEntry != m_Entries.end() && IsSubsetEquals(m_pCommunityCache->SelectableCountries(), CommunityEntry->second))
{ {
if(pCommunity->HasCountry(pCountryName)) for(const CCommunityCountry *pSelectableCountry : m_pCommunityCache->SelectableCountries())
{ {
Add(pCommunity->Id(), pCountryName); CommunityEntry->second.erase(pSelectableCountry->Name());
} }
} }
Add(m_pCommunityCache->CountryTypeFilterKey(), pCountryName);
} }
void CExcludedCommunityCountryFilterList::Add(const char *pCommunityId, const char *pCountryName) void CExcludedCommunityCountryFilterList::Add(const char *pCommunityId, const char *pCountryName)
@ -1880,10 +1966,7 @@ void CExcludedCommunityCountryFilterList::Add(const char *pCommunityId, const ch
void CExcludedCommunityCountryFilterList::Remove(const char *pCountryName) void CExcludedCommunityCountryFilterList::Remove(const char *pCountryName)
{ {
for(const CCommunity *pCommunity : m_CurrentCommunitiesGetter()) Remove(m_pCommunityCache->CountryTypeFilterKey(), pCountryName);
{
Remove(pCommunity->Id(), pCountryName);
}
} }
void CExcludedCommunityCountryFilterList::Remove(const char *pCommunityId, const char *pCountryName) void CExcludedCommunityCountryFilterList::Remove(const char *pCommunityId, const char *pCountryName)
@ -1897,50 +1980,38 @@ void CExcludedCommunityCountryFilterList::Remove(const char *pCommunityId, const
void CExcludedCommunityCountryFilterList::Clear() void CExcludedCommunityCountryFilterList::Clear()
{ {
for(const CCommunity *pCommunity : m_CurrentCommunitiesGetter()) auto CommunityEntry = m_Entries.find(m_pCommunityCache->CountryTypeFilterKey());
if(CommunityEntry != m_Entries.end())
{ {
auto CommunityEntry = m_Entries.find(pCommunity->Id()); CommunityEntry->second.clear();
if(CommunityEntry != m_Entries.end())
{
CommunityEntry->second.clear();
}
} }
} }
bool CExcludedCommunityCountryFilterList::Filtered(const char *pCountryName) const bool CExcludedCommunityCountryFilterList::Filtered(const char *pCountryName) const
{ {
const auto Communities = m_CurrentCommunitiesGetter(); auto CommunityEntry = m_Entries.find(CCommunityId(m_pCommunityCache->CountryTypeFilterKey()));
return std::none_of(Communities.begin(), Communities.end(), [&](const CCommunity *pCommunity) { if(CommunityEntry == m_Entries.end())
if(!pCommunity->HasCountry(pCountryName))
return false;
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; return false;
});
const auto &CountryEntries = CommunityEntry->second;
return !IsSubsetEquals(m_pCommunityCache->SelectableCountries(), CountryEntries) &&
CountryEntries.find(CCommunityCountryName(pCountryName)) != CountryEntries.end();
} }
bool CExcludedCommunityCountryFilterList::Empty() const bool CExcludedCommunityCountryFilterList::Empty() const
{ {
for(const CCommunity *pCommunity : m_CurrentCommunitiesGetter()) auto CommunityEntry = m_Entries.find(CCommunityId(m_pCommunityCache->CountryTypeFilterKey()));
{ return CommunityEntry == m_Entries.end() ||
auto CommunityEntry = m_Entries.find(CCommunityId(pCommunity->Id())); CommunityEntry->second.empty() ||
return CommunityEntry == m_Entries.end() || CommunityEntry->second.empty(); IsSubsetEquals(m_pCommunityCache->SelectableCountries(), CommunityEntry->second);
}
return false;
} }
void CExcludedCommunityCountryFilterList::Clean(const std::vector<CCommunity> &vAllowedCommunities) void CExcludedCommunityCountryFilterList::Clean(const std::vector<CCommunity> &vAllowedCommunities)
{ {
for(auto It = m_Entries.begin(); It != m_Entries.end();) for(auto It = m_Entries.begin(); It != m_Entries.end();)
{ {
const bool Found = std::find_if(vAllowedCommunities.begin(), vAllowedCommunities.end(), [&](const CCommunity &AllowedCommunity) { const bool AllEntry = str_comp(It->first.Id(), IServerBrowser::COMMUNITY_ALL) == 0;
const bool Found = AllEntry || std::find_if(vAllowedCommunities.begin(), vAllowedCommunities.end(), [&](const CCommunity &AllowedCommunity) {
return str_comp(It->first.Id(), AllowedCommunity.Id()) == 0; return str_comp(It->first.Id(), AllowedCommunity.Id()) == 0;
}) != vAllowedCommunities.end(); }) != vAllowedCommunities.end();
if(Found) if(Found)
@ -1977,6 +2048,36 @@ void CExcludedCommunityCountryFilterList::Clean(const std::vector<CCommunity> &v
} }
} }
} }
auto AllCommunityEntry = m_Entries.find(CCommunityId(IServerBrowser::COMMUNITY_ALL));
if(AllCommunityEntry != m_Entries.end())
{
auto &CountryEntries = AllCommunityEntry->second;
for(auto It = CountryEntries.begin(); It != CountryEntries.end();)
{
if(std::any_of(vAllowedCommunities.begin(), vAllowedCommunities.end(), [&](const auto &Community) { return Community.HasCountry(It->Name()); }))
{
++It;
}
else
{
It = CountryEntries.erase(It);
}
}
// Prevent filter that would exclude all allowed countries
std::unordered_set<CCommunityCountryName> UniqueCountries;
for(const CCommunity &AllowedCommunity : vAllowedCommunities)
{
for(const CCommunityCountry &Country : AllowedCommunity.Countries())
{
UniqueCountries.emplace(Country.Name());
}
}
if(CountryEntries.size() == UniqueCountries.size())
{
CountryEntries.clear();
}
}
} }
void CExcludedCommunityCountryFilterList::Save(IConfigManager *pConfigManager) const void CExcludedCommunityCountryFilterList::Save(IConfigManager *pConfigManager) const
@ -1998,13 +2099,18 @@ void CExcludedCommunityCountryFilterList::Save(IConfigManager *pConfigManager) c
void CExcludedCommunityTypeFilterList::Add(const char *pTypeName) void CExcludedCommunityTypeFilterList::Add(const char *pTypeName)
{ {
for(const CCommunity *pCommunity : m_CurrentCommunitiesGetter()) // Handle special case that all selectable entries are currently filtered,
// where adding more entries to the exclusion list would have no effect.
auto CommunityEntry = m_Entries.find(m_pCommunityCache->CountryTypeFilterKey());
if(CommunityEntry != m_Entries.end() && IsSubsetEquals(m_pCommunityCache->SelectableTypes(), CommunityEntry->second))
{ {
if(pCommunity->HasType(pTypeName)) for(const CCommunityType *pSelectableType : m_pCommunityCache->SelectableTypes())
{ {
Add(pCommunity->Id(), pTypeName); CommunityEntry->second.erase(pSelectableType->Name());
} }
} }
Add(m_pCommunityCache->CountryTypeFilterKey(), pTypeName);
} }
void CExcludedCommunityTypeFilterList::Add(const char *pCommunityId, const char *pTypeName) void CExcludedCommunityTypeFilterList::Add(const char *pCommunityId, const char *pTypeName)
@ -2019,10 +2125,7 @@ void CExcludedCommunityTypeFilterList::Add(const char *pCommunityId, const char
void CExcludedCommunityTypeFilterList::Remove(const char *pTypeName) void CExcludedCommunityTypeFilterList::Remove(const char *pTypeName)
{ {
for(const CCommunity *pCommunity : m_CurrentCommunitiesGetter()) Remove(m_pCommunityCache->CountryTypeFilterKey(), pTypeName);
{
Remove(pCommunity->Id(), pTypeName);
}
} }
void CExcludedCommunityTypeFilterList::Remove(const char *pCommunityId, const char *pTypeName) void CExcludedCommunityTypeFilterList::Remove(const char *pCommunityId, const char *pTypeName)
@ -2036,47 +2139,38 @@ void CExcludedCommunityTypeFilterList::Remove(const char *pCommunityId, const ch
void CExcludedCommunityTypeFilterList::Clear() void CExcludedCommunityTypeFilterList::Clear()
{ {
for(const CCommunity *pCommunity : m_CurrentCommunitiesGetter()) auto CommunityEntry = m_Entries.find(m_pCommunityCache->CountryTypeFilterKey());
if(CommunityEntry != m_Entries.end())
{ {
auto CommunityEntry = m_Entries.find(pCommunity->Id()); CommunityEntry->second.clear();
if(CommunityEntry != m_Entries.end())
{
CommunityEntry->second.clear();
}
} }
} }
bool CExcludedCommunityTypeFilterList::Filtered(const char *pTypeName) const bool CExcludedCommunityTypeFilterList::Filtered(const char *pTypeName) const
{ {
const auto Communities = m_CurrentCommunitiesGetter(); auto CommunityEntry = m_Entries.find(CCommunityId(m_pCommunityCache->CountryTypeFilterKey()));
return std::none_of(Communities.begin(), Communities.end(), [&](const CCommunity *pCommunity) { if(CommunityEntry == m_Entries.end())
if(!pCommunity->HasType(pTypeName)) return false;
return false;
auto CommunityEntry = m_Entries.find(CCommunityId(pCommunity->Id())); const auto &TypeEntries = CommunityEntry->second;
if(CommunityEntry == m_Entries.end()) return !IsSubsetEquals(m_pCommunityCache->SelectableTypes(), TypeEntries) &&
return true; TypeEntries.find(CCommunityTypeName(pTypeName)) != TypeEntries.end();
const auto &TypeEntries = CommunityEntry->second;
return TypeEntries.find(CCommunityTypeName(pTypeName)) == TypeEntries.end();
});
} }
bool CExcludedCommunityTypeFilterList::Empty() const bool CExcludedCommunityTypeFilterList::Empty() const
{ {
for(const CCommunity *pCommunity : m_CurrentCommunitiesGetter()) auto CommunityEntry = m_Entries.find(CCommunityId(m_pCommunityCache->CountryTypeFilterKey()));
{ return CommunityEntry == m_Entries.end() ||
auto CommunityEntry = m_Entries.find(CCommunityId(pCommunity->Id())); CommunityEntry->second.empty() ||
return CommunityEntry == m_Entries.end() || CommunityEntry->second.empty(); IsSubsetEquals(m_pCommunityCache->SelectableTypes(), CommunityEntry->second);
}
return false;
} }
void CExcludedCommunityTypeFilterList::Clean(const std::vector<CCommunity> &vAllowedCommunities) void CExcludedCommunityTypeFilterList::Clean(const std::vector<CCommunity> &vAllowedCommunities)
{ {
for(auto It = m_Entries.begin(); It != m_Entries.end();) for(auto It = m_Entries.begin(); It != m_Entries.end();)
{ {
const bool Found = std::find_if(vAllowedCommunities.begin(), vAllowedCommunities.end(), [&](const CCommunity &AllowedCommunity) { const bool AllEntry = str_comp(It->first.Id(), IServerBrowser::COMMUNITY_ALL) == 0;
const bool Found = AllEntry || std::find_if(vAllowedCommunities.begin(), vAllowedCommunities.end(), [&](const CCommunity &AllowedCommunity) {
return str_comp(It->first.Id(), AllowedCommunity.Id()) == 0; return str_comp(It->first.Id(), AllowedCommunity.Id()) == 0;
}) != vAllowedCommunities.end(); }) != vAllowedCommunities.end();
if(Found) if(Found)
@ -2106,13 +2200,43 @@ void CExcludedCommunityTypeFilterList::Clean(const std::vector<CCommunity> &vAll
It = TypeEntries.erase(It); It = TypeEntries.erase(It);
} }
} }
// Prevent filter that would exclude all allowed countries // Prevent filter that would exclude all allowed types
if(TypeEntries.size() == AllowedCommunity.Types().size()) if(TypeEntries.size() == AllowedCommunity.Types().size())
{ {
TypeEntries.clear(); TypeEntries.clear();
} }
} }
} }
auto AllCommunityEntry = m_Entries.find(CCommunityId(IServerBrowser::COMMUNITY_ALL));
if(AllCommunityEntry != m_Entries.end())
{
auto &TypeEntries = AllCommunityEntry->second;
for(auto It = TypeEntries.begin(); It != TypeEntries.end();)
{
if(std::any_of(vAllowedCommunities.begin(), vAllowedCommunities.end(), [&](const auto &Community) { return Community.HasType(It->Name()); }))
{
++It;
}
else
{
It = TypeEntries.erase(It);
}
}
// Prevent filter that would exclude all allowed types
std::unordered_set<CCommunityCountryName> UniqueTypes;
for(const CCommunity &AllowedCommunity : vAllowedCommunities)
{
for(const CCommunityType &Type : AllowedCommunity.Types())
{
UniqueTypes.emplace(Type.Name());
}
}
if(TypeEntries.size() == UniqueTypes.size())
{
TypeEntries.clear();
}
}
} }
void CExcludedCommunityTypeFilterList::Save(IConfigManager *pConfigManager) const void CExcludedCommunityTypeFilterList::Save(IConfigManager *pConfigManager) const

View file

@ -160,8 +160,8 @@ private:
class CExcludedCommunityCountryFilterList : public IFilterList class CExcludedCommunityCountryFilterList : public IFilterList
{ {
public: public:
CExcludedCommunityCountryFilterList(std::function<std::vector<const CCommunity *>()> CurrentCommunitiesGetter) : CExcludedCommunityCountryFilterList(const ICommunityCache *pCommunityCache) :
m_CurrentCommunitiesGetter(CurrentCommunitiesGetter) m_pCommunityCache(pCommunityCache)
{ {
} }
@ -176,15 +176,15 @@ public:
void Save(IConfigManager *pConfigManager) const; void Save(IConfigManager *pConfigManager) const;
private: private:
std::function<std::vector<const CCommunity *>()> m_CurrentCommunitiesGetter; const ICommunityCache *m_pCommunityCache;
std::unordered_map<CCommunityId, std::unordered_set<CCommunityCountryName>> m_Entries; std::unordered_map<CCommunityId, std::unordered_set<CCommunityCountryName>> m_Entries;
}; };
class CExcludedCommunityTypeFilterList : public IFilterList class CExcludedCommunityTypeFilterList : public IFilterList
{ {
public: public:
CExcludedCommunityTypeFilterList(std::function<std::vector<const CCommunity *>()> CurrentCommunitiesGetter) : CExcludedCommunityTypeFilterList(const ICommunityCache *pCommunityCache) :
m_CurrentCommunitiesGetter(CurrentCommunitiesGetter) m_pCommunityCache(pCommunityCache)
{ {
} }
@ -199,10 +199,38 @@ public:
void Save(IConfigManager *pConfigManager) const; void Save(IConfigManager *pConfigManager) const;
private: private:
std::function<std::vector<const CCommunity *>()> m_CurrentCommunitiesGetter; const ICommunityCache *m_pCommunityCache;
std::unordered_map<CCommunityId, std::unordered_set<CCommunityTypeName>> m_Entries; std::unordered_map<CCommunityId, std::unordered_set<CCommunityTypeName>> m_Entries;
}; };
class CCommunityCache : public ICommunityCache
{
IServerBrowser *m_pServerBrowser;
SHA256_DIGEST m_InfoSha256 = SHA256_ZEROED;
int m_LastType = IServerBrowser::NUM_TYPES; // initial value does not appear normally, marking uninitialized cache
unsigned m_SelectedCommunitiesHash = 0;
std::vector<const CCommunity *> m_vpSelectedCommunities;
std::vector<const CCommunityCountry *> m_vpSelectableCountries;
std::vector<const CCommunityType *> m_vpSelectableTypes;
bool m_AnyRanksAvailable = false;
bool m_CountryTypesFilterAvailable = false;
const char *m_pCountryTypeFilterKey = IServerBrowser::COMMUNITY_ALL;
public:
CCommunityCache(IServerBrowser *pServerBrowser) :
m_pServerBrowser(pServerBrowser)
{
}
void Update(bool Force) override;
const std::vector<const CCommunity *> &SelectedCommunities() const override { return m_vpSelectedCommunities; }
const std::vector<const CCommunityCountry *> &SelectableCountries() const override { return m_vpSelectableCountries; }
const std::vector<const CCommunityType *> &SelectableTypes() const override { return m_vpSelectableTypes; }
bool AnyRanksAvailable() const override { return m_AnyRanksAvailable; }
bool CountriesTypesFilterAvailable() const override { return m_CountryTypesFilterAvailable; }
const char *CountryTypeFilterKey() const override { return m_pCountryTypeFilterKey; }
};
class CServerBrowser : public IServerBrowser class CServerBrowser : public IServerBrowser
{ {
public: public:
@ -255,6 +283,8 @@ public:
bool DDNetInfoAvailable() const override { return m_pDDNetInfo != nullptr; } bool DDNetInfoAvailable() const override { return m_pDDNetInfo != nullptr; }
SHA256_DIGEST DDNetInfoSha256() const override { return m_DDNetInfoSha256; } SHA256_DIGEST DDNetInfoSha256() const override { return m_DDNetInfoSha256; }
ICommunityCache &CommunityCache() override { return m_CommunityCache; }
const ICommunityCache &CommunityCache() const override { return m_CommunityCache; }
CFavoriteCommunityFilterList &FavoriteCommunitiesFilter() override { return m_FavoriteCommunitiesFilter; } CFavoriteCommunityFilterList &FavoriteCommunitiesFilter() override { return m_FavoriteCommunitiesFilter; }
CExcludedCommunityFilterList &CommunitiesFilter() override { return m_CommunitiesFilter; } CExcludedCommunityFilterList &CommunitiesFilter() override { return m_CommunitiesFilter; }
CExcludedCommunityCountryFilterList &CountriesFilter() override { return m_CountriesFilter; } CExcludedCommunityCountryFilterList &CountriesFilter() override { return m_CountriesFilter; }
@ -307,6 +337,7 @@ private:
int m_OwnLocation = CServerInfo::LOC_UNKNOWN; int m_OwnLocation = CServerInfo::LOC_UNKNOWN;
CCommunityCache m_CommunityCache;
CFavoriteCommunityFilterList m_FavoriteCommunitiesFilter; CFavoriteCommunityFilterList m_FavoriteCommunitiesFilter;
CExcludedCommunityFilterList m_CommunitiesFilter; CExcludedCommunityFilterList m_CommunitiesFilter;
CExcludedCommunityCountryFilterList m_CountriesFilter; CExcludedCommunityCountryFilterList m_CountriesFilter;

View file

@ -248,6 +248,18 @@ public:
virtual bool Filtered(const char *pElement) const = 0; virtual bool Filtered(const char *pElement) const = 0;
}; };
class ICommunityCache
{
public:
virtual void Update(bool Force) = 0;
virtual const std::vector<const CCommunity *> &SelectedCommunities() const = 0;
virtual const std::vector<const CCommunityCountry *> &SelectableCountries() const = 0;
virtual const std::vector<const CCommunityType *> &SelectableTypes() const = 0;
virtual bool AnyRanksAvailable() const = 0;
virtual bool CountriesTypesFilterAvailable() const = 0;
virtual const char *CountryTypeFilterKey() const = 0;
};
class IServerBrowser : public IInterface class IServerBrowser : public IInterface
{ {
MACRO_INTERFACE("serverbrowser") MACRO_INTERFACE("serverbrowser")
@ -286,6 +298,11 @@ public:
static constexpr const char *COMMUNITY_DDNET = "ddnet"; static constexpr const char *COMMUNITY_DDNET = "ddnet";
static constexpr const char *COMMUNITY_NONE = "none"; static constexpr const char *COMMUNITY_NONE = "none";
/**
* Special community value for country/type filters that
* affect all communities.
*/
static constexpr const char *COMMUNITY_ALL = "all";
static constexpr const char *SEARCH_EXCLUDE_TOKEN = ";"; static constexpr const char *SEARCH_EXCLUDE_TOKEN = ";";
@ -313,6 +330,8 @@ public:
virtual bool DDNetInfoAvailable() const = 0; virtual bool DDNetInfoAvailable() const = 0;
virtual SHA256_DIGEST DDNetInfoSha256() const = 0; virtual SHA256_DIGEST DDNetInfoSha256() const = 0;
virtual ICommunityCache &CommunityCache() = 0;
virtual const ICommunityCache &CommunityCache() const = 0;
virtual IFilterList &FavoriteCommunitiesFilter() = 0; virtual IFilterList &FavoriteCommunitiesFilter() = 0;
virtual IFilterList &CommunitiesFilter() = 0; virtual IFilterList &CommunitiesFilter() = 0;
virtual IFilterList &CountriesFilter() = 0; virtual IFilterList &CountriesFilter() = 0;

View file

@ -663,12 +663,6 @@ void CMenus::RenderMenubar(CUIRect Box, IClient::EClientState ClientState)
static CButtonContainer s_InternetButton; static CButtonContainer s_InternetButton;
if(DoButton_MenuTab(&s_InternetButton, FONT_ICON_EARTH_AMERICAS, ActivePage == PAGE_INTERNET, &Button, IGraphics::CORNER_T, &m_aAnimatorsBigPage[BIG_TAB_INTERNET])) if(DoButton_MenuTab(&s_InternetButton, FONT_ICON_EARTH_AMERICAS, ActivePage == PAGE_INTERNET, &Button, IGraphics::CORNER_T, &m_aAnimatorsBigPage[BIG_TAB_INTERNET]))
{ {
if(ServerBrowser()->GetCurrentType() != IServerBrowser::TYPE_INTERNET)
{
if(ServerBrowser()->GetCurrentType() == IServerBrowser::TYPE_LAN)
Client()->RequestDDNetInfo();
ServerBrowser()->Refresh(IServerBrowser::TYPE_INTERNET);
}
NewPage = PAGE_INTERNET; NewPage = PAGE_INTERNET;
} }
GameClient()->m_Tooltips.DoToolTip(&s_InternetButton, &Button, Localize("Internet")); GameClient()->m_Tooltips.DoToolTip(&s_InternetButton, &Button, Localize("Internet"));
@ -677,8 +671,6 @@ void CMenus::RenderMenubar(CUIRect Box, IClient::EClientState ClientState)
static CButtonContainer s_LanButton; static CButtonContainer s_LanButton;
if(DoButton_MenuTab(&s_LanButton, FONT_ICON_NETWORK_WIRED, ActivePage == PAGE_LAN, &Button, IGraphics::CORNER_T, &m_aAnimatorsBigPage[BIG_TAB_LAN])) if(DoButton_MenuTab(&s_LanButton, FONT_ICON_NETWORK_WIRED, ActivePage == PAGE_LAN, &Button, IGraphics::CORNER_T, &m_aAnimatorsBigPage[BIG_TAB_LAN]))
{ {
if(ServerBrowser()->GetCurrentType() != IServerBrowser::TYPE_LAN)
ServerBrowser()->Refresh(IServerBrowser::TYPE_LAN);
NewPage = PAGE_LAN; NewPage = PAGE_LAN;
} }
GameClient()->m_Tooltips.DoToolTip(&s_LanButton, &Button, Localize("LAN")); GameClient()->m_Tooltips.DoToolTip(&s_LanButton, &Button, Localize("LAN"));
@ -687,12 +679,6 @@ void CMenus::RenderMenubar(CUIRect Box, IClient::EClientState ClientState)
static CButtonContainer s_FavoritesButton; static CButtonContainer s_FavoritesButton;
if(DoButton_MenuTab(&s_FavoritesButton, FONT_ICON_STAR, ActivePage == PAGE_FAVORITES, &Button, IGraphics::CORNER_T, &m_aAnimatorsBigPage[BIG_TAB_FAVORITES])) if(DoButton_MenuTab(&s_FavoritesButton, FONT_ICON_STAR, ActivePage == PAGE_FAVORITES, &Button, IGraphics::CORNER_T, &m_aAnimatorsBigPage[BIG_TAB_FAVORITES]))
{ {
if(ServerBrowser()->GetCurrentType() != IServerBrowser::TYPE_FAVORITES)
{
if(ServerBrowser()->GetCurrentType() == IServerBrowser::TYPE_LAN)
Client()->RequestDDNetInfo();
ServerBrowser()->Refresh(IServerBrowser::TYPE_FAVORITES);
}
NewPage = PAGE_FAVORITES; NewPage = PAGE_FAVORITES;
} }
GameClient()->m_Tooltips.DoToolTip(&s_FavoritesButton, &Button, Localize("Favorites")); GameClient()->m_Tooltips.DoToolTip(&s_FavoritesButton, &Button, Localize("Favorites"));
@ -710,13 +696,6 @@ void CMenus::RenderMenubar(CUIRect Box, IClient::EClientState ClientState)
const int Page = PAGE_FAVORITE_COMMUNITY_1 + FavoriteCommunityIndex; const int Page = PAGE_FAVORITE_COMMUNITY_1 + FavoriteCommunityIndex;
if(DoButton_MenuTab(&s_aFavoriteCommunityButtons[FavoriteCommunityIndex], FONT_ICON_ELLIPSIS, ActivePage == Page, &Button, IGraphics::CORNER_T, &m_aAnimatorsBigPage[BIT_TAB_FAVORITE_COMMUNITY_1 + FavoriteCommunityIndex], nullptr, nullptr, nullptr, 10.0f, FindCommunityIcon(pCommunity->Id()))) if(DoButton_MenuTab(&s_aFavoriteCommunityButtons[FavoriteCommunityIndex], FONT_ICON_ELLIPSIS, 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; NewPage = Page;
} }
GameClient()->m_Tooltips.DoToolTip(&s_aFavoriteCommunityButtons[FavoriteCommunityIndex], &Button, pCommunity->Name()); GameClient()->m_Tooltips.DoToolTip(&s_aFavoriteCommunityButtons[FavoriteCommunityIndex], &Button, pCommunity->Name());
@ -1036,7 +1015,7 @@ void CMenus::Render()
static int s_Frame = 0; static int s_Frame = 0;
if(s_Frame == 0) if(s_Frame == 0)
{ {
RefreshBrowserTab(g_Config.m_UiPage); RefreshBrowserTab(true);
s_Frame++; s_Frame++;
} }
else if(s_Frame == 1) else if(s_Frame == 1)
@ -1851,7 +1830,7 @@ void CMenus::RenderPopupConnecting(CUIRect Screen)
{ {
Client()->Disconnect(); Client()->Disconnect();
Ui()->SetActiveItem(nullptr); Ui()->SetActiveItem(nullptr);
RefreshBrowserTab(g_Config.m_UiPage); RefreshBrowserTab(true);
} }
} }
@ -1970,7 +1949,7 @@ void CMenus::RenderPopupLoading(CUIRect Screen)
{ {
Client()->Disconnect(); Client()->Disconnect();
Ui()->SetActiveItem(nullptr); Ui()->SetActiveItem(nullptr);
RefreshBrowserTab(g_Config.m_UiPage); RefreshBrowserTab(true);
} }
} }
@ -2383,30 +2362,63 @@ const CMenus::CMenuImage *CMenus::FindMenuImage(const char *pName)
void CMenus::SetMenuPage(int NewPage) void CMenus::SetMenuPage(int NewPage)
{ {
const int OldPage = m_MenuPage;
m_MenuPage = NewPage; m_MenuPage = NewPage;
if(NewPage >= PAGE_INTERNET && NewPage <= PAGE_FAVORITE_COMMUNITY_5) if(NewPage >= PAGE_INTERNET && NewPage <= PAGE_FAVORITE_COMMUNITY_5)
{
g_Config.m_UiPage = NewPage; g_Config.m_UiPage = NewPage;
if(!m_ShowStart && OldPage != NewPage)
{
RefreshBrowserTab(false);
}
}
} }
void CMenus::RefreshBrowserTab(int UiPage) void CMenus::RefreshBrowserTab(bool Force)
{ {
if(UiPage == PAGE_INTERNET) if(g_Config.m_UiPage == PAGE_INTERNET)
{ {
Client()->RequestDDNetInfo(); if(Force || ServerBrowser()->GetCurrentType() != IServerBrowser::TYPE_INTERNET)
ServerBrowser()->Refresh(IServerBrowser::TYPE_INTERNET); {
if(Force || ServerBrowser()->GetCurrentType() == IServerBrowser::TYPE_LAN)
{
Client()->RequestDDNetInfo();
}
ServerBrowser()->Refresh(IServerBrowser::TYPE_INTERNET);
UpdateCommunityCache(true);
}
} }
else if(UiPage == PAGE_LAN) else if(g_Config.m_UiPage == PAGE_LAN)
{ {
ServerBrowser()->Refresh(IServerBrowser::TYPE_LAN); if(Force || ServerBrowser()->GetCurrentType() != IServerBrowser::TYPE_LAN)
{
ServerBrowser()->Refresh(IServerBrowser::TYPE_LAN);
UpdateCommunityCache(true);
}
} }
else if(UiPage == PAGE_FAVORITES) else if(g_Config.m_UiPage == PAGE_FAVORITES)
{ {
Client()->RequestDDNetInfo(); if(Force || ServerBrowser()->GetCurrentType() != IServerBrowser::TYPE_FAVORITES)
ServerBrowser()->Refresh(IServerBrowser::TYPE_FAVORITES); {
if(Force || ServerBrowser()->GetCurrentType() == IServerBrowser::TYPE_LAN)
{
Client()->RequestDDNetInfo();
}
ServerBrowser()->Refresh(IServerBrowser::TYPE_FAVORITES);
UpdateCommunityCache(true);
}
} }
else if(UiPage >= PAGE_FAVORITE_COMMUNITY_1 && UiPage <= PAGE_FAVORITE_COMMUNITY_5) else if(g_Config.m_UiPage >= PAGE_FAVORITE_COMMUNITY_1 && g_Config.m_UiPage <= PAGE_FAVORITE_COMMUNITY_5)
{ {
Client()->RequestDDNetInfo(); const int BrowserType = g_Config.m_UiPage - PAGE_FAVORITE_COMMUNITY_1 + IServerBrowser::TYPE_FAVORITE_COMMUNITY_1;
ServerBrowser()->Refresh(UiPage - PAGE_FAVORITE_COMMUNITY_1 + IServerBrowser::TYPE_FAVORITE_COMMUNITY_1); if(Force || ServerBrowser()->GetCurrentType() != BrowserType)
{
if(Force || ServerBrowser()->GetCurrentType() == IServerBrowser::TYPE_LAN)
{
Client()->RequestDDNetInfo();
}
ServerBrowser()->Refresh(BrowserType);
UpdateCommunityCache(true);
}
} }
} }

View file

@ -508,17 +508,6 @@ protected:
static void ConchainFavoritesUpdate(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 ConchainCommunitiesUpdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
static void ConchainUiPageUpdate(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
{
SHA256_DIGEST m_InfoSha256 = SHA256_ZEROED;
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;
bool m_AnyRanksAvailable;
};
SCommunityCache m_CommunityCache;
void UpdateCommunityCache(bool Force); void UpdateCommunityCache(bool Force);
// community icons // community icons
@ -773,7 +762,7 @@ public:
private: private:
static int GhostlistFetchCallback(const CFsFileInfo *pInfo, int IsDir, int StorageType, void *pUser); static int GhostlistFetchCallback(const CFsFileInfo *pInfo, int IsDir, int StorageType, void *pUser);
void SetMenuPage(int NewPage); void SetMenuPage(int NewPage);
void RefreshBrowserTab(int UiPage); void RefreshBrowserTab(bool Force);
// found in menus_ingame.cpp // found in menus_ingame.cpp
void RenderInGameNetwork(CUIRect MainView); void RenderInGameNetwork(CUIRect MainView);

View file

@ -591,7 +591,7 @@ void CMenus::RenderServerbrowserStatusBox(CUIRect StatusBox, bool WasListboxItem
static CButtonContainer s_RefreshButton; static CButtonContainer s_RefreshButton;
if(Ui()->DoButton_Menu(m_RefreshButton, &s_RefreshButton, RefreshLabelFunc, &ButtonRefresh, Props) || (!Ui()->IsPopupOpen() && (Input()->KeyPress(KEY_F5) || (Input()->KeyPress(KEY_R) && Input()->ModifierIsPressed())))) if(Ui()->DoButton_Menu(m_RefreshButton, &s_RefreshButton, RefreshLabelFunc, &ButtonRefresh, Props) || (!Ui()->IsPopupOpen() && (Input()->KeyPress(KEY_F5) || (Input()->KeyPress(KEY_R) && Input()->ModifierIsPressed()))))
{ {
RefreshBrowserTab(g_Config.m_UiPage); RefreshBrowserTab(true);
} }
} }
@ -715,7 +715,7 @@ void CMenus::RenderServerbrowserFilters(CUIRect View)
g_Config.m_BrFilterConnectingPlayers ^= 1; g_Config.m_BrFilterConnectingPlayers ^= 1;
// map finish filters // map finish filters
if(m_CommunityCache.m_AnyRanksAvailable) if(ServerBrowser()->CommunityCache().AnyRanksAvailable())
{ {
View.HSplitTop(RowHeight, &Button, &View); View.HSplitTop(RowHeight, &Button, &View);
if(DoButton_CheckBox(&g_Config.m_BrIndicateFinished, Localize("Indicate map finish"), g_Config.m_BrIndicateFinished, &Button)) if(DoButton_CheckBox(&g_Config.m_BrIndicateFinished, Localize("Indicate map finish"), g_Config.m_BrIndicateFinished, &Button))
@ -737,9 +737,8 @@ void CMenus::RenderServerbrowserFilters(CUIRect View)
} }
} }
// countries and types filters (not shown if there are no countries and types, or if only the none community is selected) // countries and types filters
if((!m_CommunityCache.m_vpSelectableCountries.empty() || !m_CommunityCache.m_vpSelectableTypes.empty()) && if(ServerBrowser()->CommunityCache().CountriesTypesFilterAvailable())
(m_CommunityCache.m_vpSelectedCommunities.size() != 1 || str_comp(m_CommunityCache.m_vpSelectedCommunities[0]->Id(), IServerBrowser::COMMUNITY_NONE) != 0))
{ {
const ColorRGBA ColorActive = ColorRGBA(0.0f, 0.0f, 0.0f, 0.3f); const ColorRGBA ColorActive = ColorRGBA(0.0f, 0.0f, 0.0f, 0.3f);
const ColorRGBA ColorInactive = ColorRGBA(0.0f, 0.0f, 0.0f, 0.15f); const ColorRGBA ColorInactive = ColorRGBA(0.0f, 0.0f, 0.0f, 0.15f);
@ -806,7 +805,7 @@ void CMenus::ResetServerbrowserFilters()
if(g_Config.m_UiPage != PAGE_LAN) if(g_Config.m_UiPage != PAGE_LAN)
{ {
if(m_CommunityCache.m_AnyRanksAvailable) if(ServerBrowser()->CommunityCache().AnyRanksAvailable())
{ {
g_Config.m_BrFilterUnfinishedMap = 0; g_Config.m_BrFilterUnfinishedMap = 0;
} }
@ -891,10 +890,14 @@ void CMenus::RenderServerbrowserDDNetFilter(CUIRect View,
break; break;
} }
} }
// when last one is removed, reset (re-enable all) // When last one is removed, re-enable all currently selectable items.
// Don't use Clear, to avoid enabling also currently unselectable items.
if(AllFilteredExceptUs) if(AllFilteredExceptUs)
{ {
Filter.Clear(); for(int j = 0; j < MaxItems; ++j)
{
Filter.Remove(GetItemName(j));
}
} }
else if(Active) else if(Active)
{ {
@ -912,8 +915,11 @@ void CMenus::RenderServerbrowserDDNetFilter(CUIRect View,
} }
else if(Click == 3) else if(Click == 3)
{ {
// middle click to reset (re-enable all) // middle click to reset (re-enable all currently selectable items)
Filter.Clear(); for(int j = 0; j < MaxItems; ++j)
{
Filter.Remove(GetItemName(j));
}
Client()->ServerBrowserUpdate(); Client()->ServerBrowserUpdate();
if(UpdateCommunityCacheOnChange) if(UpdateCommunityCacheOnChange)
UpdateCommunityCache(true); UpdateCommunityCache(true);
@ -991,7 +997,7 @@ void CMenus::RenderServerbrowserCommunitiesFilter(CUIRect View)
void CMenus::RenderServerbrowserCountriesFilter(CUIRect View) void CMenus::RenderServerbrowserCountriesFilter(CUIRect View)
{ {
const int MaxEntries = m_CommunityCache.m_vpSelectableCountries.size(); const int MaxEntries = ServerBrowser()->CommunityCache().SelectableCountries().size();
const int EntriesPerRow = MaxEntries > 8 ? 5 : 4; const int EntriesPerRow = MaxEntries > 8 ? 5 : 4;
static CScrollRegion s_ScrollRegion; static CScrollRegion s_ScrollRegion;
@ -1001,14 +1007,14 @@ void CMenus::RenderServerbrowserCountriesFilter(CUIRect View)
const float Spacing = 2.0f; const float Spacing = 2.0f;
const auto &&GetItemName = [&](int ItemIndex) { const auto &&GetItemName = [&](int ItemIndex) {
return m_CommunityCache.m_vpSelectableCountries[ItemIndex]->Name(); return ServerBrowser()->CommunityCache().SelectableCountries()[ItemIndex]->Name();
}; };
const auto &&RenderItem = [&](int ItemIndex, CUIRect Item, const void *pItemId, bool Active) { const auto &&RenderItem = [&](int ItemIndex, CUIRect Item, const void *pItemId, bool Active) {
Item.Margin(Spacing, &Item); Item.Margin(Spacing, &Item);
const float OldWidth = Item.w; const float OldWidth = Item.w;
Item.w = Item.h * 2.0f; Item.w = Item.h * 2.0f;
Item.x += (OldWidth - Item.w) / 2.0f; Item.x += (OldWidth - Item.w) / 2.0f;
m_pClient->m_CountryFlags.Render(m_CommunityCache.m_vpSelectableCountries[ItemIndex]->FlagId(), ColorRGBA(1.0f, 1.0f, 1.0f, (Active ? 0.9f : 0.2f) + (Ui()->HotItem() == pItemId ? 0.1f : 0.0f)), Item.x, Item.y, Item.w, Item.h); m_pClient->m_CountryFlags.Render(ServerBrowser()->CommunityCache().SelectableCountries()[ItemIndex]->FlagId(), ColorRGBA(1.0f, 1.0f, 1.0f, (Active ? 0.9f : 0.2f) + (Ui()->HotItem() == pItemId ? 0.1f : 0.0f)), Item.x, Item.y, Item.w, Item.h);
}; };
RenderServerbrowserDDNetFilter(View, ServerBrowser()->CountriesFilter(), ItemHeight + 2.0f * Spacing, MaxEntries, EntriesPerRow, s_ScrollRegion, s_vItemIds, false, GetItemName, RenderItem); RenderServerbrowserDDNetFilter(View, ServerBrowser()->CountriesFilter(), ItemHeight + 2.0f * Spacing, MaxEntries, EntriesPerRow, s_ScrollRegion, s_vItemIds, false, GetItemName, RenderItem);
@ -1016,7 +1022,7 @@ void CMenus::RenderServerbrowserCountriesFilter(CUIRect View)
void CMenus::RenderServerbrowserTypesFilter(CUIRect View) void CMenus::RenderServerbrowserTypesFilter(CUIRect View)
{ {
const int MaxEntries = m_CommunityCache.m_vpSelectableTypes.size(); const int MaxEntries = ServerBrowser()->CommunityCache().SelectableTypes().size();
const int EntriesPerRow = 3; const int EntriesPerRow = 3;
static CScrollRegion s_ScrollRegion; static CScrollRegion s_ScrollRegion;
@ -1026,7 +1032,7 @@ void CMenus::RenderServerbrowserTypesFilter(CUIRect View)
const float Spacing = 2.0f; const float Spacing = 2.0f;
const auto &&GetItemName = [&](int ItemIndex) { const auto &&GetItemName = [&](int ItemIndex) {
return m_CommunityCache.m_vpSelectableTypes[ItemIndex]->Name(); return ServerBrowser()->CommunityCache().SelectableTypes()[ItemIndex]->Name();
}; };
const auto &&RenderItem = [&](int ItemIndex, CUIRect Item, const void *pItemId, bool Active) { const auto &&RenderItem = [&](int ItemIndex, CUIRect Item, const void *pItemId, bool Active) {
Item.Margin(Spacing, &Item); Item.Margin(Spacing, &Item);
@ -1848,7 +1854,6 @@ void CMenus::ConchainCommunitiesUpdate(IConsole::IResult *pResult, void *pUserDa
void CMenus::ConchainUiPageUpdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData) void CMenus::ConchainUiPageUpdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData)
{ {
const int OldPage = g_Config.m_UiPage;
pfnCallback(pResult, pCallbackUserData); pfnCallback(pResult, pCallbackUserData);
CMenus *pThis = static_cast<CMenus *>(pUserData); CMenus *pThis = static_cast<CMenus *>(pUserData);
if(pResult->NumArguments() >= 1) if(pResult->NumArguments() >= 1)
@ -1861,11 +1866,6 @@ void CMenus::ConchainUiPageUpdate(IConsole::IResult *pResult, void *pUserData, I
} }
pThis->SetMenuPage(g_Config.m_UiPage); pThis->SetMenuPage(g_Config.m_UiPage);
if(!pThis->m_ShowStart && g_Config.m_UiPage != OldPage)
{
pThis->RefreshBrowserTab(g_Config.m_UiPage);
}
} }
} }
@ -1876,63 +1876,13 @@ void CMenus::UpdateCommunityCache(bool Force)
{ {
// Reset page to internet when there is no favorite community for this page, // 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. // i.e. when favorite community is removed via console while the page is open.
// This also updates the community cache because the page is changed.
SetMenuPage(PAGE_INTERNET); SetMenuPage(PAGE_INTERNET);
RefreshBrowserTab(g_Config.m_UiPage);
} }
else
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_5)
{ {
// Favorite community was changed while its page is active, ServerBrowser()->CommunityCache().Update(Force);
// 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_InfoSha256 != SHA256_ZEROED &&
m_CommunityCache.m_InfoSha256 == ServerBrowser()->DDNetInfoSha256() &&
!CurrentCommunitiesChanged && !PageChanged)
{
return;
}
ServerBrowser()->CleanFilters();
m_CommunityCache.m_InfoSha256 = ServerBrowser()->DDNetInfoSha256();
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();
for(const CCommunity *pCommunity : m_CommunityCache.m_vpSelectedCommunities)
{
for(const auto &Country : pCommunity->Countries())
{
const auto ExistingCountry = std::find_if(m_CommunityCache.m_vpSelectableCountries.begin(), m_CommunityCache.m_vpSelectableCountries.end(), [&](const CCommunityCountry *pOther) {
return str_comp(Country.Name(), pOther->Name()) == 0 && Country.FlagId() == pOther->FlagId();
});
if(ExistingCountry == m_CommunityCache.m_vpSelectableCountries.end())
{
m_CommunityCache.m_vpSelectableCountries.push_back(&Country);
}
}
for(const auto &Type : pCommunity->Types())
{
const auto ExistingType = std::find_if(m_CommunityCache.m_vpSelectableTypes.begin(), m_CommunityCache.m_vpSelectableTypes.end(), [&](const CCommunityType *pOther) {
return str_comp(Type.Name(), pOther->Name()) == 0;
});
if(ExistingType == m_CommunityCache.m_vpSelectableTypes.end())
{
m_CommunityCache.m_vpSelectableTypes.push_back(&Type);
}
}
}
m_CommunityCache.m_AnyRanksAvailable = std::any_of(m_CommunityCache.m_vpSelectedCommunities.begin(), m_CommunityCache.m_vpSelectedCommunities.end(), [](const CCommunity *pCommunity) {
return pCommunity->HasRanks();
});
} }
CMenus::CAbstractCommunityIconJob::CAbstractCommunityIconJob(CMenus *pMenus, const char *pCommunityId, int StorageType) : CMenus::CAbstractCommunityIconJob::CAbstractCommunityIconJob(CMenus *pMenus, const char *pCommunityId, int StorageType) :

View file

@ -64,7 +64,7 @@ void CMenus::RenderGame(CUIRect MainView)
else else
{ {
Client()->Disconnect(); Client()->Disconnect();
RefreshBrowserTab(g_Config.m_UiPage); RefreshBrowserTab(true);
} }
} }
@ -847,12 +847,6 @@ void CMenus::RenderInGameNetwork(CUIRect MainView)
static CButtonContainer s_InternetButton; static CButtonContainer s_InternetButton;
if(DoButton_MenuTab(&s_InternetButton, FONT_ICON_EARTH_AMERICAS, g_Config.m_UiPage == PAGE_INTERNET, &Button, IGraphics::CORNER_NONE)) if(DoButton_MenuTab(&s_InternetButton, FONT_ICON_EARTH_AMERICAS, g_Config.m_UiPage == PAGE_INTERNET, &Button, IGraphics::CORNER_NONE))
{ {
if(ServerBrowser()->GetCurrentType() != IServerBrowser::TYPE_INTERNET)
{
if(ServerBrowser()->GetCurrentType() == IServerBrowser::TYPE_LAN)
Client()->RequestDDNetInfo();
ServerBrowser()->Refresh(IServerBrowser::TYPE_INTERNET);
}
NewPage = PAGE_INTERNET; NewPage = PAGE_INTERNET;
} }
GameClient()->m_Tooltips.DoToolTip(&s_InternetButton, &Button, Localize("Internet")); GameClient()->m_Tooltips.DoToolTip(&s_InternetButton, &Button, Localize("Internet"));
@ -861,8 +855,6 @@ void CMenus::RenderInGameNetwork(CUIRect MainView)
static CButtonContainer s_LanButton; static CButtonContainer s_LanButton;
if(DoButton_MenuTab(&s_LanButton, FONT_ICON_NETWORK_WIRED, g_Config.m_UiPage == PAGE_LAN, &Button, IGraphics::CORNER_NONE)) if(DoButton_MenuTab(&s_LanButton, FONT_ICON_NETWORK_WIRED, g_Config.m_UiPage == PAGE_LAN, &Button, IGraphics::CORNER_NONE))
{ {
if(ServerBrowser()->GetCurrentType() != IServerBrowser::TYPE_LAN)
ServerBrowser()->Refresh(IServerBrowser::TYPE_LAN);
NewPage = PAGE_LAN; NewPage = PAGE_LAN;
} }
GameClient()->m_Tooltips.DoToolTip(&s_LanButton, &Button, Localize("LAN")); GameClient()->m_Tooltips.DoToolTip(&s_LanButton, &Button, Localize("LAN"));
@ -871,12 +863,6 @@ void CMenus::RenderInGameNetwork(CUIRect MainView)
static CButtonContainer s_FavoritesButton; static CButtonContainer s_FavoritesButton;
if(DoButton_MenuTab(&s_FavoritesButton, FONT_ICON_STAR, g_Config.m_UiPage == PAGE_FAVORITES, &Button, IGraphics::CORNER_NONE)) if(DoButton_MenuTab(&s_FavoritesButton, FONT_ICON_STAR, g_Config.m_UiPage == PAGE_FAVORITES, &Button, IGraphics::CORNER_NONE))
{ {
if(ServerBrowser()->GetCurrentType() != IServerBrowser::TYPE_FAVORITES)
{
if(ServerBrowser()->GetCurrentType() == IServerBrowser::TYPE_LAN)
Client()->RequestDDNetInfo();
ServerBrowser()->Refresh(IServerBrowser::TYPE_FAVORITES);
}
NewPage = PAGE_FAVORITES; NewPage = PAGE_FAVORITES;
} }
GameClient()->m_Tooltips.DoToolTip(&s_FavoritesButton, &Button, Localize("Favorites")); GameClient()->m_Tooltips.DoToolTip(&s_FavoritesButton, &Button, Localize("Favorites"));
@ -890,13 +876,6 @@ void CMenus::RenderInGameNetwork(CUIRect MainView)
const int Page = PAGE_FAVORITE_COMMUNITY_1 + FavoriteCommunityIndex; 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()))) 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; NewPage = Page;
} }
GameClient()->m_Tooltips.DoToolTip(&s_aFavoriteCommunityButtons[FavoriteCommunityIndex], &Button, pCommunity->Name()); GameClient()->m_Tooltips.DoToolTip(&s_aFavoriteCommunityButtons[FavoriteCommunityIndex], &Button, pCommunity->Name());

View file

@ -72,7 +72,7 @@ void CMenus::RenderStartMenu(CUIRect MainView)
// Activate internet tab before joining tutorial to make sure the server info // Activate internet tab before joining tutorial to make sure the server info
// for the tutorial servers is available. // for the tutorial servers is available.
SetMenuPage(PAGE_INTERNET); SetMenuPage(PAGE_INTERNET);
RefreshBrowserTab(IServerBrowser::TYPE_INTERNET); RefreshBrowserTab(true);
const char *pAddr = ServerBrowser()->GetTutorialServer(); const char *pAddr = ServerBrowser()->GetTutorialServer();
if(pAddr) if(pAddr)
{ {