ddnet/src/engine/client/serverbrowser.cpp

1030 lines
28 KiB
C++
Raw Normal View History

2010-11-20 10:37:14 +00:00
/* (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. */
2011-11-29 21:18:40 +00:00
#include <algorithm> // sort TODO: remove this
2010-05-29 07:25:38 +00:00
#include <base/math.h>
2010-05-29 07:25:38 +00:00
#include <base/system.h>
2011-03-23 12:06:35 +00:00
2010-05-29 07:25:38 +00:00
#include <engine/shared/config.h>
#include <engine/shared/memheap.h>
2011-03-23 12:06:35 +00:00
#include <engine/shared/network.h>
#include <engine/shared/protocol.h>
2010-05-29 07:25:38 +00:00
#include <engine/config.h>
2011-03-23 12:06:35 +00:00
#include <engine/console.h>
#include <engine/friends.h>
#include <engine/masterserver.h>
2014-09-13 14:36:25 +00:00
#include <engine/storage.h>
2010-05-29 07:25:38 +00:00
#include <mastersrv/mastersrv.h>
2014-09-18 14:13:06 +00:00
#include <engine/external/json-parser/json.h>
#include "serverbrowser.h"
2010-05-29 07:25:38 +00:00
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)); }
2010-05-29 07:25:38 +00:00
};
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;
// the token is to keep server refresh separated from each other
m_CurrentToken = 1;
m_ServerlistType = 0;
m_BroadcastTime = 0;
}
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>();
2011-03-23 12:06:35 +00:00
m_pFriends = Kernel()->RequestInterface<IFriends>();
2010-05-29 07:25:38 +00:00
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;
}
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;
2010-05-29 07:25:38 +00:00
}
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_NumPlayers < b->m_Info.m_NumPlayers;
}
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;
}
2010-05-29 07:25:38 +00:00
void CServerBrowser::Filter()
{
int i = 0, p = 0;
m_NumSortedServers = 0;
// allocate the sorted list
if(m_NumSortedServersCapacity < m_NumServers)
{
if(m_pSortedServerlist)
mem_free(m_pSortedServerlist);
m_NumSortedServersCapacity = m_NumServers;
m_pSortedServerlist = (int *)mem_alloc(m_NumSortedServersCapacity*sizeof(int), 1);
}
// filter the servers
for(i = 0; i < m_NumServers; i++)
{
int Filtered = 0;
2011-06-26 15:10:13 +00:00
if(g_Config.m_BrFilterEmpty && ((g_Config.m_BrFilterSpectators && m_ppServerlist[i]->m_Info.m_NumPlayers == 0) || m_ppServerlist[i]->m_Info.m_NumClients == 0))
Filtered = 1;
else if(g_Config.m_BrFilterFull && ((g_Config.m_BrFilterSpectators && m_ppServerlist[i]->m_Info.m_NumPlayers == m_ppServerlist[i]->m_Info.m_MaxPlayers) ||
m_ppServerlist[i]->m_Info.m_NumClients == m_ppServerlist[i]->m_Info.m_MaxClients))
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))
2010-05-29 07:25:38 +00:00
{
Filtered = 1;
2010-05-29 07:25:38 +00:00
}
2011-06-26 15:10:13 +00:00
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)
)
2011-03-23 12:06:35 +00:00
{
2011-06-26 15:10:13 +00:00
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
2011-06-26 15:10:13 +00:00
{
if(g_Config.m_BrFilterCountry)
2011-06-26 15:10:13 +00:00
{
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;
}
}
2011-06-26 15:10:13 +00:00
}
if(!Filtered && g_Config.m_BrFilterString[0] != 0)
2011-06-26 15:10:13 +00:00
{
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))
2011-03-23 12:06:35 +00:00
{
MatchFound = 1;
m_ppServerlist[i]->m_Info.m_QuickSearchHit |= IServerBrowser::QUICK_SERVERNAME;
2011-03-23 12:06:35 +00:00
}
// 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;
}
}
2011-06-26 15:10:13 +00:00
// 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;
}
2010-05-29 07:25:38 +00:00
}
if(Filtered == 0)
2011-06-26 15:10:13 +00:00
{
// 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 = max(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;
}
2010-05-29 07:25:38 +00:00
}
}
int CServerBrowser::SortHash() const
{
int i = g_Config.m_BrSort&0xf;
i |= g_Config.m_BrFilterEmpty<<4;
i |= g_Config.m_BrFilterFull<<5;
i |= g_Config.m_BrFilterSpectators<<6;
2011-03-23 12:06:35 +00:00
i |= g_Config.m_BrFilterFriends<<7;
i |= g_Config.m_BrFilterPw<<8;
i |= g_Config.m_BrSortOrder<<9;
i |= g_Config.m_BrFilterCompatversion<<10;
i |= g_Config.m_BrFilterPure<<11;
i |= g_Config.m_BrFilterPureMap<<12;
2011-04-17 09:57:33 +00:00
i |= g_Config.m_BrFilterGametypeStrict<<13;
i |= g_Config.m_BrFilterCountry<<14;
i |= g_Config.m_BrFilterPing<<15;
2010-05-29 07:25:38 +00:00
return i;
}
void CServerBrowser::Sort()
{
int i;
// create filtered list
Filter();
// sort
if(g_Config.m_BrSort == IServerBrowser::SORT_NAME)
std::stable_sort(m_pSortedServerlist, m_pSortedServerlist+m_NumSortedServers, SortWrap(this, &CServerBrowser::SortCompareName));
2010-05-29 07:25:38 +00:00
else if(g_Config.m_BrSort == IServerBrowser::SORT_PING)
std::stable_sort(m_pSortedServerlist, m_pSortedServerlist+m_NumSortedServers, SortWrap(this, &CServerBrowser::SortComparePing));
2010-05-29 07:25:38 +00:00
else if(g_Config.m_BrSort == IServerBrowser::SORT_MAP)
std::stable_sort(m_pSortedServerlist, m_pSortedServerlist+m_NumSortedServers, SortWrap(this, &CServerBrowser::SortCompareMap));
2010-05-29 07:25:38 +00:00
else if(g_Config.m_BrSort == IServerBrowser::SORT_NUMPLAYERS)
std::stable_sort(m_pSortedServerlist, m_pSortedServerlist+m_NumSortedServers, SortWrap(this,
g_Config.m_BrFilterSpectators ? &CServerBrowser::SortCompareNumPlayers : &CServerBrowser::SortCompareNumClients));
2010-05-29 07:25:38 +00:00
else if(g_Config.m_BrSort == IServerBrowser::SORT_GAMETYPE)
std::stable_sort(m_pSortedServerlist, m_pSortedServerlist+m_NumSortedServers, SortWrap(this, &CServerBrowser::SortCompareGametype));
2010-05-29 07:25:38 +00:00
// 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;
2010-05-29 07:25:38 +00:00
m_NumRequests++;
}
void CServerBrowser::SetInfo(CServerEntry *pEntry, const CServerInfo &Info)
{
int Fav = pEntry->m_Info.m_Favorite;
pEntry->m_Info = Info;
pEntry->m_Info.m_Favorite = Fav;
pEntry->m_Info.m_NetAddr = pEntry->m_Addr;
// all these are just for nice compability
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;
2011-12-29 22:36:53 +00:00
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));
2010-05-29 07:25:38 +00:00
// 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 = 1;
}
// 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 **)mem_alloc(m_NumServerCapacity*sizeof(CServerEntry*), 1);
mem_copy(ppNewlist, m_ppServerlist, m_NumServers*sizeof(CServerEntry*));
mem_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)
{
static int temp = 0;
2010-05-29 07:25:38 +00:00
CServerEntry *pEntry = 0;
if(Type == IServerBrowser::SET_MASTER_ADD)
{
if(m_ServerlistType != IServerBrowser::TYPE_INTERNET)
return;
m_LastPacketTick = 0;
2014-01-10 15:19:46 +00:00
++temp;
2010-05-29 07:25:38 +00:00
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);
}
}
2014-09-13 14:36:25 +00:00
else if(Type == IServerBrowser::SET_DDNET_ADD)
{
if(m_ServerlistType != IServerBrowser::TYPE_DDNET)
return;
if(!Find(Addr))
{
pEntry = Add(Addr);
QueueRequest(pEntry);
}
}
2010-05-29 07:25:38 +00:00
else if(Type == IServerBrowser::SET_TOKEN)
{
if(Token != m_CurrentToken)
return;
pEntry = Find(Addr);
if(!pEntry)
pEntry = Add(Addr);
if(pEntry)
2010-05-29 07:25:38 +00:00
{
SetInfo(pEntry, *pInfo);
if (m_ServerlistType == IServerBrowser::TYPE_LAN)
pEntry->m_Info.m_Latency = min(static_cast<int>((time_get()-m_BroadcastTime)*1000/time_freq()), 999);
else if (pEntry->m_RequestTime > 0)
2014-05-03 18:57:19 +00:00
{
pEntry->m_Info.m_Latency = min(static_cast<int>((time_get()-pEntry->m_RequestTime)*1000/time_freq()), 999);
2014-05-03 18:57:19 +00:00
pEntry->m_RequestTime = -1; // Request has been answered
}
2010-05-29 07:25:38 +00:00
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;
2010-05-29 07:25:38 +00:00
// next token
m_CurrentToken = (m_CurrentToken+1)&0xff;
//
m_ServerlistType = Type;
if(Type == IServerBrowser::TYPE_LAN)
{
unsigned char Buffer[sizeof(SERVERBROWSE_GETINFO)+1];
CNetChunk Packet;
int i;
mem_copy(Buffer, SERVERBROWSE_GETINFO, sizeof(SERVERBROWSE_GETINFO));
Buffer[sizeof(SERVERBROWSE_GETINFO)] = m_CurrentToken;
/* do the broadcast version */
2010-05-29 07:25:38 +00:00
Packet.m_ClientID = -1;
mem_zero(&Packet, sizeof(Packet));
Packet.m_Address.type = m_pNetClient->NetType()|NETTYPE_LINK_BROADCAST;
2010-05-29 07:25:38 +00:00
Packet.m_Flags = NETSENDFLAG_CONNLESS;
Packet.m_DataSize = sizeof(Buffer);
Packet.m_pData = Buffer;
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");
2010-05-29 07:25:38 +00:00
}
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);
}
2014-09-13 14:36:25 +00:00
else if(Type == IServerBrowser::TYPE_DDNET)
{
LoadDDNet();
2014-09-19 21:52:09 +00:00
// remove unknown elements of exclude list
DDNetCountryFilterClean();
for(int i = 0; i < m_NumDDNetCountries; i++)
{
CDDNetCountry *pCntr = &m_aDDNetCountries[i];
// check for filter
if (DDNetCountryFiltered(pCntr->m_aName))
continue;
for(int g = 0; g < pCntr->m_NumServers; g++)
Set(pCntr->m_aServers[g], IServerBrowser::SET_DDNET_ADD, -1, 0);
}
2014-09-13 14:36:25 +00:00
}
2010-05-29 07:25:38 +00:00
}
void CServerBrowser::RequestImpl(const NETADDR &Addr, CServerEntry *pEntry) const
{
unsigned char Buffer[sizeof(SERVERBROWSE_GETINFO)+1];
2010-05-29 07:25:38 +00:00
CNetChunk Packet;
if(g_Config.m_Debug)
{
char aAddrStr[NETADDR_MAXSTRSIZE];
2011-12-29 22:36:53 +00:00
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);
2010-05-29 07:25:38 +00:00
}
mem_copy(Buffer, SERVERBROWSE_GETINFO, sizeof(SERVERBROWSE_GETINFO));
Buffer[sizeof(SERVERBROWSE_GETINFO)] = m_CurrentToken;
2010-05-29 07:25:38 +00:00
Packet.m_ClientID = -1;
Packet.m_Address = Addr;
Packet.m_Flags = NETSENDFLAG_CONNLESS;
Packet.m_DataSize = sizeof(Buffer);
Packet.m_pData = Buffer;
2010-05-29 07:25:38 +00:00
m_pNetClient->Send(&Packet);
if(pEntry)
pEntry->m_RequestTime = time_get();
}
2014-01-08 05:15:56 +00:00
void CServerBrowser::RequestImpl64(const NETADDR &Addr, CServerEntry *pEntry) const
{
unsigned char Buffer[sizeof(SERVERBROWSE_GETINFO64)+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_GETINFO64, sizeof(SERVERBROWSE_GETINFO64));
Buffer[sizeof(SERVERBROWSE_GETINFO64)] = m_CurrentToken;
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();
}
2010-05-29 07:25:38 +00:00
void CServerBrowser::Request(const NETADDR &Addr) const
{
2014-01-14 20:40:55 +00:00
// Call both because we can't know what kind the server is
RequestImpl64(Addr, 0);
2014-01-11 02:53:50 +00:00
RequestImpl(Addr, 0);
2010-05-29 07:25:38 +00:00
}
void CServerBrowser::Update(bool ForceResort)
{
int64 Timeout = time_freq();
2010-05-29 07:25:38 +00:00
int64 Now = time_get();
int Count;
CServerEntry *pEntry, *pNext;
2010-05-29 07:25:38 +00:00
// do server list requests
if(m_NeedRefresh && !m_pMasterServer->IsRefreshing())
{
NETADDR Addr;
CNetChunk Packet;
int i = 0;
2010-05-29 07:25:38 +00:00
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;
2010-05-29 07:25:38 +00:00
Addr = m_pMasterServer->GetAddr(i);
m_pMasterServer->SetCount(i, -1);
Packet.m_Address = Addr;
2014-01-03 15:14:41 +00:00
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)
{
2014-01-03 15:14:41 +00:00
/* ignore Server
m_MasterServerCount = -1;
2014-01-03 15:14:41 +00:00
return;
// we don't have the required server information
*/
}
2014-01-03 15:14:41 +00:00
else
2014-05-17 14:15:01 +00:00
m_MasterServerCount += Count;
}
//request Server-List
NETADDR Addr;
2014-05-17 14:15:01 +00:00
CNetChunk Packet;
2010-05-29 07:25:38 +00:00
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++)
2010-05-29 07:25:38 +00:00
{
if(!m_pMasterServer->IsValid(i))
2010-05-29 07:25:38 +00:00
continue;
Addr = m_pMasterServer->GetAddr(i);
2010-05-29 07:25:38 +00:00
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;
2014-01-03 15:14:41 +00:00
}
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;
}
2014-01-08 06:55:26 +00:00
//if(g_Config.m_Debug)
//{
// dbg_msg("client_srvbrowse", "ServerCount2: %d", m_MasterServerCount);
//}
2014-01-03 15:14:41 +00:00
}
if(m_MasterServerCount > m_NumRequests + m_LastPacketTick)
2010-05-29 07:25:38 +00:00
{
++m_LastPacketTick;
return; //wait for more packets
2010-05-29 07:25:38 +00:00
}
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;
}
2010-05-29 07:25:38 +00:00
// no more then 10 concurrent requests
if(Count == m_CurrentMaxRequests)
2010-05-29 07:25:38 +00:00
break;
if(pEntry->m_RequestTime == 0)
2014-01-08 05:15:56 +00:00
{
2014-01-14 20:40:55 +00:00
if (pEntry->m_Is64)
2014-01-11 20:38:50 +00:00
RequestImpl64(pEntry->m_Addr, pEntry);
2014-01-14 20:40:55 +00:00
else
RequestImpl(pEntry->m_Addr, pEntry);
2014-01-08 05:15:56 +00:00
}
2010-05-29 07:25:38 +00:00
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;
2014-01-03 15:14:41 +00:00
}
}
2010-05-29 07:25:38 +00:00
// check if we need to resort
if(m_Sorthash != SortHash() || ForceResort)
2010-05-29 07:25:38 +00:00
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 = 1;
if(g_Config.m_Debug)
{
char aAddrStr[NETADDR_MAXSTRSIZE];
2011-12-29 22:36:53 +00:00
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);
}
2010-05-29 07:25:38 +00:00
}
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 = 0;
return;
}
}
}
2014-09-13 14:36:25 +00:00
void CServerBrowser::LoadDDNet()
{
2014-09-18 14:13:06 +00:00
// reset servers / countries
m_NumDDNetCountries = 0;
2014-09-13 14:36:25 +00:00
2014-09-18 14:13:06 +00:00
// load ddnet server list
IStorage *pStorage = Kernel()->RequestInterface<IStorage>();
IOHANDLE File = pStorage->OpenFile("ddnet-servers.json", IOFLAG_READ, IStorage::TYPE_ALL);
2014-09-13 14:36:25 +00:00
if(File)
{
2014-09-18 14:13:06 +00:00
char aBuf[4096*4];
mem_zero(aBuf, sizeof(aBuf));
io_read(File, aBuf, sizeof(aBuf));
io_close(File);
2014-09-13 14:36:25 +00:00
2014-09-18 14:13:06 +00:00
// parse JSON
json_value *pCountries = json_parse(aBuf);
2014-09-13 14:36:25 +00:00
if (pCountries && pCountries->type == json_array)
2014-09-13 14:36:25 +00:00
{
2014-09-18 14:13:06 +00:00
for (int i = 0; i < json_array_length(pCountries) && m_NumDDNetCountries < MAX_DDNET_COUNTRIES; i++)
2014-09-13 14:36:25 +00:00
{
2014-09-18 14:13:06 +00:00
// pSrv - { name, flagId, servers }
const json_value *pSrv = json_array_get(pCountries, i);
const json_value *pAddrs = json_object_get(pSrv, "servers");
const json_value *pName = json_object_get(pSrv, "name");
const json_value *pFlagID = json_object_get(pSrv, "flagId");
2014-09-19 21:52:09 +00:00
if (pSrv->type != json_object || pAddrs->type != json_array || pName->type != json_string || pFlagID->type != json_integer)
continue; // invalid attributes
2014-09-18 14:13:06 +00:00
// build structure
CDDNetCountry *pCntr = &m_aDDNetCountries[m_NumDDNetCountries];
2014-09-19 21:52:09 +00:00
pCntr->Reset();
2014-09-18 14:13:06 +00:00
str_copy(pCntr->m_aName, json_string_get(pName), sizeof(pCntr->m_aName));
2014-09-19 21:52:09 +00:00
pCntr->m_FlagID = json_int_get(pFlagID);
2014-09-18 14:13:06 +00:00
// add addresses
2014-09-19 21:52:09 +00:00
for (int g = 0; g < json_array_length(pAddrs); g++)
2014-09-18 14:13:06 +00:00
{
2014-09-19 21:52:09 +00:00
const json_value *pAddr = json_array_get(pAddrs, g);
2014-09-18 14:13:06 +00:00
const char *pStr = json_string_get(pAddr);
2014-09-19 21:52:09 +00:00
net_addr_from_str(&pCntr->m_aServers[g], pStr);
2014-09-18 14:13:06 +00:00
pCntr->m_NumServers++;
}
m_NumDDNetCountries++;
2014-09-13 14:36:25 +00:00
}
}
}
}
bool CServerBrowser::IsRefreshing() const
{
return m_pFirstReqServer != 0;
}
2010-05-29 07:25:38 +00:00
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;
}
2010-05-29 07:25:38 +00:00
void CServerBrowser::ConfigSaveCallback(IConfig *pConfig, void *pUserData)
{
CServerBrowser *pSelf = (CServerBrowser *)pUserData;
char aAddrStr[128];
char aBuffer[256];
2011-12-29 22:36:53 +00:00
for(int i = 0; i < pSelf->m_NumFavoriteServers; i++)
2010-05-29 07:25:38 +00:00
{
2011-12-29 22:36:53 +00:00
net_addr_str(&pSelf->m_aFavoriteServers[i], aAddrStr, sizeof(aAddrStr), true);
2010-05-29 07:25:38 +00:00
str_format(aBuffer, sizeof(aBuffer), "add_favorite %s", aAddrStr);
pConfig->WriteLine(aBuffer);
}
}
2014-09-19 21:52:09 +00:00
void CServerBrowser::DDNetCountryFilterAdd(const char *pName)
{
if (DDNetCountryFiltered(pName))
return;
char aBuf[256];
str_format(aBuf, sizeof(aBuf), ",%s", pName);
str_append(g_Config.m_BrFilterExcludeCountries, aBuf, sizeof(g_Config.m_BrFilterExcludeCountries));
}
void CServerBrowser::DDNetCountryFilterRem(const char *pName)
{
if (!DDNetCountryFiltered(pName))
return;
// rewrite exclude/filter list
char aBuf[256];
char *p;
str_copy(aBuf, g_Config.m_BrFilterExcludeCountries, sizeof(aBuf));
g_Config.m_BrFilterExcludeCountries[0] = '\0';
p = strtok(aBuf, ",");
while(p)
{
if(str_comp_nocase(pName, p) != 0)
{
char aBuf2[256];
str_format(aBuf2, sizeof(aBuf2), ",%s", p);
str_append(g_Config.m_BrFilterExcludeCountries, aBuf2, sizeof(g_Config.m_BrFilterExcludeCountries));
}
p = strtok(NULL, ",");
}
}
bool CServerBrowser::DDNetCountryFiltered(const char *pName)
{
char aBuf[256];
char *p;
str_copy(aBuf, g_Config.m_BrFilterExcludeCountries, sizeof(aBuf));
p = strtok(aBuf, ",");
while(p)
{
if(str_comp_nocase(pName, p) == 0)
return true; // country excluded
p = strtok(NULL, ",");
}
return false; // contry not excluded
}
void CServerBrowser::DDNetCountryFilterClean()
{
char aNewList[256];
for(int i = 0; i < m_NumDDNetCountries; i++)
{
const char *pName = m_aDDNetCountries[i].m_aName;
if(DDNetCountryFiltered(pName))
{
char aBuf[256];
str_format(aBuf, sizeof(aBuf), ",%s", pName);
str_append(aNewList, aBuf, sizeof(aNewList));
}
}
str_copy(g_Config.m_BrFilterExcludeCountries, aNewList, sizeof(g_Config.m_BrFilterExcludeCountries));
}