Merge pull request #7459 from heinrich5991/pr_ddnet_community_json

Add icon URL, remove `servers-key`/`ranks-key` from community JSON
This commit is contained in:
Robert Müller 2023-11-18 21:08:23 +00:00 committed by GitHub
commit ee04cd4dda
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 157 additions and 132 deletions

View file

@ -94,7 +94,6 @@ protected:
TMapLoadingCallbackFunc m_MapLoadingCBFunc;
char m_aNews[3000];
char m_aCommunityIconsDownloadUrl[256];
int m_Points;
int64_t m_ReconnectTime;
@ -258,7 +257,6 @@ public:
virtual unsigned GetCurrentMapCrc() const = 0;
const char *News() const { return m_aNews; }
const char *CommunityIconsDownloadUrl() const { return m_aCommunityIconsDownloadUrl; }
int Points() const { return m_Points; }
int64_t ReconnectTime() const { return m_ReconnectTime; }
void SetReconnectTime(int64_t ReconnectTime) { m_ReconnectTime = ReconnectTime; }

View file

@ -135,7 +135,6 @@ CClient::CClient() :
m_pDDNetInfoTask = NULL;
m_aNews[0] = '\0';
m_aMapDownloadUrl[0] = '\0';
m_aCommunityIconsDownloadUrl[0] = '\0';
m_Points = -1;
m_CurrentServerInfoRequestTime = -1;
@ -2245,12 +2244,6 @@ void CClient::LoadDDNetInfo()
str_copy(m_aMapDownloadUrl, MapDownloadUrl);
}
const json_value &CommunityIconsDownloadUrl = DDNetInfo["community-icons-download-url"];
if(CommunityIconsDownloadUrl.type == json_string)
{
str_copy(m_aCommunityIconsDownloadUrl, CommunityIconsDownloadUrl);
}
const json_value &Points = DDNetInfo["points"];
if(Points.type == json_integer)
{

View file

@ -1141,6 +1141,91 @@ void CServerBrowser::LoadDDNetLocation()
}
}
bool CServerBrowser::ParseCommunityServers(CCommunity *pCommunity, const json_value &Servers)
{
for(unsigned ServerIndex = 0; ServerIndex < Servers.u.array.length; ++ServerIndex)
{
// pServer - { name, flagId, servers }
const json_value &Server = Servers[ServerIndex];
if(Server.type != json_object)
{
log_error("serverbrowser", "invalid server (ServerIndex=%u)", ServerIndex);
return false;
}
const json_value &Name = Server["name"];
const json_value &FlagId = Server["flagId"];
const json_value &Types = Server["servers"];
if(Name.type != json_string || FlagId.type != json_integer || Types.type != json_object)
{
log_error("serverbrowser", "invalid server attribute (ServerIndex=%u)", ServerIndex);
return false;
}
if(Types.u.object.length == 0)
continue;
pCommunity->m_vCountries.emplace_back(Name.u.string.ptr, FlagId.u.integer);
CCommunityCountry *pCountry = &pCommunity->m_vCountries.back();
for(unsigned TypeIndex = 0; TypeIndex < Types.u.object.length; ++TypeIndex)
{
const json_value &Addresses = *Types.u.object.values[TypeIndex].value;
if(Addresses.type != json_array)
{
log_error("serverbrowser", "invalid addresses (ServerIndex=%u, TypeIndex=%u)", ServerIndex, TypeIndex);
return false;
}
if(Addresses.u.array.length == 0)
continue;
const char *pTypeName = Types.u.object.values[TypeIndex].name;
// add type if it doesn't exist already
const auto CommunityType = std::find_if(pCommunity->m_vTypes.begin(), pCommunity->m_vTypes.end(), [pTypeName](const auto &Elem) {
return str_comp(Elem.Name(), pTypeName) == 0;
});
if(CommunityType == pCommunity->m_vTypes.end())
{
pCommunity->m_vTypes.emplace_back(pTypeName);
}
// add addresses
for(unsigned AddressIndex = 0; AddressIndex < Addresses.u.array.length; ++AddressIndex)
{
const json_value &Address = Addresses[AddressIndex];
if(Address.type != json_string)
{
log_error("serverbrowser", "invalid address (ServerIndex=%u, TypeIndex=%u, AddressIndex=%u)", ServerIndex, TypeIndex, AddressIndex);
return false;
}
NETADDR NetAddr;
if(net_addr_from_str(&NetAddr, Address.u.string.ptr))
{
log_error("serverbrowser", "invalid address (ServerIndex=%u, TypeIndex=%u, AddressIndex=%u)", ServerIndex, TypeIndex, AddressIndex);
continue;
}
pCountry->m_vServers.emplace_back(NetAddr, pTypeName);
}
}
}
return true;
}
bool CServerBrowser::ParseCommunityFinishes(CCommunity *pCommunity, const json_value &Finishes)
{
for(unsigned FinishIndex = 0; FinishIndex < Finishes.u.array.length; ++FinishIndex)
{
const json_value &Finish = Finishes[FinishIndex];
if(Finish.type != json_string)
{
log_error("serverbrowser", "invalid rank (FinishIndex=%u)", FinishIndex);
return false;
}
pCommunity->m_FinishedMaps.emplace((const char *)Finish);
}
return true;
}
void CServerBrowser::LoadDDNetServers()
{
// Parse communities
@ -1151,136 +1236,90 @@ void CServerBrowser::LoadDDNetServers()
return;
const json_value &Communities = (*m_pDDNetInfo)["communities"];
if(Communities.type != json_object)
if(Communities.type != json_array)
return;
for(unsigned CommunityIndex = 0; CommunityIndex < Communities.u.object.length; ++CommunityIndex)
for(unsigned CommunityIndex = 0; CommunityIndex < Communities.u.array.length; ++CommunityIndex)
{
const char *pCommunityId = Communities.u.object.values[CommunityIndex].name;
const json_value &Community = *Communities.u.object.values[CommunityIndex].value;
const json_value &Community = Communities[CommunityIndex];
if(Community.type != json_object)
{
log_error("serverbrowser", "invalid community (CommunityId=%s)", pCommunityId);
log_error("serverbrowser", "invalid community (CommunityIndex=%d)", (int)CommunityIndex);
continue;
}
const json_value &Name = Community["name"];
const json_value &JsonServersKey = Community["servers-key"];
const json_value &JsonRanksKey = Community["ranks-key"];
const json_value &IconSha256 = Community["icon-sha256"];
if(Name.type != json_string || JsonServersKey.type != json_string || JsonRanksKey.type != json_string || IconSha256.type != json_string)
const json_value &Id = Community["id"];
if(Id.type != json_string)
{
log_error("serverbrowser", "invalid community attribute (CommunityId=%s)", pCommunityId);
log_error("serverbrowser", "invalid community id (CommunityIndex=%d)", (int)CommunityIndex);
continue;
}
const json_value &Icon = Community["icon"];
const json_value &IconSha256 = Icon["sha256"];
const json_value &IconUrl = Icon["url"];
const json_value &Name = Community["name"];
const json_value *pFinishes = &Icon["finishes"];
const json_value *pServers = &Icon["servers"];
if(pFinishes->type == json_none)
{
if(str_comp(Id, COMMUNITY_DDNET) == 0)
{
pFinishes = &(*m_pDDNetInfo)["maps"];
}
}
// Backward compatibility.
if(pServers->type == json_none)
{
if(str_comp(Id, COMMUNITY_DDNET) == 0)
{
pServers = &(*m_pDDNetInfo)["servers"];
}
else if(str_comp(Id, "kog") == 0)
{
pServers = &(*m_pDDNetInfo)["servers-kog"];
}
}
if(false ||
Icon.type != json_object ||
IconSha256.type != json_string ||
IconUrl.type != json_string ||
Name.type != json_string ||
(pFinishes->type != json_array && pFinishes->type != json_none) ||
pServers->type != json_array)
{
log_error("serverbrowser", "invalid community attribute (CommunityId=%s)", (const char *)Id);
continue;
}
SHA256_DIGEST ParsedIconSha256;
if(sha256_from_str(&ParsedIconSha256, IconSha256.u.string.ptr) != 0)
if(sha256_from_str(&ParsedIconSha256, IconSha256) != 0)
{
log_error("serverbrowser", "invalid community icon sha256 (CommunityId=%s)", pCommunityId);
log_error("serverbrowser", "invalid community icon sha256 (CommunityId=%s)", (const char *)Id);
continue;
}
m_vCommunities.emplace_back(pCommunityId, Name.u.string.ptr, JsonServersKey.u.string.ptr, JsonRanksKey.u.string.ptr, ParsedIconSha256);
}
// Parse finishes for each community
for(auto &Community : m_vCommunities)
{
if(!Community.HasRanks())
continue;
const json_value &Ranks = (*m_pDDNetInfo)[Community.JsonRanksKey()];
if(Ranks.type != json_array)
CCommunity NewCommunity(Id, Name, ParsedIconSha256, IconUrl);
if(ParseCommunityServers(&NewCommunity, *pServers))
{
log_error("serverbrowser", "invalid community ranks (CommunityId=%s)", Community.Id());
log_error("serverbrowser", "invalid community servers (CommunityId=%s)", NewCommunity.Id());
continue;
}
NewCommunity.m_HasFinishes = pFinishes->type == json_array;
if(NewCommunity.m_HasFinishes && ParseCommunityFinishes(&NewCommunity, *pFinishes))
{
log_error("serverbrowser", "invalid community finishes (CommunityId=%s)", NewCommunity.Id());
continue;
}
for(unsigned RankIndex = 0; RankIndex < Ranks.u.array.length; ++RankIndex)
for(const auto &Country : NewCommunity.Countries())
{
const json_value &Rank = *Ranks.u.array.values[RankIndex];
if(Rank.type != json_string)
for(const auto &Server : Country.Servers())
{
log_error("serverbrowser", "invalid rank (RankIndex=%u)", RankIndex);
continue;
}
Community.m_RankedMaps.emplace(Rank.u.string.ptr);
}
}
// parse servers for each community
for(auto &Community : m_vCommunities)
{
const json_value &Servers = (*m_pDDNetInfo)[Community.JsonServersKey()];
if(Servers.type != json_array)
{
log_error("serverbrowser", "invalid community servers (CommunityId=%s)", Community.Id());
continue;
}
for(unsigned ServerIndex = 0; ServerIndex < Servers.u.array.length; ++ServerIndex)
{
// pServer - { name, flagId, servers }
const json_value &Server = *Servers.u.array.values[ServerIndex];
if(Server.type != json_object)
{
log_error("serverbrowser", "invalid server (ServerIndex=%u)", ServerIndex);
continue;
}
const json_value &Name = Server["name"];
const json_value &FlagId = Server["flagId"];
const json_value &Types = Server["servers"];
if(Name.type != json_string || FlagId.type != json_integer || Types.type != json_object)
{
log_error("serverbrowser", "invalid server attribute (ServerIndex=%u)", ServerIndex);
continue;
}
if(Types.u.object.length == 0)
continue;
Community.m_vCountries.emplace_back(Name.u.string.ptr, FlagId.u.integer);
CCommunityCountry *pCountry = &Community.m_vCountries.back();
for(unsigned TypeIndex = 0; TypeIndex < Types.u.object.length; ++TypeIndex)
{
const json_value &Addresses = *Types.u.object.values[TypeIndex].value;
if(Addresses.type != json_array)
{
log_error("serverbrowser", "invalid addresses (ServerIndex=%u, TypeIndex=%u)", ServerIndex, TypeIndex);
continue;
}
if(Addresses.u.array.length == 0)
continue;
const char *pTypeName = Types.u.object.values[TypeIndex].name;
// add type if it doesn't exist already
const auto CommunityType = std::find_if(Community.m_vTypes.begin(), Community.m_vTypes.end(), [pTypeName](const auto &Elem) {
return str_comp(Elem.Name(), pTypeName) == 0;
});
if(CommunityType == Community.m_vTypes.end())
{
Community.m_vTypes.emplace_back(pTypeName);
}
// add addresses
for(unsigned AddressIndex = 0; AddressIndex < Addresses.u.array.length; ++AddressIndex)
{
const json_value &Address = *Addresses.u.array.values[AddressIndex];
if(Address.type != json_string)
{
log_error("serverbrowser", "invalid address (ServerIndex=%u, TypeIndex=%u, AddressIndex=%u)", ServerIndex, TypeIndex, AddressIndex);
continue;
}
NETADDR NetAddr;
net_addr_from_str(&NetAddr, Address.u.string.ptr);
pCountry->m_vServers.emplace_back(NetAddr, pTypeName);
m_CommunityServersByAddr.emplace(std::make_pair(NetAddr, CCommunityServer(Community.Id(), pCountry->Name(), pTypeName)));
}
m_CommunityServersByAddr.emplace(Server.Address(), CCommunityServer(NewCommunity.Id(), Country.Name(), Server.TypeName()));
}
}
m_vCommunities.push_back(std::move(NewCommunity));
}
// Add default none community
m_vCommunities.emplace_back(COMMUNITY_NONE, "None", "", "", SHA256_ZEROED);
m_vCommunities.emplace_back(COMMUNITY_NONE, "None", SHA256_ZEROED, "");
// Remove unknown elements from exclude lists
CleanFilters();
@ -1391,10 +1430,10 @@ int CServerBrowser::LoadingProgression() const
CServerInfo::ERankState CCommunity::HasRank(const char *pMap) const
{
if(!HasRanks() || pMap[0] == '\0')
if(!HasRanks())
return CServerInfo::RANK_UNAVAILABLE;
const CCommunityMap Needle(pMap);
return m_RankedMaps.count(Needle) == 0 ? CServerInfo::RANK_UNRANKED : CServerInfo::RANK_RANKED;
return m_FinishedMaps.count(Needle) == 0 ? CServerInfo::RANK_UNRANKED : CServerInfo::RANK_RANKED;
}
const std::vector<CCommunity> &CServerBrowser::Communities() const

View file

@ -219,6 +219,9 @@ private:
void SetInfo(CServerEntry *pEntry, const CServerInfo &Info);
void SetLatency(NETADDR Addr, int Latency);
static bool ParseCommunityFinishes(CCommunity *pCommunity, const json_value &Finishes);
static bool ParseCommunityServers(CCommunity *pCommunity, const json_value &Servers);
};
#endif

View file

@ -209,31 +209,29 @@ class CCommunity
char m_aId[CServerInfo::MAX_COMMUNITY_ID_LENGTH];
char m_aName[64];
char m_aJsonServersKey[32];
char m_aJsonRanksKey[32];
SHA256_DIGEST m_IconSha256;
char m_aIconUrl[128];
std::vector<CCommunityCountry> m_vCountries;
std::vector<CCommunityType> m_vTypes;
std::unordered_set<CCommunityMap, CCommunityMap::SHash> m_RankedMaps;
bool m_HasFinishes = false;
std::unordered_set<CCommunityMap, CCommunityMap::SHash> m_FinishedMaps;
public:
CCommunity(const char *pId, const char *pName, const char *pJsonServersKey, const char *pJsonRanksKey, SHA256_DIGEST IconSha256) :
CCommunity(const char *pId, const char *pName, SHA256_DIGEST IconSha256, const char *pIconUrl) :
m_IconSha256(IconSha256)
{
str_copy(m_aId, pId);
str_copy(m_aName, pName);
str_copy(m_aJsonServersKey, pJsonServersKey);
str_copy(m_aJsonRanksKey, pJsonRanksKey);
str_copy(m_aIconUrl, pIconUrl);
}
const char *Id() const { return m_aId; }
const char *Name() const { return m_aName; }
const char *JsonServersKey() const { return m_aJsonServersKey; }
const char *JsonRanksKey() const { return m_aJsonRanksKey; }
const char *IconUrl() const { return m_aIconUrl; }
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 HasRanks() const { return m_aJsonRanksKey[0] != '\0'; }
bool HasRanks() const { return m_HasFinishes; }
CServerInfo::ERankState HasRank(const char *pMap) const;
};

View file

@ -1989,10 +1989,6 @@ void CMenus::UpdateCommunityIcons()
}
}
const char *pDownloadUrl = Client()->CommunityIconsDownloadUrl();
if(pDownloadUrl[0] == '\0')
return;
// Find added and updated community icons
for(const auto &Community : ServerBrowser()->Communities())
{
@ -2006,9 +2002,7 @@ void CMenus::UpdateCommunityIcons()
});
if(pExistingDownload == m_CommunityIconDownloadJobs.end() && (ExistingIcon == m_vCommunityIcons.end() || ExistingIcon->m_Sha256 != Community.IconSha256()))
{
char aUrl[256];
str_format(aUrl, sizeof(aUrl), "%s/%s.png", pDownloadUrl, Community.Id());
std::shared_ptr<CCommunityIconDownloadJob> pJob = std::make_shared<CCommunityIconDownloadJob>(this, Community.Id(), aUrl);
std::shared_ptr<CCommunityIconDownloadJob> pJob = std::make_shared<CCommunityIconDownloadJob>(this, Community.Id(), Community.IconUrl());
Engine()->AddJob(pJob);
m_CommunityIconDownloadJobs.push_back(pJob);
}