mirror of
https://github.com/ddnet/ddnet.git
synced 2024-11-13 11:38:19 +00:00
44b1014f41
One from each country and so on instead of doing one country fully first before moving to the next. This makes loading servers like Japan and AUS much faster since they're at the back of the list. This also reduces the peak number of packets sent to each IP at once.
1383 lines
37 KiB
C++
1383 lines
37 KiB
C++
/* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */
|
|
/* If you are missing that file, acquire a complete release at teeworlds.com. */
|
|
#include <algorithm> // sort TODO: remove this
|
|
|
|
#include <base/hash_ctxt.h>
|
|
#include <base/math.h>
|
|
#include <base/system.h>
|
|
|
|
#include <engine/shared/config.h>
|
|
#include <engine/shared/json.h>
|
|
#include <engine/shared/memheap.h>
|
|
#include <engine/shared/network.h>
|
|
#include <engine/shared/protocol.h>
|
|
|
|
#include <engine/config.h>
|
|
#include <engine/console.h>
|
|
#include <engine/friends.h>
|
|
#include <engine/masterserver.h>
|
|
#include <engine/storage.h>
|
|
|
|
#include <mastersrv/mastersrv.h>
|
|
|
|
#include <engine/external/json-parser/json.h>
|
|
|
|
#include "serverbrowser.h"
|
|
class SortWrap
|
|
{
|
|
typedef bool (CServerBrowser::*SortFunc)(int, int) const;
|
|
SortFunc m_pfnSort;
|
|
CServerBrowser *m_pThis;
|
|
public:
|
|
SortWrap(CServerBrowser *t, SortFunc f) : m_pfnSort(f), m_pThis(t) {}
|
|
bool operator()(int a, int b) { return (g_Config.m_BrSortOrder ? (m_pThis->*m_pfnSort)(b, a) : (m_pThis->*m_pfnSort)(a, b)); }
|
|
};
|
|
|
|
CServerBrowser::CServerBrowser()
|
|
{
|
|
m_pMasterServer = 0;
|
|
m_ppServerlist = 0;
|
|
m_pSortedServerlist = 0;
|
|
|
|
m_NumFavoriteServers = 0;
|
|
|
|
mem_zero(m_aServerlistIp, sizeof(m_aServerlistIp));
|
|
|
|
m_pFirstReqServer = 0; // request list
|
|
m_pLastReqServer = 0;
|
|
m_NumRequests = 0;
|
|
|
|
m_NeedRefresh = 0;
|
|
|
|
m_NumSortedServers = 0;
|
|
m_NumSortedServersCapacity = 0;
|
|
m_NumServers = 0;
|
|
m_NumServerCapacity = 0;
|
|
|
|
m_Sorthash = 0;
|
|
m_aFilterString[0] = 0;
|
|
m_aFilterGametypeString[0] = 0;
|
|
|
|
m_ServerlistType = 0;
|
|
m_BroadcastTime = 0;
|
|
secure_random_fill(m_aTokenSeed, sizeof(m_aTokenSeed));
|
|
m_RequestNumber = 0;
|
|
|
|
m_pDDNetInfo = 0;
|
|
}
|
|
|
|
CServerBrowser::~CServerBrowser()
|
|
{
|
|
if(m_pDDNetInfo)
|
|
json_value_free(m_pDDNetInfo);
|
|
}
|
|
|
|
void CServerBrowser::SetBaseInfo(class CNetClient *pClient, const char *pNetVersion)
|
|
{
|
|
m_pNetClient = pClient;
|
|
str_copy(m_aNetVersion, pNetVersion, sizeof(m_aNetVersion));
|
|
m_pMasterServer = Kernel()->RequestInterface<IMasterServer>();
|
|
m_pConsole = Kernel()->RequestInterface<IConsole>();
|
|
m_pFriends = Kernel()->RequestInterface<IFriends>();
|
|
IConfig *pConfig = Kernel()->RequestInterface<IConfig>();
|
|
if(pConfig)
|
|
pConfig->RegisterCallback(ConfigSaveCallback, this);
|
|
}
|
|
|
|
const CServerInfo *CServerBrowser::SortedGet(int Index) const
|
|
{
|
|
if(Index < 0 || Index >= m_NumSortedServers)
|
|
return 0;
|
|
return &m_ppServerlist[m_pSortedServerlist[Index]]->m_Info;
|
|
}
|
|
|
|
int CServerBrowser::GenerateToken(const NETADDR &Addr) const
|
|
{
|
|
SHA256_CTX Sha256;
|
|
sha256_init(&Sha256);
|
|
sha256_update(&Sha256, m_aTokenSeed, sizeof(m_aTokenSeed));
|
|
sha256_update(&Sha256, (unsigned char *)&Addr, sizeof(Addr));
|
|
SHA256_DIGEST Digest = sha256_finish(&Sha256);
|
|
return (Digest.data[0] << 16) | (Digest.data[1] << 8) | Digest.data[2];
|
|
}
|
|
|
|
int CServerBrowser::GetBasicToken(int Token)
|
|
{
|
|
return Token & 0xff;
|
|
}
|
|
|
|
int CServerBrowser::GetExtraToken(int Token)
|
|
{
|
|
return Token >> 8;
|
|
}
|
|
|
|
bool CServerBrowser::SortCompareName(int Index1, int Index2) const
|
|
{
|
|
CServerEntry *a = m_ppServerlist[Index1];
|
|
CServerEntry *b = m_ppServerlist[Index2];
|
|
// make sure empty entries are listed last
|
|
return (a->m_GotInfo && b->m_GotInfo) || (!a->m_GotInfo && !b->m_GotInfo) ? str_comp(a->m_Info.m_aName, b->m_Info.m_aName) < 0 :
|
|
a->m_GotInfo ? true : false;
|
|
}
|
|
|
|
bool CServerBrowser::SortCompareMap(int Index1, int Index2) const
|
|
{
|
|
CServerEntry *a = m_ppServerlist[Index1];
|
|
CServerEntry *b = m_ppServerlist[Index2];
|
|
return str_comp(a->m_Info.m_aMap, b->m_Info.m_aMap) < 0;
|
|
}
|
|
|
|
bool CServerBrowser::SortComparePing(int Index1, int Index2) const
|
|
{
|
|
CServerEntry *a = m_ppServerlist[Index1];
|
|
CServerEntry *b = m_ppServerlist[Index2];
|
|
return a->m_Info.m_Latency < b->m_Info.m_Latency;
|
|
}
|
|
|
|
bool CServerBrowser::SortCompareGametype(int Index1, int Index2) const
|
|
{
|
|
CServerEntry *a = m_ppServerlist[Index1];
|
|
CServerEntry *b = m_ppServerlist[Index2];
|
|
return str_comp(a->m_Info.m_aGameType, b->m_Info.m_aGameType) < 0;
|
|
}
|
|
|
|
bool CServerBrowser::SortCompareNumPlayers(int Index1, int Index2) const
|
|
{
|
|
CServerEntry *a = m_ppServerlist[Index1];
|
|
CServerEntry *b = m_ppServerlist[Index2];
|
|
return a->m_Info.m_NumFilteredPlayers > b->m_Info.m_NumFilteredPlayers;
|
|
}
|
|
|
|
bool CServerBrowser::SortCompareNumClients(int Index1, int Index2) const
|
|
{
|
|
CServerEntry *a = m_ppServerlist[Index1];
|
|
CServerEntry *b = m_ppServerlist[Index2];
|
|
return a->m_Info.m_NumClients > b->m_Info.m_NumClients;
|
|
}
|
|
|
|
bool CServerBrowser::SortCompareNumPlayersAndPing(int Index1, int Index2) const
|
|
{
|
|
CServerEntry *a = m_ppServerlist[Index1];
|
|
CServerEntry *b = m_ppServerlist[Index2];
|
|
|
|
if(a->m_Info.m_NumFilteredPlayers == b->m_Info.m_NumFilteredPlayers)
|
|
return a->m_Info.m_Latency > b->m_Info.m_Latency;
|
|
if(a->m_Info.m_NumFilteredPlayers == 0 || b->m_Info.m_NumFilteredPlayers == 0)
|
|
return a->m_Info.m_NumFilteredPlayers == 0;
|
|
else
|
|
return a->m_Info.m_NumFilteredPlayers - (a->m_Info.m_Latency/100)*MAX_CLIENTS < b->m_Info.m_NumFilteredPlayers - (b->m_Info.m_Latency/100)*MAX_CLIENTS;
|
|
}
|
|
|
|
void CServerBrowser::Filter()
|
|
{
|
|
int i = 0, p = 0;
|
|
m_NumSortedServers = 0;
|
|
|
|
// allocate the sorted list
|
|
if(m_NumSortedServersCapacity < m_NumServers)
|
|
{
|
|
if(m_pSortedServerlist)
|
|
free(m_pSortedServerlist);
|
|
m_NumSortedServersCapacity = m_NumServers;
|
|
m_pSortedServerlist = (int *)calloc(m_NumSortedServersCapacity, sizeof(int));
|
|
}
|
|
|
|
// filter the servers
|
|
for(i = 0; i < m_NumServers; i++)
|
|
{
|
|
int Filtered = 0;
|
|
|
|
if(g_Config.m_BrFilterEmpty && m_ppServerlist[i]->m_Info.m_NumFilteredPlayers == 0)
|
|
Filtered = 1;
|
|
else if(g_Config.m_BrFilterFull && Players(m_ppServerlist[i]->m_Info) == Max(m_ppServerlist[i]->m_Info))
|
|
Filtered = 1;
|
|
else if(g_Config.m_BrFilterPw && m_ppServerlist[i]->m_Info.m_Flags&SERVER_FLAG_PASSWORD)
|
|
Filtered = 1;
|
|
else if(g_Config.m_BrFilterPure &&
|
|
(str_comp(m_ppServerlist[i]->m_Info.m_aGameType, "DM") != 0 &&
|
|
str_comp(m_ppServerlist[i]->m_Info.m_aGameType, "TDM") != 0 &&
|
|
str_comp(m_ppServerlist[i]->m_Info.m_aGameType, "CTF") != 0))
|
|
{
|
|
Filtered = 1;
|
|
}
|
|
else if(g_Config.m_BrFilterPureMap &&
|
|
!(str_comp(m_ppServerlist[i]->m_Info.m_aMap, "dm1") == 0 ||
|
|
str_comp(m_ppServerlist[i]->m_Info.m_aMap, "dm2") == 0 ||
|
|
str_comp(m_ppServerlist[i]->m_Info.m_aMap, "dm6") == 0 ||
|
|
str_comp(m_ppServerlist[i]->m_Info.m_aMap, "dm7") == 0 ||
|
|
str_comp(m_ppServerlist[i]->m_Info.m_aMap, "dm8") == 0 ||
|
|
str_comp(m_ppServerlist[i]->m_Info.m_aMap, "dm9") == 0 ||
|
|
str_comp(m_ppServerlist[i]->m_Info.m_aMap, "ctf1") == 0 ||
|
|
str_comp(m_ppServerlist[i]->m_Info.m_aMap, "ctf2") == 0 ||
|
|
str_comp(m_ppServerlist[i]->m_Info.m_aMap, "ctf3") == 0 ||
|
|
str_comp(m_ppServerlist[i]->m_Info.m_aMap, "ctf4") == 0 ||
|
|
str_comp(m_ppServerlist[i]->m_Info.m_aMap, "ctf5") == 0 ||
|
|
str_comp(m_ppServerlist[i]->m_Info.m_aMap, "ctf6") == 0 ||
|
|
str_comp(m_ppServerlist[i]->m_Info.m_aMap, "ctf7") == 0)
|
|
)
|
|
{
|
|
Filtered = 1;
|
|
}
|
|
else if(g_Config.m_BrFilterPing < m_ppServerlist[i]->m_Info.m_Latency)
|
|
Filtered = 1;
|
|
else if(g_Config.m_BrFilterCompatversion && str_comp_num(m_ppServerlist[i]->m_Info.m_aVersion, m_aNetVersion, 3) != 0)
|
|
Filtered = 1;
|
|
else if(g_Config.m_BrFilterServerAddress[0] && !str_find_nocase(m_ppServerlist[i]->m_Info.m_aAddress, g_Config.m_BrFilterServerAddress))
|
|
Filtered = 1;
|
|
else if(g_Config.m_BrFilterGametypeStrict && g_Config.m_BrFilterGametype[0] && str_comp_nocase(m_ppServerlist[i]->m_Info.m_aGameType, g_Config.m_BrFilterGametype))
|
|
Filtered = 1;
|
|
else if(!g_Config.m_BrFilterGametypeStrict && g_Config.m_BrFilterGametype[0] && !str_find_nocase(m_ppServerlist[i]->m_Info.m_aGameType, g_Config.m_BrFilterGametype))
|
|
Filtered = 1;
|
|
else if(g_Config.m_BrFilterUnfinishedMap && m_ppServerlist[i]->m_Info.m_HasRank == 1)
|
|
Filtered = 1;
|
|
else
|
|
{
|
|
if(g_Config.m_BrFilterCountry)
|
|
{
|
|
Filtered = 1;
|
|
// match against player country
|
|
for(p = 0; p < m_ppServerlist[i]->m_Info.m_NumClients; p++)
|
|
{
|
|
if(m_ppServerlist[i]->m_Info.m_aClients[p].m_Country == g_Config.m_BrFilterCountryIndex)
|
|
{
|
|
Filtered = 0;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(!Filtered && g_Config.m_BrFilterString[0] != 0)
|
|
{
|
|
int MatchFound = 0;
|
|
|
|
m_ppServerlist[i]->m_Info.m_QuickSearchHit = 0;
|
|
|
|
// match against server name
|
|
if(str_find_nocase(m_ppServerlist[i]->m_Info.m_aName, g_Config.m_BrFilterString))
|
|
{
|
|
MatchFound = 1;
|
|
m_ppServerlist[i]->m_Info.m_QuickSearchHit |= IServerBrowser::QUICK_SERVERNAME;
|
|
}
|
|
|
|
// match against players
|
|
for(p = 0; p < m_ppServerlist[i]->m_Info.m_NumClients; p++)
|
|
{
|
|
if(str_find_nocase(m_ppServerlist[i]->m_Info.m_aClients[p].m_aName, g_Config.m_BrFilterString) ||
|
|
str_find_nocase(m_ppServerlist[i]->m_Info.m_aClients[p].m_aClan, g_Config.m_BrFilterString))
|
|
{
|
|
MatchFound = 1;
|
|
m_ppServerlist[i]->m_Info.m_QuickSearchHit |= IServerBrowser::QUICK_PLAYER;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// match against map
|
|
if(str_find_nocase(m_ppServerlist[i]->m_Info.m_aMap, g_Config.m_BrFilterString))
|
|
{
|
|
MatchFound = 1;
|
|
m_ppServerlist[i]->m_Info.m_QuickSearchHit |= IServerBrowser::QUICK_MAPNAME;
|
|
}
|
|
|
|
if(!MatchFound)
|
|
Filtered = 1;
|
|
}
|
|
|
|
if(!Filtered && g_Config.m_BrExcludeString[0] != 0)
|
|
{
|
|
int MatchFound = 0;
|
|
|
|
// match against server name
|
|
if(str_find_nocase(m_ppServerlist[i]->m_Info.m_aName, g_Config.m_BrExcludeString))
|
|
{
|
|
MatchFound = 1;
|
|
}
|
|
|
|
// match against map
|
|
if(str_find_nocase(m_ppServerlist[i]->m_Info.m_aMap, g_Config.m_BrExcludeString))
|
|
{
|
|
MatchFound = 1;
|
|
}
|
|
|
|
// match against gametype
|
|
if(str_find_nocase(m_ppServerlist[i]->m_Info.m_aGameType, g_Config.m_BrExcludeString))
|
|
{
|
|
MatchFound = 1;
|
|
}
|
|
|
|
if(MatchFound)
|
|
Filtered = 1;
|
|
}
|
|
}
|
|
|
|
if(Filtered == 0)
|
|
{
|
|
// check for friend
|
|
m_ppServerlist[i]->m_Info.m_FriendState = IFriends::FRIEND_NO;
|
|
for(p = 0; p < m_ppServerlist[i]->m_Info.m_NumClients; p++)
|
|
{
|
|
m_ppServerlist[i]->m_Info.m_aClients[p].m_FriendState = m_pFriends->GetFriendState(m_ppServerlist[i]->m_Info.m_aClients[p].m_aName,
|
|
m_ppServerlist[i]->m_Info.m_aClients[p].m_aClan);
|
|
m_ppServerlist[i]->m_Info.m_FriendState = maximum(m_ppServerlist[i]->m_Info.m_FriendState, m_ppServerlist[i]->m_Info.m_aClients[p].m_FriendState);
|
|
}
|
|
|
|
if(!g_Config.m_BrFilterFriends || m_ppServerlist[i]->m_Info.m_FriendState != IFriends::FRIEND_NO)
|
|
m_pSortedServerlist[m_NumSortedServers++] = i;
|
|
}
|
|
}
|
|
}
|
|
|
|
int CServerBrowser::SortHash() const
|
|
{
|
|
int i = g_Config.m_BrSort&0xff;
|
|
i |= g_Config.m_BrFilterEmpty<<4;
|
|
i |= g_Config.m_BrFilterFull<<5;
|
|
i |= g_Config.m_BrFilterSpectators<<6;
|
|
i |= g_Config.m_BrFilterFriends<<7;
|
|
i |= g_Config.m_BrFilterPw<<8;
|
|
i |= g_Config.m_BrSortOrder<<9;
|
|
i |= g_Config.m_BrFilterCompatversion<<11;
|
|
i |= g_Config.m_BrFilterPure<<12;
|
|
i |= g_Config.m_BrFilterPureMap<<13;
|
|
i |= g_Config.m_BrFilterGametypeStrict<<14;
|
|
i |= g_Config.m_BrFilterUnfinishedMap<<15;
|
|
i |= g_Config.m_BrFilterCountry<<16;
|
|
i |= g_Config.m_BrFilterConnectingPlayers<<17;
|
|
return i;
|
|
}
|
|
|
|
void SetFilteredPlayers(const CServerInfo &Item)
|
|
{
|
|
if (g_Config.m_BrFilterSpectators)
|
|
Item.m_NumFilteredPlayers = Item.m_NumPlayers;
|
|
else
|
|
Item.m_NumFilteredPlayers = Item.m_NumClients;
|
|
if (g_Config.m_BrFilterConnectingPlayers)
|
|
{
|
|
for (int i = 0; i < MAX_CLIENTS; i++)
|
|
{
|
|
const CServerInfo::CClient &Client = Item.m_aClients[i];
|
|
|
|
if (str_comp(Client.m_aName, "(connecting)") == 0 && Client.m_aClan[0] == '\0' && Client.m_Country == -1 && Client.m_Score == 0)
|
|
Item.m_NumFilteredPlayers--;
|
|
}
|
|
}
|
|
}
|
|
|
|
void CServerBrowser::Sort()
|
|
{
|
|
int i;
|
|
|
|
// fill m_NumFilteredPlayers
|
|
for(i = 0; i < m_NumServers; i++)
|
|
{
|
|
SetFilteredPlayers(m_ppServerlist[i]->m_Info);
|
|
}
|
|
|
|
// create filtered list
|
|
Filter();
|
|
|
|
// sort
|
|
if(g_Config.m_BrSortOrder == 2 && (g_Config.m_BrSort == IServerBrowser::SORT_NUMPLAYERS ||g_Config.m_BrSort == IServerBrowser::SORT_PING))
|
|
std::stable_sort(m_pSortedServerlist, m_pSortedServerlist+m_NumSortedServers, SortWrap(this, &CServerBrowser::SortCompareNumPlayersAndPing));
|
|
else if(g_Config.m_BrSort == IServerBrowser::SORT_NAME)
|
|
std::stable_sort(m_pSortedServerlist, m_pSortedServerlist+m_NumSortedServers, SortWrap(this, &CServerBrowser::SortCompareName));
|
|
else if(g_Config.m_BrSort == IServerBrowser::SORT_PING)
|
|
std::stable_sort(m_pSortedServerlist, m_pSortedServerlist+m_NumSortedServers, SortWrap(this, &CServerBrowser::SortComparePing));
|
|
else if(g_Config.m_BrSort == IServerBrowser::SORT_MAP)
|
|
std::stable_sort(m_pSortedServerlist, m_pSortedServerlist+m_NumSortedServers, SortWrap(this, &CServerBrowser::SortCompareMap));
|
|
else if(g_Config.m_BrSort == IServerBrowser::SORT_NUMPLAYERS)
|
|
std::stable_sort(m_pSortedServerlist, m_pSortedServerlist+m_NumSortedServers, SortWrap(this, &CServerBrowser::SortCompareNumPlayers));
|
|
else if(g_Config.m_BrSort == IServerBrowser::SORT_GAMETYPE)
|
|
std::stable_sort(m_pSortedServerlist, m_pSortedServerlist+m_NumSortedServers, SortWrap(this, &CServerBrowser::SortCompareGametype));
|
|
|
|
// set indexes
|
|
for(i = 0; i < m_NumSortedServers; i++)
|
|
m_ppServerlist[m_pSortedServerlist[i]]->m_Info.m_SortedIndex = i;
|
|
|
|
str_copy(m_aFilterGametypeString, g_Config.m_BrFilterGametype, sizeof(m_aFilterGametypeString));
|
|
str_copy(m_aFilterString, g_Config.m_BrFilterString, sizeof(m_aFilterString));
|
|
m_Sorthash = SortHash();
|
|
}
|
|
|
|
void CServerBrowser::RemoveRequest(CServerEntry *pEntry)
|
|
{
|
|
if(pEntry->m_pPrevReq || pEntry->m_pNextReq || m_pFirstReqServer == pEntry)
|
|
{
|
|
if(pEntry->m_pPrevReq)
|
|
pEntry->m_pPrevReq->m_pNextReq = pEntry->m_pNextReq;
|
|
else
|
|
m_pFirstReqServer = pEntry->m_pNextReq;
|
|
|
|
if(pEntry->m_pNextReq)
|
|
pEntry->m_pNextReq->m_pPrevReq = pEntry->m_pPrevReq;
|
|
else
|
|
m_pLastReqServer = pEntry->m_pPrevReq;
|
|
|
|
pEntry->m_pPrevReq = 0;
|
|
pEntry->m_pNextReq = 0;
|
|
m_NumRequests--;
|
|
}
|
|
}
|
|
|
|
CServerBrowser::CServerEntry *CServerBrowser::Find(const NETADDR &Addr)
|
|
{
|
|
CServerEntry *pEntry = m_aServerlistIp[Addr.ip[0]];
|
|
|
|
for(; pEntry; pEntry = pEntry->m_pNextIp)
|
|
{
|
|
if(net_addr_comp(&pEntry->m_Addr, &Addr) == 0)
|
|
return pEntry;
|
|
}
|
|
return (CServerEntry*)0;
|
|
}
|
|
|
|
void CServerBrowser::QueueRequest(CServerEntry *pEntry)
|
|
{
|
|
// add it to the list of servers that we should request info from
|
|
pEntry->m_pPrevReq = m_pLastReqServer;
|
|
if(m_pLastReqServer)
|
|
m_pLastReqServer->m_pNextReq = pEntry;
|
|
else
|
|
m_pFirstReqServer = pEntry;
|
|
m_pLastReqServer = pEntry;
|
|
pEntry->m_pNextReq = 0;
|
|
m_NumRequests++;
|
|
}
|
|
|
|
void CServerBrowser::SetInfo(CServerEntry *pEntry, const CServerInfo &Info)
|
|
{
|
|
bool Fav = pEntry->m_Info.m_Favorite;
|
|
bool Off = pEntry->m_Info.m_Official;
|
|
pEntry->m_Info = Info;
|
|
pEntry->m_Info.m_Favorite = Fav;
|
|
pEntry->m_Info.m_Official = Off;
|
|
pEntry->m_Info.m_NetAddr = pEntry->m_Addr;
|
|
|
|
// all these are just for nice compatibility
|
|
if(pEntry->m_Info.m_aGameType[0] == '0' && pEntry->m_Info.m_aGameType[1] == 0)
|
|
str_copy(pEntry->m_Info.m_aGameType, "DM", sizeof(pEntry->m_Info.m_aGameType));
|
|
else if(pEntry->m_Info.m_aGameType[0] == '1' && pEntry->m_Info.m_aGameType[1] == 0)
|
|
str_copy(pEntry->m_Info.m_aGameType, "TDM", sizeof(pEntry->m_Info.m_aGameType));
|
|
else if(pEntry->m_Info.m_aGameType[0] == '2' && pEntry->m_Info.m_aGameType[1] == 0)
|
|
str_copy(pEntry->m_Info.m_aGameType, "CTF", sizeof(pEntry->m_Info.m_aGameType));
|
|
|
|
/*if(!request)
|
|
{
|
|
pEntry->m_Info.latency = (time_get()-pEntry->request_time)*1000/time_freq();
|
|
RemoveRequest(pEntry);
|
|
}*/
|
|
|
|
pEntry->m_GotInfo = 1;
|
|
}
|
|
|
|
CServerBrowser::CServerEntry *CServerBrowser::Add(const NETADDR &Addr)
|
|
{
|
|
int Hash = Addr.ip[0];
|
|
CServerEntry *pEntry = 0;
|
|
int i;
|
|
|
|
// create new pEntry
|
|
pEntry = (CServerEntry *)m_ServerlistHeap.Allocate(sizeof(CServerEntry));
|
|
mem_zero(pEntry, sizeof(CServerEntry));
|
|
|
|
// set the info
|
|
pEntry->m_Addr = Addr;
|
|
pEntry->m_Info.m_NetAddr = Addr;
|
|
|
|
pEntry->m_Info.m_Latency = 999;
|
|
pEntry->m_Info.m_HasRank = -1;
|
|
net_addr_str(&Addr, pEntry->m_Info.m_aAddress, sizeof(pEntry->m_Info.m_aAddress), true);
|
|
str_copy(pEntry->m_Info.m_aName, pEntry->m_Info.m_aAddress, sizeof(pEntry->m_Info.m_aName));
|
|
|
|
// check if it's a favorite
|
|
for(i = 0; i < m_NumFavoriteServers; i++)
|
|
{
|
|
if(net_addr_comp(&Addr, &m_aFavoriteServers[i]) == 0)
|
|
{
|
|
pEntry->m_Info.m_Favorite = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// check if it's an official server
|
|
for(int Network = 0; Network < NUM_NETWORKS; Network++)
|
|
{
|
|
for(int i = 0; i < m_aNetworks[Network].m_NumCountries; i++)
|
|
{
|
|
CNetworkCountry *pCntr = &m_aNetworks[Network].m_aCountries[i];
|
|
for(int j = 0; j < pCntr->m_NumServers; j++)
|
|
{
|
|
if(net_addr_comp(&Addr, &pCntr->m_aServers[j]) == 0)
|
|
{
|
|
pEntry->m_Info.m_Official = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// add to the hash list
|
|
pEntry->m_pNextIp = m_aServerlistIp[Hash];
|
|
m_aServerlistIp[Hash] = pEntry;
|
|
|
|
if(m_NumServers == m_NumServerCapacity)
|
|
{
|
|
CServerEntry **ppNewlist;
|
|
m_NumServerCapacity += 100;
|
|
ppNewlist = (CServerEntry **)calloc(m_NumServerCapacity, sizeof(CServerEntry *));
|
|
mem_copy(ppNewlist, m_ppServerlist, m_NumServers*sizeof(CServerEntry*));
|
|
free(m_ppServerlist);
|
|
m_ppServerlist = ppNewlist;
|
|
}
|
|
|
|
// add to list
|
|
m_ppServerlist[m_NumServers] = pEntry;
|
|
pEntry->m_Info.m_ServerIndex = m_NumServers;
|
|
m_NumServers++;
|
|
|
|
return pEntry;
|
|
}
|
|
|
|
void CServerBrowser::Set(const NETADDR &Addr, int Type, int Token, const CServerInfo *pInfo)
|
|
{
|
|
CServerEntry *pEntry = 0;
|
|
if(Type == IServerBrowser::SET_MASTER_ADD)
|
|
{
|
|
if(m_ServerlistType != IServerBrowser::TYPE_INTERNET)
|
|
return;
|
|
m_LastPacketTick = 0;
|
|
if(!Find(Addr))
|
|
{
|
|
pEntry = Add(Addr);
|
|
QueueRequest(pEntry);
|
|
}
|
|
}
|
|
else if(Type == IServerBrowser::SET_FAV_ADD)
|
|
{
|
|
if(m_ServerlistType != IServerBrowser::TYPE_FAVORITES)
|
|
return;
|
|
|
|
if(!Find(Addr))
|
|
{
|
|
pEntry = Add(Addr);
|
|
QueueRequest(pEntry);
|
|
}
|
|
}
|
|
else if(Type == IServerBrowser::SET_DDNET_ADD)
|
|
{
|
|
if(m_ServerlistType != IServerBrowser::TYPE_DDNET)
|
|
return;
|
|
|
|
if(!Find(Addr))
|
|
{
|
|
pEntry = Add(Addr);
|
|
QueueRequest(pEntry);
|
|
}
|
|
}
|
|
else if(Type == IServerBrowser::SET_KOG_ADD)
|
|
{
|
|
if(m_ServerlistType != IServerBrowser::TYPE_KOG)
|
|
return;
|
|
|
|
if(!Find(Addr))
|
|
{
|
|
pEntry = Add(Addr);
|
|
QueueRequest(pEntry);
|
|
}
|
|
}
|
|
else if(Type == IServerBrowser::SET_TOKEN)
|
|
{
|
|
int BasicToken = Token;
|
|
int ExtraToken = 0;
|
|
if(pInfo->m_Type == SERVERINFO_EXTENDED)
|
|
{
|
|
BasicToken = Token & 0xff;
|
|
ExtraToken = Token >> 8;
|
|
}
|
|
|
|
pEntry = Find(Addr);
|
|
if(m_ServerlistType != IServerBrowser::TYPE_LAN)
|
|
{
|
|
if(!pEntry)
|
|
{
|
|
return;
|
|
}
|
|
int Token = GenerateToken(Addr);
|
|
bool Drop = false;
|
|
Drop = Drop || BasicToken != GetBasicToken(Token);
|
|
Drop = Drop || (pInfo->m_Type == SERVERINFO_EXTENDED && ExtraToken != GetExtraToken(Token));
|
|
if(Drop)
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
if(!pEntry)
|
|
pEntry = Add(Addr);
|
|
if(pEntry)
|
|
{
|
|
if(m_ServerlistType == IServerBrowser::TYPE_LAN)
|
|
{
|
|
NETADDR Broadcast;
|
|
mem_zero(&Broadcast, sizeof(Broadcast));
|
|
Broadcast.type = m_pNetClient->NetType()|NETTYPE_LINK_BROADCAST;
|
|
int Token = GenerateToken(Broadcast);
|
|
bool Drop = false;
|
|
Drop = Drop || BasicToken != GetBasicToken(Token);
|
|
Drop = Drop || (pInfo->m_Type == SERVERINFO_EXTENDED && ExtraToken != GetExtraToken(Token));
|
|
if(Drop)
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
SetInfo(pEntry, *pInfo);
|
|
if (m_ServerlistType == IServerBrowser::TYPE_LAN)
|
|
pEntry->m_Info.m_Latency = minimum(static_cast<int>((time_get()-m_BroadcastTime)*1000/time_freq()), 999);
|
|
else if (pEntry->m_RequestTime > 0)
|
|
{
|
|
pEntry->m_Info.m_Latency = minimum(static_cast<int>((time_get()-pEntry->m_RequestTime)*1000/time_freq()), 999);
|
|
pEntry->m_RequestTime = -1; // Request has been answered
|
|
}
|
|
RemoveRequest(pEntry);
|
|
}
|
|
}
|
|
|
|
Sort();
|
|
}
|
|
|
|
void CServerBrowser::Refresh(int Type)
|
|
{
|
|
// clear out everything
|
|
m_ServerlistHeap.Reset();
|
|
m_NumServers = 0;
|
|
m_NumSortedServers = 0;
|
|
mem_zero(m_aServerlistIp, sizeof(m_aServerlistIp));
|
|
m_pFirstReqServer = 0;
|
|
m_pLastReqServer = 0;
|
|
m_NumRequests = 0;
|
|
m_CurrentMaxRequests = g_Config.m_BrMaxRequests;
|
|
m_RequestNumber++;
|
|
|
|
m_ServerlistType = Type;
|
|
|
|
if(Type == IServerBrowser::TYPE_LAN)
|
|
{
|
|
unsigned char Buffer[sizeof(SERVERBROWSE_GETINFO)+1];
|
|
CNetChunk Packet;
|
|
int i;
|
|
|
|
/* do the broadcast version */
|
|
Packet.m_ClientID = -1;
|
|
mem_zero(&Packet, sizeof(Packet));
|
|
Packet.m_Address.type = m_pNetClient->NetType()|NETTYPE_LINK_BROADCAST;
|
|
Packet.m_Flags = NETSENDFLAG_CONNLESS|NETSENDFLAG_EXTENDED;
|
|
Packet.m_DataSize = sizeof(Buffer);
|
|
Packet.m_pData = Buffer;
|
|
mem_zero(&Packet.m_aExtraData, sizeof(Packet.m_aExtraData));
|
|
|
|
int Token = GenerateToken(Packet.m_Address);
|
|
mem_copy(Buffer, SERVERBROWSE_GETINFO, sizeof(SERVERBROWSE_GETINFO));
|
|
Buffer[sizeof(SERVERBROWSE_GETINFO)] = GetBasicToken(Token);
|
|
|
|
Packet.m_aExtraData[0] = GetExtraToken(Token) >> 8;
|
|
Packet.m_aExtraData[1] = GetExtraToken(Token) & 0xff;
|
|
|
|
m_BroadcastTime = time_get();
|
|
|
|
for(i = 8303; i <= 8310; i++)
|
|
{
|
|
Packet.m_Address.port = i;
|
|
m_pNetClient->Send(&Packet);
|
|
}
|
|
|
|
if(g_Config.m_Debug)
|
|
m_pConsole->Print(IConsole::OUTPUT_LEVEL_DEBUG, "client_srvbrowse", "broadcasting for servers");
|
|
}
|
|
else if(Type == IServerBrowser::TYPE_INTERNET)
|
|
m_NeedRefresh = 1;
|
|
else if(Type == IServerBrowser::TYPE_FAVORITES)
|
|
{
|
|
for(int i = 0; i < m_NumFavoriteServers; i++)
|
|
Set(m_aFavoriteServers[i], IServerBrowser::SET_FAV_ADD, -1, 0);
|
|
}
|
|
else if(Type == IServerBrowser::TYPE_DDNET)
|
|
{
|
|
// remove unknown elements of exclude list
|
|
CountryFilterClean(NETWORK_DDNET);
|
|
TypeFilterClean(NETWORK_DDNET);
|
|
|
|
int MaxServers = 0;
|
|
for(int i = 0; i < m_aNetworks[NETWORK_DDNET].m_NumCountries; i++)
|
|
{
|
|
CNetworkCountry *pCntr = &m_aNetworks[NETWORK_DDNET].m_aCountries[i];
|
|
MaxServers = maximum(MaxServers, pCntr->m_NumServers);
|
|
}
|
|
|
|
for(int g = 0; g < MaxServers; g++)
|
|
{
|
|
for(int i = 0; i < m_aNetworks[NETWORK_DDNET].m_NumCountries; i++)
|
|
{
|
|
CNetworkCountry *pCntr = &m_aNetworks[NETWORK_DDNET].m_aCountries[i];
|
|
|
|
// check for filter
|
|
if(DDNetFiltered(g_Config.m_BrFilterExcludeCountries, pCntr->m_aName))
|
|
continue;
|
|
|
|
if(g >= pCntr->m_NumServers)
|
|
continue;
|
|
|
|
if(!DDNetFiltered(g_Config.m_BrFilterExcludeTypes, pCntr->m_aTypes[g]))
|
|
Set(pCntr->m_aServers[g], IServerBrowser::SET_DDNET_ADD, -1, 0);
|
|
}
|
|
}
|
|
}
|
|
else if(Type == IServerBrowser::TYPE_KOG)
|
|
{
|
|
// remove unknown elements of exclude list
|
|
CountryFilterClean(NETWORK_KOG);
|
|
TypeFilterClean(NETWORK_KOG);
|
|
|
|
int MaxServers = 0;
|
|
for(int i = 0; i < m_aNetworks[NETWORK_KOG].m_NumCountries; i++)
|
|
{
|
|
CNetworkCountry *pCntr = &m_aNetworks[NETWORK_KOG].m_aCountries[i];
|
|
MaxServers = maximum(MaxServers, pCntr->m_NumServers);
|
|
}
|
|
|
|
for(int g = 0; g < MaxServers; g++)
|
|
{
|
|
for(int i = 0; i < m_aNetworks[NETWORK_KOG].m_NumCountries; i++)
|
|
{
|
|
CNetworkCountry *pCntr = &m_aNetworks[NETWORK_KOG].m_aCountries[i];
|
|
|
|
// check for filter
|
|
if(DDNetFiltered(g_Config.m_BrFilterExcludeCountriesKoG, pCntr->m_aName))
|
|
continue;
|
|
|
|
if(g >= pCntr->m_NumServers)
|
|
continue;
|
|
|
|
if(!DDNetFiltered(g_Config.m_BrFilterExcludeTypesKoG, pCntr->m_aTypes[g]))
|
|
Set(pCntr->m_aServers[g], IServerBrowser::SET_KOG_ADD, -1, 0);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void CServerBrowser::RequestImpl(const NETADDR &Addr, CServerEntry *pEntry) const
|
|
{
|
|
unsigned char Buffer[sizeof(SERVERBROWSE_GETINFO)+1];
|
|
CNetChunk Packet;
|
|
|
|
if(g_Config.m_Debug)
|
|
{
|
|
char aAddrStr[NETADDR_MAXSTRSIZE];
|
|
net_addr_str(&Addr, aAddrStr, sizeof(aAddrStr), true);
|
|
char aBuf[256];
|
|
str_format(aBuf, sizeof(aBuf),"requesting server info from %s", aAddrStr);
|
|
m_pConsole->Print(IConsole::OUTPUT_LEVEL_DEBUG, "client_srvbrowse", aBuf);
|
|
}
|
|
|
|
int Token = GenerateToken(Addr);
|
|
|
|
mem_copy(Buffer, SERVERBROWSE_GETINFO, sizeof(SERVERBROWSE_GETINFO));
|
|
Buffer[sizeof(SERVERBROWSE_GETINFO)] = GetBasicToken(Token);
|
|
|
|
Packet.m_ClientID = -1;
|
|
Packet.m_Address = Addr;
|
|
Packet.m_Flags = NETSENDFLAG_CONNLESS|NETSENDFLAG_EXTENDED;
|
|
Packet.m_DataSize = sizeof(Buffer);
|
|
Packet.m_pData = Buffer;
|
|
mem_zero(&Packet.m_aExtraData, sizeof(Packet.m_aExtraData));
|
|
Packet.m_aExtraData[0] = GetExtraToken(Token) >> 8;
|
|
Packet.m_aExtraData[1] = GetExtraToken(Token) & 0xff;
|
|
|
|
m_pNetClient->Send(&Packet);
|
|
|
|
if(pEntry)
|
|
pEntry->m_RequestTime = time_get();
|
|
}
|
|
|
|
void CServerBrowser::RequestImpl64(const NETADDR &Addr, CServerEntry *pEntry) const
|
|
{
|
|
unsigned char Buffer[sizeof(SERVERBROWSE_GETINFO_64_LEGACY)+1];
|
|
CNetChunk Packet;
|
|
|
|
if(g_Config.m_Debug)
|
|
{
|
|
char aAddrStr[NETADDR_MAXSTRSIZE];
|
|
net_addr_str(&Addr, aAddrStr, sizeof(aAddrStr), true);
|
|
char aBuf[256];
|
|
str_format(aBuf, sizeof(aBuf),"requesting server info 64 from %s", aAddrStr);
|
|
m_pConsole->Print(IConsole::OUTPUT_LEVEL_DEBUG, "client_srvbrowse", aBuf);
|
|
}
|
|
|
|
mem_copy(Buffer, SERVERBROWSE_GETINFO_64_LEGACY, sizeof(SERVERBROWSE_GETINFO_64_LEGACY));
|
|
Buffer[sizeof(SERVERBROWSE_GETINFO_64_LEGACY)] = GetBasicToken(GenerateToken(Addr));
|
|
|
|
Packet.m_ClientID = -1;
|
|
Packet.m_Address = Addr;
|
|
Packet.m_Flags = NETSENDFLAG_CONNLESS;
|
|
Packet.m_DataSize = sizeof(Buffer);
|
|
Packet.m_pData = Buffer;
|
|
|
|
m_pNetClient->Send(&Packet);
|
|
|
|
if(pEntry)
|
|
pEntry->m_RequestTime = time_get();
|
|
}
|
|
|
|
void CServerBrowser::RequestCurrentServer(const NETADDR &Addr) const
|
|
{
|
|
RequestImpl(Addr, 0);
|
|
}
|
|
|
|
|
|
void CServerBrowser::Update(bool ForceResort)
|
|
{
|
|
int64 Timeout = time_freq();
|
|
int64 Now = time_get();
|
|
int Count;
|
|
CServerEntry *pEntry, *pNext;
|
|
|
|
// do server list requests
|
|
if(m_NeedRefresh && !m_pMasterServer->IsRefreshing())
|
|
{
|
|
NETADDR Addr;
|
|
CNetChunk Packet;
|
|
int i = 0;
|
|
|
|
m_NeedRefresh = 0;
|
|
m_MasterServerCount = -1;
|
|
mem_zero(&Packet, sizeof(Packet));
|
|
Packet.m_ClientID = -1;
|
|
Packet.m_Flags = NETSENDFLAG_CONNLESS;
|
|
Packet.m_DataSize = sizeof(SERVERBROWSE_GETCOUNT);
|
|
Packet.m_pData = SERVERBROWSE_GETCOUNT;
|
|
|
|
for(i = 0; i < IMasterServer::MAX_MASTERSERVERS; i++)
|
|
{
|
|
if(!m_pMasterServer->IsValid(i))
|
|
continue;
|
|
|
|
Addr = m_pMasterServer->GetAddr(i);
|
|
m_pMasterServer->SetCount(i, -1);
|
|
Packet.m_Address = Addr;
|
|
m_pNetClient->Send(&Packet);
|
|
if(g_Config.m_Debug)
|
|
{
|
|
dbg_msg("client_srvbrowse", "count-request sent to %d", i);
|
|
}
|
|
}
|
|
}
|
|
|
|
//Check if all server counts arrived
|
|
if(m_MasterServerCount == -1)
|
|
{
|
|
m_MasterServerCount = 0;
|
|
for(int i = 0; i < IMasterServer::MAX_MASTERSERVERS; i++)
|
|
{
|
|
if(!m_pMasterServer->IsValid(i))
|
|
continue;
|
|
int Count = m_pMasterServer->GetCount(i);
|
|
if(Count == -1)
|
|
{
|
|
/* ignore Server
|
|
m_MasterServerCount = -1;
|
|
return;
|
|
// we don't have the required server information
|
|
*/
|
|
}
|
|
else
|
|
m_MasterServerCount += Count;
|
|
}
|
|
//request Server-List
|
|
NETADDR Addr;
|
|
CNetChunk Packet;
|
|
mem_zero(&Packet, sizeof(Packet));
|
|
Packet.m_ClientID = -1;
|
|
Packet.m_Flags = NETSENDFLAG_CONNLESS;
|
|
Packet.m_DataSize = sizeof(SERVERBROWSE_GETLIST);
|
|
Packet.m_pData = SERVERBROWSE_GETLIST;
|
|
|
|
for(int i = 0; i < IMasterServer::MAX_MASTERSERVERS; i++)
|
|
{
|
|
if(!m_pMasterServer->IsValid(i))
|
|
continue;
|
|
|
|
Addr = m_pMasterServer->GetAddr(i);
|
|
Packet.m_Address = Addr;
|
|
m_pNetClient->Send(&Packet);
|
|
}
|
|
if(g_Config.m_Debug)
|
|
{
|
|
dbg_msg("client_srvbrowse", "servercount: %d, requesting server list", m_MasterServerCount);
|
|
}
|
|
m_LastPacketTick = 0;
|
|
}
|
|
else if(m_MasterServerCount > -1)
|
|
{
|
|
m_MasterServerCount = 0;
|
|
for(int i = 0; i < IMasterServer::MAX_MASTERSERVERS; i++)
|
|
{
|
|
if(!m_pMasterServer->IsValid(i))
|
|
continue;
|
|
int Count = m_pMasterServer->GetCount(i);
|
|
if(Count == -1)
|
|
{
|
|
/* ignore Server
|
|
m_MasterServerCount = -1;
|
|
return;
|
|
// we don't have the required server information
|
|
*/
|
|
}
|
|
else
|
|
m_MasterServerCount += Count;
|
|
}
|
|
//if(g_Config.m_Debug)
|
|
//{
|
|
// dbg_msg("client_srvbrowse", "ServerCount2: %d", m_MasterServerCount);
|
|
//}
|
|
}
|
|
if(m_MasterServerCount > m_NumRequests + m_LastPacketTick)
|
|
{
|
|
++m_LastPacketTick;
|
|
return; //wait for more packets
|
|
}
|
|
pEntry = m_pFirstReqServer;
|
|
Count = 0;
|
|
while(1)
|
|
{
|
|
if(!pEntry) // no more entries
|
|
break;
|
|
if(pEntry->m_RequestTime && pEntry->m_RequestTime+Timeout < Now)
|
|
{
|
|
pEntry = pEntry->m_pNextReq;
|
|
continue;
|
|
}
|
|
// no more then 10 concurrent requests
|
|
if(Count == m_CurrentMaxRequests)
|
|
break;
|
|
|
|
if(pEntry->m_RequestTime == 0)
|
|
{
|
|
if (pEntry->m_Request64Legacy)
|
|
RequestImpl64(pEntry->m_Addr, pEntry);
|
|
else
|
|
RequestImpl(pEntry->m_Addr, pEntry);
|
|
}
|
|
|
|
Count++;
|
|
pEntry = pEntry->m_pNextReq;
|
|
}
|
|
|
|
if(m_pFirstReqServer && Count == 0 && m_CurrentMaxRequests > 1) //NO More current Server Requests
|
|
{
|
|
//reset old ones
|
|
pEntry = m_pFirstReqServer;
|
|
while(1)
|
|
{
|
|
if(!pEntry) // no more entries
|
|
break;
|
|
pEntry->m_RequestTime = 0;
|
|
pEntry = pEntry->m_pNextReq;
|
|
}
|
|
|
|
//update max-requests
|
|
m_CurrentMaxRequests = m_CurrentMaxRequests/2;
|
|
if(m_CurrentMaxRequests < 1)
|
|
m_CurrentMaxRequests = 1;
|
|
}
|
|
else if(Count == 0 && m_CurrentMaxRequests == 1) //we reached the limit, just release all left requests. IF a server sends us a packet, a new request will be added automatically, so we can delete all
|
|
{
|
|
pEntry = m_pFirstReqServer;
|
|
while(1)
|
|
{
|
|
if(!pEntry) // no more entries
|
|
break;
|
|
pNext = pEntry->m_pNextReq;
|
|
RemoveRequest(pEntry); //release request
|
|
pEntry = pNext;
|
|
}
|
|
}
|
|
|
|
// check if we need to resort
|
|
if(m_Sorthash != SortHash() || ForceResort)
|
|
Sort();
|
|
}
|
|
|
|
|
|
bool CServerBrowser::IsFavorite(const NETADDR &Addr) const
|
|
{
|
|
// search for the address
|
|
int i;
|
|
for(i = 0; i < m_NumFavoriteServers; i++)
|
|
{
|
|
if(net_addr_comp(&Addr, &m_aFavoriteServers[i]) == 0)
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void CServerBrowser::AddFavorite(const NETADDR &Addr)
|
|
{
|
|
CServerEntry *pEntry;
|
|
|
|
if(m_NumFavoriteServers == MAX_FAVORITES)
|
|
return;
|
|
|
|
// make sure that we don't already have the server in our list
|
|
for(int i = 0; i < m_NumFavoriteServers; i++)
|
|
{
|
|
if(net_addr_comp(&Addr, &m_aFavoriteServers[i]) == 0)
|
|
return;
|
|
}
|
|
|
|
// add the server to the list
|
|
m_aFavoriteServers[m_NumFavoriteServers++] = Addr;
|
|
pEntry = Find(Addr);
|
|
if(pEntry)
|
|
pEntry->m_Info.m_Favorite = true;
|
|
|
|
if(g_Config.m_Debug)
|
|
{
|
|
char aAddrStr[NETADDR_MAXSTRSIZE];
|
|
net_addr_str(&Addr, aAddrStr, sizeof(aAddrStr), true);
|
|
char aBuf[256];
|
|
str_format(aBuf, sizeof(aBuf), "added fav, %s", aAddrStr);
|
|
m_pConsole->Print(IConsole::OUTPUT_LEVEL_DEBUG, "client_srvbrowse", aBuf);
|
|
}
|
|
}
|
|
|
|
void CServerBrowser::RemoveFavorite(const NETADDR &Addr)
|
|
{
|
|
int i;
|
|
CServerEntry *pEntry;
|
|
|
|
for(i = 0; i < m_NumFavoriteServers; i++)
|
|
{
|
|
if(net_addr_comp(&Addr, &m_aFavoriteServers[i]) == 0)
|
|
{
|
|
mem_move(&m_aFavoriteServers[i], &m_aFavoriteServers[i+1], sizeof(NETADDR)*(m_NumFavoriteServers-(i+1)));
|
|
m_NumFavoriteServers--;
|
|
|
|
pEntry = Find(Addr);
|
|
if(pEntry)
|
|
pEntry->m_Info.m_Favorite = false;
|
|
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
void CServerBrowser::LoadDDNetServers()
|
|
{
|
|
if (!m_pDDNetInfo)
|
|
return;
|
|
|
|
// reset servers / countries
|
|
for (int Network = 0; Network < NUM_NETWORKS; Network++)
|
|
{
|
|
CNetwork *pNet = &m_aNetworks[Network];
|
|
|
|
// parse JSON
|
|
const json_value *pServers = json_object_get(m_pDDNetInfo, Network == NETWORK_DDNET ? "servers" : "servers-kog");
|
|
|
|
if (!pServers || pServers->type != json_array)
|
|
return;
|
|
|
|
pNet->m_NumCountries = 0;
|
|
pNet->m_NumTypes = 0;
|
|
|
|
for (int i = 0; i < json_array_length(pServers) && pNet->m_NumCountries < MAX_COUNTRIES; i++)
|
|
{
|
|
// pSrv - { name, flagId, servers }
|
|
const json_value *pSrv = json_array_get(pServers, i);
|
|
const json_value *pTypes = json_object_get(pSrv, "servers");
|
|
const json_value *pName = json_object_get(pSrv, "name");
|
|
const json_value *pFlagID = json_object_get(pSrv, "flagId");
|
|
|
|
if (pSrv->type != json_object || pTypes->type != json_object || pName->type != json_string || pFlagID->type != json_integer)
|
|
{
|
|
dbg_msg("client_srvbrowse", "invalid attributes");
|
|
continue;
|
|
}
|
|
|
|
// build structure
|
|
CNetworkCountry *pCntr = &pNet->m_aCountries[pNet->m_NumCountries];
|
|
|
|
pCntr->Reset();
|
|
|
|
str_copy(pCntr->m_aName, json_string_get(pName), sizeof(pCntr->m_aName));
|
|
pCntr->m_FlagID = json_int_get(pFlagID);
|
|
|
|
// add country
|
|
for (unsigned int t = 0; t < pTypes->u.object.length; t++)
|
|
{
|
|
const char *pType = pTypes->u.object.values[t].name;
|
|
const json_value *pAddrs = pTypes->u.object.values[t].value;
|
|
|
|
if (pAddrs->type != json_array)
|
|
{
|
|
dbg_msg("client_srvbrowse", "invalid attributes");
|
|
continue;
|
|
}
|
|
|
|
// add type
|
|
if(json_array_length(pAddrs) > 0 && pNet->m_NumTypes < MAX_TYPES)
|
|
{
|
|
int Pos;
|
|
for(Pos = 0; Pos < pNet->m_NumTypes; Pos++)
|
|
{
|
|
if(!str_comp(pNet->m_aTypes[Pos], pType))
|
|
break;
|
|
}
|
|
if(Pos == pNet->m_NumTypes)
|
|
{
|
|
str_copy(pNet->m_aTypes[pNet->m_NumTypes], pType, sizeof(pNet->m_aTypes[pNet->m_NumTypes]));
|
|
pNet->m_NumTypes++;
|
|
}
|
|
}
|
|
|
|
// add addresses
|
|
for (int g = 0; g < json_array_length(pAddrs); g++, pCntr->m_NumServers++)
|
|
{
|
|
const json_value *pAddr = json_array_get(pAddrs, g);
|
|
if (pAddr->type != json_string)
|
|
{
|
|
dbg_msg("client_srvbrowse", "invalid attributes");
|
|
continue;
|
|
}
|
|
const char *pStr = json_string_get(pAddr);
|
|
net_addr_from_str(&pCntr->m_aServers[pCntr->m_NumServers], pStr);
|
|
str_copy(pCntr->m_aTypes[pCntr->m_NumServers], pType, sizeof(pCntr->m_aTypes[pCntr->m_NumServers]));
|
|
}
|
|
}
|
|
|
|
pNet->m_NumCountries++;
|
|
}
|
|
}
|
|
}
|
|
|
|
void CServerBrowser::RecheckOfficial()
|
|
{
|
|
for(int Network = 0; Network < NUM_NETWORKS; Network++)
|
|
{
|
|
for(int i = 0; i < m_aNetworks[Network].m_NumCountries; i++)
|
|
{
|
|
CNetworkCountry *pCntr = &m_aNetworks[Network].m_aCountries[i];
|
|
for(int j = 0; j < pCntr->m_NumServers; j++)
|
|
{
|
|
CServerEntry *pEntry = Find(pCntr->m_aServers[j]);
|
|
if(pEntry)
|
|
{
|
|
pEntry->m_Info.m_Official = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void CServerBrowser::LoadDDNetRanks()
|
|
{
|
|
for(int i = 0; i < m_NumServers; i++)
|
|
{
|
|
if(m_ppServerlist[i]->m_Info.m_aMap[0])
|
|
m_ppServerlist[i]->m_Info.m_HasRank = HasRank(m_ppServerlist[i]->m_Info.m_aMap);
|
|
}
|
|
}
|
|
|
|
int CServerBrowser::HasRank(const char *pMap)
|
|
{
|
|
if(m_ServerlistType != IServerBrowser::TYPE_DDNET || !m_pDDNetInfo)
|
|
return -1;
|
|
|
|
const json_value *pDDNetRanks = json_object_get(m_pDDNetInfo, "maps");
|
|
|
|
if(!pDDNetRanks || pDDNetRanks->type != json_array)
|
|
return -1;
|
|
|
|
for (int i = 0; i < json_array_length(pDDNetRanks); i++)
|
|
{
|
|
const json_value *pJson = json_array_get(pDDNetRanks, i);
|
|
if(!pJson || pJson->type != json_string)
|
|
continue;
|
|
|
|
const char *pStr = json_string_get(pJson);
|
|
|
|
if(str_comp(pMap, pStr) == 0)
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void CServerBrowser::LoadDDNetInfoJson()
|
|
{
|
|
IStorage *pStorage = Kernel()->RequestInterface<IStorage>();
|
|
IOHANDLE File = pStorage->OpenFile(DDNET_INFO, IOFLAG_READ, IStorage::TYPE_SAVE);
|
|
|
|
if(!File)
|
|
return;
|
|
|
|
const int Length = io_length(File);
|
|
if(Length <= 0)
|
|
{
|
|
io_close(File);
|
|
return;
|
|
}
|
|
|
|
char *pBuf = (char *)malloc(Length);
|
|
pBuf[0] = '\0';
|
|
|
|
io_read(File, pBuf, Length);
|
|
io_close(File);
|
|
|
|
if(m_pDDNetInfo)
|
|
json_value_free(m_pDDNetInfo);
|
|
|
|
m_pDDNetInfo = json_parse(pBuf, Length);
|
|
|
|
free(pBuf);
|
|
|
|
if(m_pDDNetInfo && m_pDDNetInfo->type != json_object)
|
|
{
|
|
json_value_free(m_pDDNetInfo);
|
|
m_pDDNetInfo = 0;
|
|
}
|
|
}
|
|
|
|
const json_value *CServerBrowser::LoadDDNetInfo()
|
|
{
|
|
LoadDDNetInfoJson();
|
|
LoadDDNetServers();
|
|
|
|
if(m_NumServers == 0)
|
|
{
|
|
Refresh(m_ServerlistType);
|
|
}
|
|
else
|
|
{
|
|
RecheckOfficial();
|
|
LoadDDNetRanks();
|
|
}
|
|
|
|
return m_pDDNetInfo;
|
|
}
|
|
|
|
bool CServerBrowser::IsRefreshing() const
|
|
{
|
|
return m_pFirstReqServer != 0;
|
|
}
|
|
|
|
bool CServerBrowser::IsRefreshingMasters() const
|
|
{
|
|
return m_pMasterServer->IsRefreshing();
|
|
}
|
|
|
|
|
|
int CServerBrowser::LoadingProgression() const
|
|
{
|
|
if(m_NumServers == 0)
|
|
return 0;
|
|
|
|
int Servers = m_NumServers;
|
|
int Loaded = m_NumServers-m_NumRequests;
|
|
return 100.0f * Loaded/Servers;
|
|
}
|
|
|
|
|
|
void CServerBrowser::ConfigSaveCallback(IConfig *pConfig, void *pUserData)
|
|
{
|
|
CServerBrowser *pSelf = (CServerBrowser *)pUserData;
|
|
|
|
char aAddrStr[128];
|
|
char aBuffer[256];
|
|
for(int i = 0; i < pSelf->m_NumFavoriteServers; i++)
|
|
{
|
|
net_addr_str(&pSelf->m_aFavoriteServers[i], aAddrStr, sizeof(aAddrStr), true);
|
|
str_format(aBuffer, sizeof(aBuffer), "add_favorite %s", aAddrStr);
|
|
pConfig->WriteLine(aBuffer);
|
|
}
|
|
}
|
|
|
|
void CServerBrowser::DDNetFilterAdd(char *pFilter, const char *pName)
|
|
{
|
|
if (DDNetFiltered(pFilter, pName))
|
|
return;
|
|
|
|
char aBuf[128];
|
|
str_format(aBuf, sizeof(aBuf), ",%s", pName);
|
|
str_append(pFilter, aBuf, 128);
|
|
}
|
|
|
|
void CServerBrowser::DDNetFilterRem(char *pFilter, const char *pName)
|
|
{
|
|
if (!DDNetFiltered(pFilter, pName))
|
|
return;
|
|
|
|
// rewrite exclude/filter list
|
|
char aBuf[128];
|
|
|
|
str_copy(aBuf, pFilter, sizeof(aBuf));
|
|
pFilter[0] = '\0';
|
|
|
|
char aToken[128];
|
|
for(const char *tok = aBuf; (tok = str_next_token(tok, ",", aToken, sizeof(aToken)));)
|
|
{
|
|
if(str_comp_nocase(pName, aToken) != 0)
|
|
{
|
|
char aBuf2[128];
|
|
str_format(aBuf2, sizeof(aBuf2), ",%s", aToken);
|
|
str_append(pFilter, aBuf2, 128);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool CServerBrowser::DDNetFiltered(char *pFilter, const char *pName)
|
|
{
|
|
return str_in_list(pFilter, ",", pName); // country not excluded
|
|
}
|
|
|
|
void CServerBrowser::CountryFilterClean(int Network)
|
|
{
|
|
char *pExcludeCountries = Network == NETWORK_KOG ? g_Config.m_BrFilterExcludeCountriesKoG : g_Config.m_BrFilterExcludeCountries;
|
|
char aNewList[128];
|
|
aNewList[0] = '\0';
|
|
|
|
for(int Network = 0; Network < NUM_NETWORKS; Network++)
|
|
{
|
|
for(int i = 0; i < m_aNetworks[Network].m_NumCountries; i++)
|
|
{
|
|
const char *pName = m_aNetworks[Network].m_aCountries[i].m_aName;
|
|
if(DDNetFiltered(pExcludeCountries, pName))
|
|
{
|
|
char aBuf[128];
|
|
str_format(aBuf, sizeof(aBuf), ",%s", pName);
|
|
str_append(aNewList, aBuf, sizeof(aNewList));
|
|
}
|
|
}
|
|
}
|
|
|
|
str_copy(pExcludeCountries, aNewList, sizeof(g_Config.m_BrFilterExcludeCountries));
|
|
}
|
|
|
|
void CServerBrowser::TypeFilterClean(int Network)
|
|
{
|
|
char *pExcludeTypes = Network == NETWORK_KOG ? g_Config.m_BrFilterExcludeTypesKoG : g_Config.m_BrFilterExcludeTypes;
|
|
char aNewList[128];
|
|
aNewList[0] = '\0';
|
|
|
|
for(int i = 0; i < m_aNetworks[Network].m_NumTypes; i++)
|
|
{
|
|
const char *pName = m_aNetworks[Network].m_aTypes[i];
|
|
if(DDNetFiltered(pExcludeTypes, pName))
|
|
{
|
|
char aBuf[128];
|
|
str_format(aBuf, sizeof(aBuf), ",%s", pName);
|
|
str_append(aNewList, aBuf, sizeof(aNewList));
|
|
}
|
|
}
|
|
|
|
str_copy(pExcludeTypes, aNewList, sizeof(g_Config.m_BrFilterExcludeTypes));
|
|
}
|