Merge pull request #8198 from Robyt3/Browser-Community-Filter-All

Track country/type filters separately for internet/favorites tabs
This commit is contained in:
Dennis Felsing 2024-04-11 04:39:43 +00:00 committed by GitHub
commit 8dcaee069e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 324 additions and 220 deletions

View file

@ -53,8 +53,9 @@ bool matchesExactly(const char *a, const char *b)
}
CServerBrowser::CServerBrowser() :
m_CountriesFilter([this]() { return CurrentCommunities(); }),
m_TypesFilter([this]() { return CurrentCommunities(); })
m_CommunityCache(this),
m_CountriesFilter(&m_CommunityCache),
m_TypesFilter(&m_CommunityCache)
{
m_ppServerlist = nullptr;
m_pSortedServerlist = nullptr;
@ -1728,6 +1729,78 @@ unsigned CServerBrowser::CurrentCommunitiesHash() const
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)
{
// 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;
}
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)
{
m_Entries.emplace(pCommunityId);
@ -1859,13 +1940,18 @@ void CExcludedCommunityFilterList::Save(IConfigManager *pConfigManager) const
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)
@ -1880,10 +1966,7 @@ void CExcludedCommunityCountryFilterList::Add(const char *pCommunityId, const ch
void CExcludedCommunityCountryFilterList::Remove(const char *pCountryName)
{
for(const CCommunity *pCommunity : m_CurrentCommunitiesGetter())
{
Remove(pCommunity->Id(), pCountryName);
}
Remove(m_pCommunityCache->CountryTypeFilterKey(), pCountryName);
}
void CExcludedCommunityCountryFilterList::Remove(const char *pCommunityId, const char *pCountryName)
@ -1897,50 +1980,38 @@ void CExcludedCommunityCountryFilterList::Remove(const char *pCommunityId, const
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());
if(CommunityEntry != m_Entries.end())
{
CommunityEntry->second.clear();
}
CommunityEntry->second.clear();
}
}
bool CExcludedCommunityCountryFilterList::Filtered(const char *pCountryName) const
{
const auto Communities = m_CurrentCommunitiesGetter();
return std::none_of(Communities.begin(), Communities.end(), [&](const CCommunity *pCommunity) {
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;
auto CommunityEntry = m_Entries.find(CCommunityId(m_pCommunityCache->CountryTypeFilterKey()));
if(CommunityEntry == m_Entries.end())
return false;
});
const auto &CountryEntries = CommunityEntry->second;
return !IsSubsetEquals(m_pCommunityCache->SelectableCountries(), CountryEntries) &&
CountryEntries.find(CCommunityCountryName(pCountryName)) != CountryEntries.end();
}
bool CExcludedCommunityCountryFilterList::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;
auto CommunityEntry = m_Entries.find(CCommunityId(m_pCommunityCache->CountryTypeFilterKey()));
return CommunityEntry == m_Entries.end() ||
CommunityEntry->second.empty() ||
IsSubsetEquals(m_pCommunityCache->SelectableCountries(), CommunityEntry->second);
}
void CExcludedCommunityCountryFilterList::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) {
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;
}) != vAllowedCommunities.end();
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
@ -1998,13 +2099,18 @@ void CExcludedCommunityCountryFilterList::Save(IConfigManager *pConfigManager) c
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)
@ -2019,10 +2125,7 @@ void CExcludedCommunityTypeFilterList::Add(const char *pCommunityId, const char
void CExcludedCommunityTypeFilterList::Remove(const char *pTypeName)
{
for(const CCommunity *pCommunity : m_CurrentCommunitiesGetter())
{
Remove(pCommunity->Id(), pTypeName);
}
Remove(m_pCommunityCache->CountryTypeFilterKey(), 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()
{
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());
if(CommunityEntry != m_Entries.end())
{
CommunityEntry->second.clear();
}
CommunityEntry->second.clear();
}
}
bool CExcludedCommunityTypeFilterList::Filtered(const char *pTypeName) const
{
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(m_pCommunityCache->CountryTypeFilterKey()));
if(CommunityEntry == m_Entries.end())
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();
});
const auto &TypeEntries = CommunityEntry->second;
return !IsSubsetEquals(m_pCommunityCache->SelectableTypes(), TypeEntries) &&
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;
auto CommunityEntry = m_Entries.find(CCommunityId(m_pCommunityCache->CountryTypeFilterKey()));
return CommunityEntry == m_Entries.end() ||
CommunityEntry->second.empty() ||
IsSubsetEquals(m_pCommunityCache->SelectableTypes(), CommunityEntry->second);
}
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) {
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;
}) != vAllowedCommunities.end();
if(Found)
@ -2106,13 +2200,43 @@ void CExcludedCommunityTypeFilterList::Clean(const std::vector<CCommunity> &vAll
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())
{
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
@ -2148,7 +2272,7 @@ bool CServerBrowser::IsRegistered(const NETADDR &Addr)
const int NumServers = m_pHttp->NumServers();
for(int i = 0; i < NumServers; i++)
{
const CServerInfo Info = m_pHttp->Server(i);
const CServerInfo &Info = m_pHttp->Server(i);
for(int j = 0; j < Info.m_NumAddresses; j++)
{
if(net_addr_comp(&Info.m_aAddresses[j], &Addr) == 0)

View file

@ -160,8 +160,8 @@ private:
class CExcludedCommunityCountryFilterList : public IFilterList
{
public:
CExcludedCommunityCountryFilterList(std::function<std::vector<const CCommunity *>()> CurrentCommunitiesGetter) :
m_CurrentCommunitiesGetter(CurrentCommunitiesGetter)
CExcludedCommunityCountryFilterList(const ICommunityCache *pCommunityCache) :
m_pCommunityCache(pCommunityCache)
{
}
@ -176,15 +176,15 @@ public:
void Save(IConfigManager *pConfigManager) const;
private:
std::function<std::vector<const CCommunity *>()> m_CurrentCommunitiesGetter;
const ICommunityCache *m_pCommunityCache;
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)
CExcludedCommunityTypeFilterList(const ICommunityCache *pCommunityCache) :
m_pCommunityCache(pCommunityCache)
{
}
@ -199,10 +199,38 @@ public:
void Save(IConfigManager *pConfigManager) const;
private:
std::function<std::vector<const CCommunity *>()> m_CurrentCommunitiesGetter;
const ICommunityCache *m_pCommunityCache;
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
{
public:
@ -255,6 +283,8 @@ public:
bool DDNetInfoAvailable() const override { return m_pDDNetInfo != nullptr; }
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; }
CExcludedCommunityFilterList &CommunitiesFilter() override { return m_CommunitiesFilter; }
CExcludedCommunityCountryFilterList &CountriesFilter() override { return m_CountriesFilter; }
@ -307,6 +337,7 @@ private:
int m_OwnLocation = CServerInfo::LOC_UNKNOWN;
CCommunityCache m_CommunityCache;
CFavoriteCommunityFilterList m_FavoriteCommunitiesFilter;
CExcludedCommunityFilterList m_CommunitiesFilter;
CExcludedCommunityCountryFilterList m_CountriesFilter;

View file

@ -248,6 +248,18 @@ public:
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
{
MACRO_INTERFACE("serverbrowser")
@ -286,6 +298,11 @@ public:
static constexpr const char *COMMUNITY_DDNET = "ddnet";
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 = ";";
@ -313,6 +330,8 @@ public:
virtual bool DDNetInfoAvailable() const = 0;
virtual SHA256_DIGEST DDNetInfoSha256() const = 0;
virtual ICommunityCache &CommunityCache() = 0;
virtual const ICommunityCache &CommunityCache() const = 0;
virtual IFilterList &FavoriteCommunitiesFilter() = 0;
virtual IFilterList &CommunitiesFilter() = 0;
virtual IFilterList &CountriesFilter() = 0;

View file

@ -663,12 +663,6 @@ void CMenus::RenderMenubar(CUIRect Box, IClient::EClientState ClientState)
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(ServerBrowser()->GetCurrentType() != IServerBrowser::TYPE_INTERNET)
{
if(ServerBrowser()->GetCurrentType() == IServerBrowser::TYPE_LAN)
Client()->RequestDDNetInfo();
ServerBrowser()->Refresh(IServerBrowser::TYPE_INTERNET);
}
NewPage = PAGE_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;
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;
}
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;
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;
}
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;
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;
}
GameClient()->m_Tooltips.DoToolTip(&s_aFavoriteCommunityButtons[FavoriteCommunityIndex], &Button, pCommunity->Name());
@ -1036,7 +1015,7 @@ void CMenus::Render()
static int s_Frame = 0;
if(s_Frame == 0)
{
RefreshBrowserTab(g_Config.m_UiPage);
RefreshBrowserTab(true);
s_Frame++;
}
else if(s_Frame == 1)
@ -1851,7 +1830,7 @@ void CMenus::RenderPopupConnecting(CUIRect Screen)
{
Client()->Disconnect();
Ui()->SetActiveItem(nullptr);
RefreshBrowserTab(g_Config.m_UiPage);
RefreshBrowserTab(true);
}
}
@ -1970,7 +1949,7 @@ void CMenus::RenderPopupLoading(CUIRect Screen)
{
Client()->Disconnect();
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)
{
const int OldPage = m_MenuPage;
m_MenuPage = NewPage;
if(NewPage >= PAGE_INTERNET && NewPage <= PAGE_FAVORITE_COMMUNITY_5)
{
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();
ServerBrowser()->Refresh(IServerBrowser::TYPE_INTERNET);
if(Force || ServerBrowser()->GetCurrentType() != 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();
ServerBrowser()->Refresh(IServerBrowser::TYPE_FAVORITES);
if(Force || ServerBrowser()->GetCurrentType() != 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();
ServerBrowser()->Refresh(UiPage - PAGE_FAVORITE_COMMUNITY_1 + IServerBrowser::TYPE_FAVORITE_COMMUNITY_1);
const int BrowserType = g_Config.m_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 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
{
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);
// community icons
@ -773,7 +762,7 @@ public:
private:
static int GhostlistFetchCallback(const CFsFileInfo *pInfo, int IsDir, int StorageType, void *pUser);
void SetMenuPage(int NewPage);
void RefreshBrowserTab(int UiPage);
void RefreshBrowserTab(bool Force);
// found in menus_ingame.cpp
void RenderInGameNetwork(CUIRect MainView);

View file

@ -591,7 +591,7 @@ void CMenus::RenderServerbrowserStatusBox(CUIRect StatusBox, bool WasListboxItem
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()))))
{
RefreshBrowserTab(g_Config.m_UiPage);
RefreshBrowserTab(true);
}
}
@ -715,7 +715,7 @@ void CMenus::RenderServerbrowserFilters(CUIRect View)
g_Config.m_BrFilterConnectingPlayers ^= 1;
// map finish filters
if(m_CommunityCache.m_AnyRanksAvailable)
if(ServerBrowser()->CommunityCache().AnyRanksAvailable())
{
View.HSplitTop(RowHeight, &Button, &View);
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)
if((!m_CommunityCache.m_vpSelectableCountries.empty() || !m_CommunityCache.m_vpSelectableTypes.empty()) &&
(m_CommunityCache.m_vpSelectedCommunities.size() != 1 || str_comp(m_CommunityCache.m_vpSelectedCommunities[0]->Id(), IServerBrowser::COMMUNITY_NONE) != 0))
// countries and types filters
if(ServerBrowser()->CommunityCache().CountriesTypesFilterAvailable())
{
const ColorRGBA ColorActive = ColorRGBA(0.0f, 0.0f, 0.0f, 0.3f);
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(m_CommunityCache.m_AnyRanksAvailable)
if(ServerBrowser()->CommunityCache().AnyRanksAvailable())
{
g_Config.m_BrFilterUnfinishedMap = 0;
}
@ -891,10 +890,14 @@ void CMenus::RenderServerbrowserDDNetFilter(CUIRect View,
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)
{
Filter.Clear();
for(int j = 0; j < MaxItems; ++j)
{
Filter.Remove(GetItemName(j));
}
}
else if(Active)
{
@ -912,8 +915,11 @@ void CMenus::RenderServerbrowserDDNetFilter(CUIRect View,
}
else if(Click == 3)
{
// middle click to reset (re-enable all)
Filter.Clear();
// middle click to reset (re-enable all currently selectable items)
for(int j = 0; j < MaxItems; ++j)
{
Filter.Remove(GetItemName(j));
}
Client()->ServerBrowserUpdate();
if(UpdateCommunityCacheOnChange)
UpdateCommunityCache(true);
@ -991,7 +997,7 @@ void CMenus::RenderServerbrowserCommunitiesFilter(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;
static CScrollRegion s_ScrollRegion;
@ -1001,14 +1007,14 @@ void CMenus::RenderServerbrowserCountriesFilter(CUIRect View)
const float Spacing = 2.0f;
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) {
Item.Margin(Spacing, &Item);
const float OldWidth = Item.w;
Item.w = Item.h * 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);
@ -1016,7 +1022,7 @@ void CMenus::RenderServerbrowserCountriesFilter(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;
static CScrollRegion s_ScrollRegion;
@ -1026,7 +1032,7 @@ void CMenus::RenderServerbrowserTypesFilter(CUIRect View)
const float Spacing = 2.0f;
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) {
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)
{
const int OldPage = g_Config.m_UiPage;
pfnCallback(pResult, pCallbackUserData);
CMenus *pThis = static_cast<CMenus *>(pUserData);
if(pResult->NumArguments() >= 1)
@ -1861,11 +1866,6 @@ void CMenus::ConchainUiPageUpdate(IConsole::IResult *pResult, void *pUserData, I
}
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,
// 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);
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_5)
else
{
// 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);
ServerBrowser()->CommunityCache().Update(Force);
}
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) :

View file

@ -64,7 +64,7 @@ void CMenus::RenderGame(CUIRect MainView)
else
{
Client()->Disconnect();
RefreshBrowserTab(g_Config.m_UiPage);
RefreshBrowserTab(true);
}
}
@ -847,12 +847,6 @@ 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(ServerBrowser()->GetCurrentType() != IServerBrowser::TYPE_INTERNET)
{
if(ServerBrowser()->GetCurrentType() == IServerBrowser::TYPE_LAN)
Client()->RequestDDNetInfo();
ServerBrowser()->Refresh(IServerBrowser::TYPE_INTERNET);
}
NewPage = PAGE_INTERNET;
}
GameClient()->m_Tooltips.DoToolTip(&s_InternetButton, &Button, Localize("Internet"));
@ -861,8 +855,6 @@ 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(ServerBrowser()->GetCurrentType() != IServerBrowser::TYPE_LAN)
ServerBrowser()->Refresh(IServerBrowser::TYPE_LAN);
NewPage = PAGE_LAN;
}
GameClient()->m_Tooltips.DoToolTip(&s_LanButton, &Button, Localize("LAN"));
@ -871,12 +863,6 @@ 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(ServerBrowser()->GetCurrentType() != IServerBrowser::TYPE_FAVORITES)
{
if(ServerBrowser()->GetCurrentType() == IServerBrowser::TYPE_LAN)
Client()->RequestDDNetInfo();
ServerBrowser()->Refresh(IServerBrowser::TYPE_FAVORITES);
}
NewPage = PAGE_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;
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());

View file

@ -72,7 +72,7 @@ void CMenus::RenderStartMenu(CUIRect MainView)
// 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);
RefreshBrowserTab(true);
const char *pAddr = ServerBrowser()->GetTutorialServer();
if(pAddr)
{