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
|
|
|
|
2011-01-19 14:54:50 +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>
|
2015-08-30 09:21:31 +00:00
|
|
|
#include <engine/shared/packer.h>
|
2011-03-23 12:06:35 +00:00
|
|
|
#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>
|
2012-10-07 21:56:37 +00:00
|
|
|
#include <engine/engine.h>
|
2011-03-23 12:06:35 +00:00
|
|
|
#include <engine/friends.h>
|
|
|
|
#include <engine/masterserver.h>
|
2010-05-29 07:25:38 +00:00
|
|
|
|
|
|
|
#include <mastersrv/mastersrv.h>
|
|
|
|
|
2011-03-27 16:00:54 +00:00
|
|
|
#include "serverbrowser.h"
|
2010-05-29 07:25:38 +00:00
|
|
|
|
|
|
|
class SortWrap
|
|
|
|
{
|
2011-11-10 19:18:05 +00:00
|
|
|
typedef bool (CServerBrowser::CServerFilter::*SortFunc)(int, int) const;
|
2010-05-29 07:25:38 +00:00
|
|
|
SortFunc m_pfnSort;
|
2011-11-10 19:18:05 +00:00
|
|
|
CServerBrowser::CServerFilter *m_pThis;
|
2010-05-29 07:25:38 +00:00
|
|
|
public:
|
2011-11-10 19:18:05 +00:00
|
|
|
SortWrap(CServerBrowser::CServerFilter *t, SortFunc f) : m_pfnSort(f), m_pThis(t) {}
|
2011-11-29 21:34:47 +00:00
|
|
|
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;
|
|
|
|
|
2012-10-07 21:56:37 +00:00
|
|
|
// favorites
|
2010-05-29 07:25:38 +00:00
|
|
|
m_NumFavoriteServers = 0;
|
2012-10-07 21:56:37 +00:00
|
|
|
m_FavLookup.m_LookupCount = 0;
|
|
|
|
m_FavLookup.m_Active = false;
|
2010-05-29 07:25:38 +00:00
|
|
|
|
2012-10-07 21:56:37 +00:00
|
|
|
//
|
2010-05-29 07:25:38 +00:00
|
|
|
mem_zero(m_aServerlistIp, sizeof(m_aServerlistIp));
|
|
|
|
|
|
|
|
m_pFirstReqServer = 0; // request list
|
|
|
|
m_pLastReqServer = 0;
|
|
|
|
m_NumRequests = 0;
|
|
|
|
|
|
|
|
m_NeedRefresh = 0;
|
|
|
|
|
|
|
|
m_NumServers = 0;
|
|
|
|
m_NumServerCapacity = 0;
|
|
|
|
|
2011-11-17 19:21:59 +00:00
|
|
|
m_NumPlayers = 0;
|
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
// the token is to keep server refresh separated from each other
|
2015-03-17 09:02:19 +00:00
|
|
|
m_CurrentLanToken = 1;
|
2010-05-29 07:25:38 +00:00
|
|
|
|
|
|
|
m_ServerlistType = 0;
|
|
|
|
m_BroadcastTime = 0;
|
2011-11-10 19:18:05 +00:00
|
|
|
}
|
|
|
|
|
2011-11-12 15:09:24 +00:00
|
|
|
int CServerBrowser::AddFilter(int SortHash, int Ping, int Country, const char* pGametype, const char* pServerAddress)
|
2011-11-10 19:18:05 +00:00
|
|
|
{
|
|
|
|
CServerFilter Filter;
|
|
|
|
Filter.m_SortHash = SortHash;
|
|
|
|
Filter.m_Ping = Ping;
|
|
|
|
Filter.m_Country = Country;
|
|
|
|
str_copy(Filter.m_aGametype, pGametype, sizeof(Filter.m_aGametype));
|
|
|
|
str_copy(Filter.m_aServerAddress, pServerAddress, sizeof(Filter.m_aServerAddress));
|
|
|
|
Filter.m_pSortedServerlist = 0;
|
|
|
|
Filter.m_NumSortedServers = 0;
|
|
|
|
Filter.m_NumSortedServersCapacity = 0;
|
2011-11-17 19:21:59 +00:00
|
|
|
Filter.m_NumPlayers = 0;
|
2011-11-12 15:09:24 +00:00
|
|
|
Filter.m_pServerBrowser = this;
|
2011-11-10 19:18:05 +00:00
|
|
|
m_lFilters.add(Filter);
|
2011-11-12 15:09:24 +00:00
|
|
|
|
|
|
|
return m_lFilters.size()-1;
|
|
|
|
}
|
|
|
|
|
2012-01-15 00:05:08 +00:00
|
|
|
void CServerBrowser::GetFilter(int Index, int *pSortHash, int *pPing, int *pCountry, char* pGametype, char* pServerAddress)
|
|
|
|
{
|
|
|
|
CServerFilter *pFilter = &m_lFilters[Index];
|
|
|
|
*pSortHash = pFilter->m_SortHash;
|
|
|
|
*pPing = pFilter->m_Ping;
|
|
|
|
*pCountry = pFilter->m_Country;
|
|
|
|
str_copy(pGametype, pFilter->m_aGametype, sizeof(pFilter->m_aGametype));
|
|
|
|
str_copy(pServerAddress, pFilter->m_aServerAddress, sizeof(pFilter->m_aServerAddress));
|
|
|
|
}
|
|
|
|
|
|
|
|
void CServerBrowser::SetFilter(int Index, int SortHash, int Ping, int Country, const char* pGametype, const char* pServerAddress)
|
|
|
|
{
|
|
|
|
CServerFilter *pFilter = &m_lFilters[Index];
|
|
|
|
pFilter->m_SortHash = SortHash;
|
|
|
|
pFilter->m_Ping = Ping;
|
|
|
|
pFilter->m_Country = Country;
|
|
|
|
str_copy(pFilter->m_aGametype, pGametype, sizeof(pFilter->m_aGametype));
|
|
|
|
str_copy(pFilter->m_aServerAddress, pServerAddress, sizeof(pFilter->m_aServerAddress));
|
|
|
|
|
|
|
|
pFilter->m_pServerBrowser->Update(true);
|
|
|
|
}
|
|
|
|
|
2011-11-12 15:09:24 +00:00
|
|
|
void CServerBrowser::RemoveFilter(int Index)
|
|
|
|
{
|
|
|
|
m_lFilters.remove_index(Index);
|
2010-05-29 07:25:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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>();
|
2010-08-17 22:06:00 +00:00
|
|
|
m_pConsole = Kernel()->RequestInterface<IConsole>();
|
2012-10-07 21:56:37 +00:00
|
|
|
m_pEngine = Kernel()->RequestInterface<IEngine>();
|
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);
|
2012-10-07 21:56:37 +00:00
|
|
|
|
|
|
|
m_pConsole->Register("add_favorite", "s", CFGFLAG_CLIENT, ConAddFavorite, this, "Add a server as a favorite");
|
|
|
|
m_pConsole->Register("remove_favorite", "s", CFGFLAG_CLIENT, ConRemoveFavorite, this, "Remove a server from favorites");
|
2010-05-29 07:25:38 +00:00
|
|
|
}
|
|
|
|
|
2011-11-10 19:18:05 +00:00
|
|
|
const CServerInfo *CServerBrowser::SortedGet(int FilterIndex, int Index) const
|
2010-05-29 07:25:38 +00:00
|
|
|
{
|
2011-11-10 19:18:05 +00:00
|
|
|
if(Index < 0 || Index >= m_lFilters[FilterIndex].m_NumSortedServers)
|
2010-05-29 07:25:38 +00:00
|
|
|
return 0;
|
2011-11-12 15:09:24 +00:00
|
|
|
return &m_ppServerlist[m_lFilters[FilterIndex].m_pSortedServerlist[Index]]->m_Info;
|
2010-05-29 07:25:38 +00:00
|
|
|
}
|
|
|
|
|
2011-11-14 19:18:50 +00:00
|
|
|
const void *CServerBrowser::GetID(int FilterIndex, int Index) const
|
|
|
|
{
|
|
|
|
return &m_lFilters[FilterIndex].m_pSortedServerlist[Index];
|
|
|
|
}
|
|
|
|
|
2011-11-12 15:09:24 +00:00
|
|
|
CServerBrowser::CServerFilter::~CServerFilter()
|
|
|
|
{
|
2011-11-17 19:21:59 +00:00
|
|
|
mem_free(m_pSortedServerlist);
|
2011-11-12 15:09:24 +00:00
|
|
|
}
|
2010-05-29 07:25:38 +00:00
|
|
|
|
2011-11-10 19:18:05 +00:00
|
|
|
bool CServerBrowser::CServerFilter::SortCompareName(int Index1, int Index2) const
|
2010-05-29 07:25:38 +00:00
|
|
|
{
|
2011-11-12 15:09:24 +00:00
|
|
|
CServerEntry *a = m_pServerBrowser->m_ppServerlist[Index1];
|
|
|
|
CServerEntry *b = m_pServerBrowser->m_ppServerlist[Index2];
|
2010-08-12 17:29:59 +00:00
|
|
|
// make sure empty entries are listed last
|
2015-03-17 09:02:19 +00:00
|
|
|
return (a->m_InfoState == CServerEntry::STATE_READY && b->m_InfoState == CServerEntry::STATE_READY) || (a->m_InfoState != CServerEntry::STATE_READY && b->m_InfoState != CServerEntry::STATE_READY) ? str_comp_nocase(a->m_Info.m_aName, b->m_Info.m_aName) < 0 :
|
|
|
|
a->m_InfoState == CServerEntry::STATE_READY;
|
2010-05-29 07:25:38 +00:00
|
|
|
}
|
|
|
|
|
2011-11-10 19:18:05 +00:00
|
|
|
bool CServerBrowser::CServerFilter::SortCompareMap(int Index1, int Index2) const
|
2010-05-29 07:25:38 +00:00
|
|
|
{
|
2011-11-12 15:09:24 +00:00
|
|
|
CServerEntry *a = m_pServerBrowser->m_ppServerlist[Index1];
|
|
|
|
CServerEntry *b = m_pServerBrowser->m_ppServerlist[Index2];
|
2012-07-07 16:35:08 +00:00
|
|
|
int Result = str_comp_nocase(a->m_Info.m_aMap, b->m_Info.m_aMap);
|
2012-09-15 17:47:49 +00:00
|
|
|
return Result < 0 || (Result == 0 && (a->m_Info.m_Flags&FLAG_PURE) && !(b->m_Info.m_Flags&FLAG_PURE));
|
2010-05-29 07:25:38 +00:00
|
|
|
}
|
|
|
|
|
2011-11-10 19:18:05 +00:00
|
|
|
bool CServerBrowser::CServerFilter::SortComparePing(int Index1, int Index2) const
|
2010-05-29 07:25:38 +00:00
|
|
|
{
|
2011-11-12 15:09:24 +00:00
|
|
|
CServerEntry *a = m_pServerBrowser->m_ppServerlist[Index1];
|
|
|
|
CServerEntry *b = m_pServerBrowser->m_ppServerlist[Index2];
|
2012-07-29 10:09:31 +00:00
|
|
|
return a->m_Info.m_Latency < b->m_Info.m_Latency ||
|
|
|
|
(a->m_Info.m_Latency == b->m_Info.m_Latency && (a->m_Info.m_Flags&FLAG_PURE) && !(b->m_Info.m_Flags&FLAG_PURE));
|
2010-05-29 07:25:38 +00:00
|
|
|
}
|
|
|
|
|
2011-11-10 19:18:05 +00:00
|
|
|
bool CServerBrowser::CServerFilter::SortCompareGametype(int Index1, int Index2) const
|
2010-05-29 07:25:38 +00:00
|
|
|
{
|
2011-11-12 15:09:24 +00:00
|
|
|
CServerEntry *a = m_pServerBrowser->m_ppServerlist[Index1];
|
|
|
|
CServerEntry *b = m_pServerBrowser->m_ppServerlist[Index2];
|
2012-03-25 10:40:36 +00:00
|
|
|
return str_comp_nocase(a->m_Info.m_aGameType, b->m_Info.m_aGameType) < 0;
|
2010-05-29 07:25:38 +00:00
|
|
|
}
|
|
|
|
|
2011-11-10 19:18:05 +00:00
|
|
|
bool CServerBrowser::CServerFilter::SortCompareNumPlayers(int Index1, int Index2) const
|
2010-05-29 07:25:38 +00:00
|
|
|
{
|
2011-11-12 15:09:24 +00:00
|
|
|
CServerEntry *a = m_pServerBrowser->m_ppServerlist[Index1];
|
|
|
|
CServerEntry *b = m_pServerBrowser->m_ppServerlist[Index2];
|
2012-07-29 10:09:31 +00:00
|
|
|
return a->m_Info.m_NumPlayers < b->m_Info.m_NumPlayers ||
|
|
|
|
(a->m_Info.m_NumPlayers == b->m_Info.m_NumPlayers && !(a->m_Info.m_Flags&FLAG_PURE) && (b->m_Info.m_Flags&FLAG_PURE));
|
2010-05-29 07:25:38 +00:00
|
|
|
}
|
|
|
|
|
2011-11-10 19:18:05 +00:00
|
|
|
bool CServerBrowser::CServerFilter::SortCompareNumClients(int Index1, int Index2) const
|
2011-03-20 14:33:49 +00:00
|
|
|
{
|
2011-11-12 15:09:24 +00:00
|
|
|
CServerEntry *a = m_pServerBrowser->m_ppServerlist[Index1];
|
|
|
|
CServerEntry *b = m_pServerBrowser->m_ppServerlist[Index2];
|
2012-07-29 10:09:31 +00:00
|
|
|
return a->m_Info.m_NumClients < b->m_Info.m_NumClients ||
|
|
|
|
(a->m_Info.m_NumClients == b->m_Info.m_NumClients && !(a->m_Info.m_Flags&FLAG_PURE) && (b->m_Info.m_Flags&FLAG_PURE));
|
2011-03-20 14:33:49 +00:00
|
|
|
}
|
|
|
|
|
2011-11-10 19:18:05 +00:00
|
|
|
void CServerBrowser::CServerFilter::Filter()
|
2010-05-29 07:25:38 +00:00
|
|
|
{
|
2011-11-12 15:09:24 +00:00
|
|
|
int NumServers = m_pServerBrowser->m_NumServers;
|
2010-05-29 07:25:38 +00:00
|
|
|
m_NumSortedServers = 0;
|
2011-11-17 19:21:59 +00:00
|
|
|
m_NumPlayers = 0;
|
2011-11-14 19:18:50 +00:00
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
// allocate the sorted list
|
2011-11-10 19:18:05 +00:00
|
|
|
if(m_NumSortedServersCapacity < NumServers)
|
2010-05-29 07:25:38 +00:00
|
|
|
{
|
|
|
|
if(m_pSortedServerlist)
|
|
|
|
mem_free(m_pSortedServerlist);
|
2012-10-07 21:56:37 +00:00
|
|
|
m_NumSortedServersCapacity = max(1000, NumServers+NumServers/2);
|
2010-05-29 07:25:38 +00:00
|
|
|
m_pSortedServerlist = (int *)mem_alloc(m_NumSortedServersCapacity*sizeof(int), 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
// filter the servers
|
2012-10-07 21:56:37 +00:00
|
|
|
for(int i = 0; i < NumServers; i++)
|
2010-05-29 07:25:38 +00:00
|
|
|
{
|
|
|
|
int Filtered = 0;
|
|
|
|
|
2011-11-12 15:09:24 +00:00
|
|
|
if(m_SortHash&FILTER_EMPTY && ((m_SortHash&FILTER_SPECTATORS && m_pServerBrowser->m_ppServerlist[i]->m_Info.m_NumPlayers == 0) || m_pServerBrowser->m_ppServerlist[i]->m_Info.m_NumClients == 0))
|
|
|
|
Filtered = 1;
|
|
|
|
else if(m_SortHash&FILTER_FULL && ((m_SortHash&FILTER_SPECTATORS && m_pServerBrowser->m_ppServerlist[i]->m_Info.m_NumPlayers == m_pServerBrowser->m_ppServerlist[i]->m_Info.m_MaxPlayers) ||
|
|
|
|
m_pServerBrowser->m_ppServerlist[i]->m_Info.m_NumClients == m_pServerBrowser->m_ppServerlist[i]->m_Info.m_MaxClients))
|
2011-06-26 15:10:13 +00:00
|
|
|
Filtered = 1;
|
2011-11-12 15:09:24 +00:00
|
|
|
else if(m_SortHash&FILTER_PW && m_pServerBrowser->m_ppServerlist[i]->m_Info.m_Flags&FLAG_PASSWORD)
|
2011-06-26 15:10:13 +00:00
|
|
|
Filtered = 1;
|
2011-11-12 15:09:24 +00:00
|
|
|
else if(m_SortHash&FILTER_FAVORITE && !m_pServerBrowser->m_ppServerlist[i]->m_Info.m_Favorite)
|
2011-06-26 15:10:13 +00:00
|
|
|
Filtered = 1;
|
2011-11-12 15:09:24 +00:00
|
|
|
else if(m_SortHash&FILTER_PURE && !(m_pServerBrowser->m_ppServerlist[i]->m_Info.m_Flags&FLAG_PURE))
|
2011-03-31 19:22:12 +00:00
|
|
|
Filtered = 1;
|
2011-11-12 15:09:24 +00:00
|
|
|
else if(m_SortHash&FILTER_PURE_MAP && !(m_pServerBrowser->m_ppServerlist[i]->m_Info.m_Flags&FLAG_PUREMAP))
|
2011-06-26 15:10:13 +00:00
|
|
|
Filtered = 1;
|
2011-11-12 15:09:24 +00:00
|
|
|
else if(m_SortHash&FILTER_PING && m_Ping < m_pServerBrowser->m_ppServerlist[i]->m_Info.m_Latency)
|
2011-06-26 15:10:13 +00:00
|
|
|
Filtered = 1;
|
2011-11-12 15:09:24 +00:00
|
|
|
else if(m_SortHash&FILTER_COMPAT_VERSION && str_comp_num(m_pServerBrowser->m_ppServerlist[i]->m_Info.m_aVersion, m_pServerBrowser->m_aNetVersion, 3) != 0)
|
2011-06-26 15:10:13 +00:00
|
|
|
Filtered = 1;
|
2011-11-12 15:09:24 +00:00
|
|
|
else if(m_aServerAddress[0] && !str_find_nocase(m_pServerBrowser->m_ppServerlist[i]->m_Info.m_aAddress, m_aServerAddress))
|
2011-06-26 15:10:13 +00:00
|
|
|
Filtered = 1;
|
2011-11-12 15:09:24 +00:00
|
|
|
else if(m_SortHash&FILTER_GAMETYPE_STRICT && m_aGametype[0] && str_comp_nocase(m_pServerBrowser->m_ppServerlist[i]->m_Info.m_aGameType, m_aGametype))
|
2011-06-26 15:10:13 +00:00
|
|
|
Filtered = 1;
|
2011-11-12 15:09:24 +00:00
|
|
|
else if(!(m_SortHash&FILTER_GAMETYPE_STRICT) && m_aGametype[0] && !str_find_nocase(m_pServerBrowser->m_ppServerlist[i]->m_Info.m_aGameType, m_aGametype))
|
2011-06-26 15:10:13 +00:00
|
|
|
Filtered = 1;
|
2011-06-29 20:27:32 +00:00
|
|
|
else
|
2011-06-26 15:10:13 +00:00
|
|
|
{
|
2011-11-10 19:18:05 +00:00
|
|
|
if(m_SortHash&FILTER_COUNTRY)
|
2011-06-26 15:10:13 +00:00
|
|
|
{
|
2011-06-29 20:27:32 +00:00
|
|
|
Filtered = 1;
|
|
|
|
// match against player country
|
2012-10-07 21:56:37 +00:00
|
|
|
for(int p = 0; p < m_pServerBrowser->m_ppServerlist[i]->m_Info.m_NumClients; p++)
|
2011-06-29 20:27:32 +00:00
|
|
|
{
|
2011-11-12 15:09:24 +00:00
|
|
|
if(m_pServerBrowser->m_ppServerlist[i]->m_Info.m_aClients[p].m_Country == m_Country)
|
2011-06-29 20:27:32 +00:00
|
|
|
{
|
|
|
|
Filtered = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2011-06-26 15:10:13 +00:00
|
|
|
}
|
2011-03-18 18:03:13 +00:00
|
|
|
|
2011-06-29 20:27:32 +00:00
|
|
|
if(!Filtered && g_Config.m_BrFilterString[0] != 0)
|
2011-06-26 15:10:13 +00:00
|
|
|
{
|
2011-06-29 20:27:32 +00:00
|
|
|
int MatchFound = 0;
|
|
|
|
|
2011-11-12 15:09:24 +00:00
|
|
|
m_pServerBrowser->m_ppServerlist[i]->m_Info.m_QuickSearchHit = 0;
|
2011-06-29 20:27:32 +00:00
|
|
|
|
|
|
|
// match against server name
|
2011-11-12 15:09:24 +00:00
|
|
|
if(str_find_nocase(m_pServerBrowser->m_ppServerlist[i]->m_Info.m_aName, g_Config.m_BrFilterString))
|
2011-03-23 12:06:35 +00:00
|
|
|
{
|
|
|
|
MatchFound = 1;
|
2011-11-12 15:09:24 +00:00
|
|
|
m_pServerBrowser->m_ppServerlist[i]->m_Info.m_QuickSearchHit |= IServerBrowser::QUICK_SERVERNAME;
|
2011-03-23 12:06:35 +00:00
|
|
|
}
|
|
|
|
|
2011-06-29 20:27:32 +00:00
|
|
|
// match against players
|
2012-10-07 21:56:37 +00:00
|
|
|
for(int p = 0; p < m_pServerBrowser->m_ppServerlist[i]->m_Info.m_NumClients; p++)
|
2011-06-29 20:27:32 +00:00
|
|
|
{
|
2011-11-12 15:09:24 +00:00
|
|
|
if(str_find_nocase(m_pServerBrowser->m_ppServerlist[i]->m_Info.m_aClients[p].m_aName, g_Config.m_BrFilterString) ||
|
|
|
|
str_find_nocase(m_pServerBrowser->m_ppServerlist[i]->m_Info.m_aClients[p].m_aClan, g_Config.m_BrFilterString))
|
2011-06-29 20:27:32 +00:00
|
|
|
{
|
|
|
|
MatchFound = 1;
|
2011-11-12 15:09:24 +00:00
|
|
|
m_pServerBrowser->m_ppServerlist[i]->m_Info.m_QuickSearchHit |= IServerBrowser::QUICK_PLAYER;
|
2011-06-29 20:27:32 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2011-06-26 15:10:13 +00:00
|
|
|
|
2011-06-29 20:27:32 +00:00
|
|
|
// match against map
|
2011-11-12 15:09:24 +00:00
|
|
|
if(str_find_nocase(m_pServerBrowser->m_ppServerlist[i]->m_Info.m_aMap, g_Config.m_BrFilterString))
|
2011-06-29 20:27:32 +00:00
|
|
|
{
|
|
|
|
MatchFound = 1;
|
2011-11-12 15:09:24 +00:00
|
|
|
m_pServerBrowser->m_ppServerlist[i]->m_Info.m_QuickSearchHit |= IServerBrowser::QUICK_MAPNAME;
|
2011-06-29 20:27:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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
|
2011-11-12 15:09:24 +00:00
|
|
|
m_pServerBrowser->m_ppServerlist[i]->m_Info.m_FriendState = IFriends::FRIEND_NO;
|
2012-10-07 21:56:37 +00:00
|
|
|
for(int p = 0; p < m_pServerBrowser->m_ppServerlist[i]->m_Info.m_NumClients; p++)
|
2011-06-26 15:10:13 +00:00
|
|
|
{
|
2011-11-12 15:09:24 +00:00
|
|
|
m_pServerBrowser->m_ppServerlist[i]->m_Info.m_aClients[p].m_FriendState = m_pServerBrowser->m_pFriends->GetFriendState(m_pServerBrowser->m_ppServerlist[i]->m_Info.m_aClients[p].m_aName,
|
|
|
|
m_pServerBrowser->m_ppServerlist[i]->m_Info.m_aClients[p].m_aClan);
|
|
|
|
m_pServerBrowser->m_ppServerlist[i]->m_Info.m_FriendState = max(m_pServerBrowser->m_ppServerlist[i]->m_Info.m_FriendState, m_pServerBrowser->m_ppServerlist[i]->m_Info.m_aClients[p].m_FriendState);
|
2011-06-26 15:10:13 +00:00
|
|
|
}
|
|
|
|
|
2011-11-12 15:09:24 +00:00
|
|
|
if(!(m_SortHash&FILTER_FRIENDS) || m_pServerBrowser->m_ppServerlist[i]->m_Info.m_FriendState != IFriends::FRIEND_NO)
|
2011-11-17 19:21:59 +00:00
|
|
|
{
|
2011-06-26 15:10:13 +00:00
|
|
|
m_pSortedServerlist[m_NumSortedServers++] = i;
|
2011-11-17 19:21:59 +00:00
|
|
|
m_NumPlayers += m_pServerBrowser->m_ppServerlist[i]->m_Info.m_NumPlayers;
|
|
|
|
}
|
2011-06-26 15:10:13 +00:00
|
|
|
}
|
2010-05-29 07:25:38 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-11-10 19:18:05 +00:00
|
|
|
int CServerBrowser::CServerFilter::SortHash() const
|
2010-05-29 07:25:38 +00:00
|
|
|
{
|
|
|
|
int i = g_Config.m_BrSort&0xf;
|
2011-11-12 15:09:24 +00:00
|
|
|
i |= g_Config.m_BrSortOrder<<4;
|
|
|
|
if(m_SortHash&FILTER_EMPTY) i |= 1<<5;
|
|
|
|
if(m_SortHash&FILTER_FULL) i |= 1<<6;
|
|
|
|
if(m_SortHash&FILTER_SPECTATORS) i |= 1<<7;
|
|
|
|
if(m_SortHash&FILTER_FRIENDS) i |= 1<<8;
|
|
|
|
if(m_SortHash&FILTER_PW) i |= 1<<9;
|
|
|
|
if(m_SortHash&FILTER_FAVORITE) i |= 1<<10;
|
|
|
|
if(m_SortHash&FILTER_COMPAT_VERSION) i |= 1<<11;
|
|
|
|
if(m_SortHash&FILTER_PURE) i |= 1<<12;
|
|
|
|
if(m_SortHash&FILTER_PURE_MAP) i |= 1<<13;
|
|
|
|
if(m_SortHash&FILTER_GAMETYPE_STRICT) i |= 1<<14;
|
|
|
|
if(m_SortHash&FILTER_COUNTRY) i |= 1<<15;
|
|
|
|
if(m_SortHash&FILTER_PING) i |= 1<<16;
|
2010-05-29 07:25:38 +00:00
|
|
|
return i;
|
|
|
|
}
|
|
|
|
|
2011-11-10 19:18:05 +00:00
|
|
|
void CServerBrowser::CServerFilter::Sort()
|
2010-05-29 07:25:38 +00:00
|
|
|
{
|
|
|
|
// create filtered list
|
|
|
|
Filter();
|
|
|
|
|
|
|
|
// sort
|
2012-10-07 21:56:37 +00:00
|
|
|
switch(g_Config.m_BrSort)
|
|
|
|
{
|
|
|
|
case IServerBrowser::SORT_NAME:
|
2011-11-10 19:18:05 +00:00
|
|
|
std::stable_sort(m_pSortedServerlist, m_pSortedServerlist+m_NumSortedServers, SortWrap(this, &CServerBrowser::CServerFilter::SortCompareName));
|
2012-10-07 21:56:37 +00:00
|
|
|
break;
|
|
|
|
case IServerBrowser::SORT_PING:
|
2011-11-10 19:18:05 +00:00
|
|
|
std::stable_sort(m_pSortedServerlist, m_pSortedServerlist+m_NumSortedServers, SortWrap(this, &CServerBrowser::CServerFilter::SortComparePing));
|
2012-10-07 21:56:37 +00:00
|
|
|
break;
|
|
|
|
case IServerBrowser::SORT_MAP:
|
2011-11-10 19:18:05 +00:00
|
|
|
std::stable_sort(m_pSortedServerlist, m_pSortedServerlist+m_NumSortedServers, SortWrap(this, &CServerBrowser::CServerFilter::SortCompareMap));
|
2012-10-07 21:56:37 +00:00
|
|
|
break;
|
|
|
|
case IServerBrowser::SORT_NUMPLAYERS:
|
2011-11-03 03:17:04 +00:00
|
|
|
std::stable_sort(m_pSortedServerlist, m_pSortedServerlist+m_NumSortedServers, SortWrap(this,
|
2011-11-10 19:18:05 +00:00
|
|
|
g_Config.m_BrFilterSpectators ? &CServerBrowser::CServerFilter::SortCompareNumPlayers : &CServerBrowser::CServerFilter::SortCompareNumClients));
|
2012-10-07 21:56:37 +00:00
|
|
|
break;
|
|
|
|
case IServerBrowser::SORT_GAMETYPE:
|
2011-11-10 19:18:05 +00:00
|
|
|
std::stable_sort(m_pSortedServerlist, m_pSortedServerlist+m_NumSortedServers, SortWrap(this, &CServerBrowser::CServerFilter::SortCompareGametype));
|
2012-10-07 21:56:37 +00:00
|
|
|
}
|
2010-05-29 07:25:38 +00:00
|
|
|
|
|
|
|
// set indexes
|
2011-11-12 15:09:24 +00:00
|
|
|
/*for(i = 0; i < m_NumSortedServers; i++)
|
|
|
|
m_ppServerlist[m_pSortedServerlist[i]]->m_Info.m_SortedIndex = i;*/
|
2010-05-29 07:25:38 +00:00
|
|
|
|
2011-11-10 19:18:05 +00:00
|
|
|
m_SortHash = SortHash();
|
2010-05-29 07:25:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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--;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-10-07 21:56:37 +00:00
|
|
|
inline int AddrHash(const NETADDR *pAddr)
|
|
|
|
{
|
|
|
|
if(pAddr->type==NETTYPE_IPV4)
|
|
|
|
return (pAddr->ip[0]+pAddr->ip[1]+pAddr->ip[2]+pAddr->ip[3])&0xFF;
|
|
|
|
else
|
|
|
|
return (pAddr->ip[0]+pAddr->ip[1]+pAddr->ip[2]+pAddr->ip[3]+pAddr->ip[4]+pAddr->ip[5]+pAddr->ip[6]+pAddr->ip[7]+
|
|
|
|
pAddr->ip[8]+pAddr->ip[9]+pAddr->ip[10]+pAddr->ip[11]+pAddr->ip[12]+pAddr->ip[13]+pAddr->ip[14]+pAddr->ip[15])&0xFF;
|
|
|
|
}
|
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
CServerBrowser::CServerEntry *CServerBrowser::Find(const NETADDR &Addr)
|
|
|
|
{
|
2012-10-07 21:56:37 +00:00
|
|
|
CServerEntry *pEntry = m_aServerlistIp[AddrHash(&Addr)];
|
2010-05-29 07:25:38 +00:00
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
m_NumRequests++;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CServerBrowser::SetInfo(CServerEntry *pEntry, const CServerInfo &Info)
|
|
|
|
{
|
|
|
|
int Fav = pEntry->m_Info.m_Favorite;
|
|
|
|
pEntry->m_Info = Info;
|
2012-07-07 16:35:08 +00:00
|
|
|
pEntry->m_Info.m_Flags &= FLAG_PASSWORD;
|
|
|
|
if(str_comp(pEntry->m_Info.m_aGameType, "DM") == 0 || str_comp(pEntry->m_Info.m_aGameType, "TDM") == 0 || str_comp(pEntry->m_Info.m_aGameType, "CTF") == 0 ||
|
|
|
|
str_comp(pEntry->m_Info.m_aGameType, "SUR") == 0 || str_comp(pEntry->m_Info.m_aGameType, "LMS") == 0)
|
|
|
|
pEntry->m_Info.m_Flags |= FLAG_PURE;
|
|
|
|
if(str_comp(pEntry->m_Info.m_aMap, "dm1") == 0 || str_comp(pEntry->m_Info.m_aMap, "dm2") == 0 || str_comp(pEntry->m_Info.m_aMap, "dm6") == 0 ||
|
|
|
|
str_comp(pEntry->m_Info.m_aMap, "dm7") == 0 || str_comp(pEntry->m_Info.m_aMap, "dm8") == 0 || str_comp(pEntry->m_Info.m_aMap, "dm9") == 0 ||
|
|
|
|
str_comp(pEntry->m_Info.m_aMap, "ctf1") == 0 || str_comp(pEntry->m_Info.m_aMap, "ctf2") == 0 || str_comp(pEntry->m_Info.m_aMap, "ctf3") == 0 ||
|
|
|
|
str_comp(pEntry->m_Info.m_aMap, "ctf4") == 0 || str_comp(pEntry->m_Info.m_aMap, "ctf5") == 0 || str_comp(pEntry->m_Info.m_aMap, "ctf6") == 0 ||
|
|
|
|
str_comp(pEntry->m_Info.m_aMap, "ctf7") == 0)
|
|
|
|
pEntry->m_Info.m_Flags |= FLAG_PUREMAP;
|
2010-05-29 07:25:38 +00:00
|
|
|
pEntry->m_Info.m_Favorite = Fav;
|
|
|
|
pEntry->m_Info.m_NetAddr = pEntry->m_Addr;
|
2011-11-17 19:21:59 +00:00
|
|
|
|
|
|
|
m_NumPlayers += pEntry->m_Info.m_NumPlayers;
|
|
|
|
|
2015-03-17 09:02:19 +00:00
|
|
|
pEntry->m_InfoState = CServerEntry::STATE_READY;
|
2010-05-29 07:25:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
CServerBrowser::CServerEntry *CServerBrowser::Add(const NETADDR &Addr)
|
|
|
|
{
|
|
|
|
// create new pEntry
|
2012-10-07 21:56:37 +00:00
|
|
|
CServerEntry *pEntry = (CServerEntry *)m_ServerlistHeap.Allocate(sizeof(CServerEntry));
|
2010-05-29 07:25:38 +00:00
|
|
|
mem_zero(pEntry, sizeof(CServerEntry));
|
|
|
|
|
|
|
|
// set the info
|
|
|
|
pEntry->m_Addr = Addr;
|
2015-03-17 09:02:19 +00:00
|
|
|
pEntry->m_InfoState = CServerEntry::STATE_INVALID;
|
2015-08-30 09:21:31 +00:00
|
|
|
pEntry->m_CurrentToken = rand();
|
2010-05-29 07:25:38 +00:00
|
|
|
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);
|
2011-03-30 10:08:33 +00:00
|
|
|
str_copy(pEntry->m_Info.m_aName, pEntry->m_Info.m_aAddress, sizeof(pEntry->m_Info.m_aName));
|
2012-10-07 21:56:37 +00:00
|
|
|
str_copy(pEntry->m_Info.m_aHostname, pEntry->m_Info.m_aAddress, sizeof(pEntry->m_Info.m_aHostname));
|
2010-05-29 07:25:38 +00:00
|
|
|
|
|
|
|
// check if it's a favorite
|
2012-10-07 21:56:37 +00:00
|
|
|
for(int i = 0; i < m_NumFavoriteServers; i++)
|
2010-05-29 07:25:38 +00:00
|
|
|
{
|
2012-10-07 21:56:37 +00:00
|
|
|
if(m_aFavoriteServers[i].m_State >= FAVSTATE_ADDR && net_addr_comp(&Addr, &m_aFavoriteServers[i].m_Addr) == 0)
|
2010-05-29 07:25:38 +00:00
|
|
|
pEntry->m_Info.m_Favorite = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// add to the hash list
|
2012-10-07 21:56:37 +00:00
|
|
|
int Hash = AddrHash(&Addr);
|
2010-05-29 07:25:38 +00:00
|
|
|
pEntry->m_pNextIp = m_aServerlistIp[Hash];
|
|
|
|
m_aServerlistIp[Hash] = pEntry;
|
|
|
|
|
2011-11-12 15:09:24 +00:00
|
|
|
if(m_NumServers == m_NumServerCapacity)
|
2010-05-29 07:25:38 +00:00
|
|
|
{
|
2012-10-07 21:56:37 +00:00
|
|
|
if(m_NumServerCapacity == 0)
|
|
|
|
{
|
|
|
|
// alloc start size
|
|
|
|
m_NumServerCapacity = 1000;
|
|
|
|
m_ppServerlist = (CServerEntry **)mem_alloc(m_NumServerCapacity*sizeof(CServerEntry*), 1);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// increase size
|
|
|
|
m_NumServerCapacity += 100;
|
|
|
|
CServerEntry **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;
|
|
|
|
}
|
2011-11-10 19:18:05 +00:00
|
|
|
}
|
2011-11-12 15:09:24 +00:00
|
|
|
|
|
|
|
// add to list
|
|
|
|
m_ppServerlist[m_NumServers] = pEntry;
|
2010-05-29 07:25:38 +00:00
|
|
|
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;
|
2012-10-07 21:56:37 +00:00
|
|
|
if(Type == SET_MASTER_ADD)
|
2010-05-29 07:25:38 +00:00
|
|
|
{
|
|
|
|
if(m_ServerlistType != IServerBrowser::TYPE_INTERNET)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if(!Find(Addr))
|
|
|
|
{
|
|
|
|
pEntry = Add(Addr);
|
|
|
|
QueueRequest(pEntry);
|
|
|
|
}
|
|
|
|
}
|
2012-10-07 21:56:37 +00:00
|
|
|
/*else if(Type == SET_FAV_ADD)
|
2010-05-29 07:25:38 +00:00
|
|
|
{
|
|
|
|
if(m_ServerlistType != IServerBrowser::TYPE_FAVORITES)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if(!Find(Addr))
|
|
|
|
{
|
|
|
|
pEntry = Add(Addr);
|
|
|
|
QueueRequest(pEntry);
|
|
|
|
}
|
2011-11-12 15:09:24 +00:00
|
|
|
}*/
|
2012-10-07 21:56:37 +00:00
|
|
|
else if(Type == SET_TOKEN)
|
2010-05-29 07:25:38 +00:00
|
|
|
{
|
2015-03-17 09:02:19 +00:00
|
|
|
if(m_ServerlistType == IServerBrowser::TYPE_LAN && Token != m_CurrentLanToken)
|
2010-05-29 07:25:38 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
pEntry = Find(Addr);
|
2015-03-17 09:02:19 +00:00
|
|
|
if(!pEntry && m_ServerlistType == IServerBrowser::TYPE_LAN)
|
2010-05-29 07:25:38 +00:00
|
|
|
pEntry = Add(Addr);
|
2015-03-18 17:58:33 +00:00
|
|
|
if(pEntry && ((pEntry->m_InfoState == CServerEntry::STATE_PENDING && Token == pEntry->m_CurrentToken) || m_ServerlistType == IServerBrowser::TYPE_LAN))
|
2010-05-29 07:25:38 +00:00
|
|
|
{
|
|
|
|
SetInfo(pEntry, *pInfo);
|
|
|
|
if(m_ServerlistType == IServerBrowser::TYPE_LAN)
|
2011-01-19 14:54:50 +00:00
|
|
|
pEntry->m_Info.m_Latency = min(static_cast<int>((time_get()-m_BroadcastTime)*1000/time_freq()), 999);
|
2010-05-29 07:25:38 +00:00
|
|
|
else
|
2011-01-19 14:54:50 +00:00
|
|
|
pEntry->m_Info.m_Latency = min(static_cast<int>((time_get()-pEntry->m_RequestTime)*1000/time_freq()), 999);
|
2010-05-29 07:25:38 +00:00
|
|
|
RemoveRequest(pEntry);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-11-10 19:18:05 +00:00
|
|
|
for(int i = 0; i < m_lFilters.size(); i++)
|
|
|
|
m_lFilters[i].Sort();
|
2010-05-29 07:25:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void CServerBrowser::Refresh(int Type)
|
|
|
|
{
|
|
|
|
// clear out everything
|
|
|
|
m_ServerlistHeap.Reset();
|
|
|
|
m_NumServers = 0;
|
2011-11-17 19:21:59 +00:00
|
|
|
m_NumPlayers = 0;
|
2011-11-14 19:18:50 +00:00
|
|
|
for(int i = 0; i < m_lFilters.size(); i++)
|
2011-11-17 19:21:59 +00:00
|
|
|
{
|
2011-11-14 19:18:50 +00:00
|
|
|
m_lFilters[i].m_NumSortedServers = 0;
|
2011-11-17 19:21:59 +00:00
|
|
|
m_lFilters[i].m_NumPlayers = 0;
|
|
|
|
}
|
2010-05-29 07:25:38 +00:00
|
|
|
mem_zero(m_aServerlistIp, sizeof(m_aServerlistIp));
|
|
|
|
m_pFirstReqServer = 0;
|
|
|
|
m_pLastReqServer = 0;
|
|
|
|
m_NumRequests = 0;
|
|
|
|
|
|
|
|
// next token
|
2015-03-17 09:02:19 +00:00
|
|
|
m_CurrentLanToken = (m_CurrentLanToken+1)&0xff;
|
2010-05-29 07:25:38 +00:00
|
|
|
|
|
|
|
//
|
|
|
|
m_ServerlistType = Type;
|
|
|
|
|
|
|
|
if(Type == IServerBrowser::TYPE_LAN)
|
|
|
|
{
|
2015-08-30 09:21:31 +00:00
|
|
|
CPacker Packer;
|
|
|
|
Packer.Reset();
|
|
|
|
Packer.AddRaw(SERVERBROWSE_GETINFO, sizeof(SERVERBROWSE_GETINFO));
|
|
|
|
Packer.AddInt(m_CurrentLanToken);
|
2010-05-29 07:25:38 +00:00
|
|
|
|
2011-03-28 18:11:28 +00:00
|
|
|
/* do the broadcast version */
|
2015-08-30 09:21:31 +00:00
|
|
|
CNetChunk Packet;
|
2010-05-29 07:25:38 +00:00
|
|
|
mem_zero(&Packet, sizeof(Packet));
|
2015-08-30 09:21:31 +00:00
|
|
|
Packet.m_ClientID = -1;
|
2012-03-04 11:47:16 +00:00
|
|
|
Packet.m_Address.type = m_pNetClient->NetType()|NETTYPE_LINK_BROADCAST;
|
2010-05-29 07:25:38 +00:00
|
|
|
Packet.m_Flags = NETSENDFLAG_CONNLESS;
|
2015-08-30 09:21:31 +00:00
|
|
|
Packet.m_DataSize = Packer.Size();
|
|
|
|
Packet.m_pData = Packer.Data();
|
2010-05-29 07:25:38 +00:00
|
|
|
m_BroadcastTime = time_get();
|
|
|
|
|
2015-08-30 09:21:31 +00:00
|
|
|
for(int i = 8303; i <= 8310; i++)
|
2010-05-29 07:25:38 +00:00
|
|
|
{
|
|
|
|
Packet.m_Address.port = i;
|
|
|
|
m_pNetClient->Send(&Packet);
|
|
|
|
}
|
|
|
|
|
|
|
|
if(g_Config.m_Debug)
|
2010-08-17 22:06:00 +00:00
|
|
|
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;
|
2011-11-10 19:18:05 +00:00
|
|
|
/*else if(Type == IServerBrowser::TYPE_FAVORITES)
|
2010-05-29 07:25:38 +00:00
|
|
|
{
|
|
|
|
for(int i = 0; i < m_NumFavoriteServers; i++)
|
2012-10-07 21:56:37 +00:00
|
|
|
if(m_aFavoriteServers[i].m_State >= FAVSTATE_ADDR)
|
|
|
|
Set(m_aFavoriteServers[i].m_Addr, SET_FAV_ADD, -1, 0);
|
2011-11-10 19:18:05 +00:00
|
|
|
}*/
|
2010-05-29 07:25:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void CServerBrowser::RequestImpl(const NETADDR &Addr, CServerEntry *pEntry) const
|
|
|
|
{
|
|
|
|
if(g_Config.m_Debug)
|
|
|
|
{
|
2011-03-30 10:08:33 +00:00
|
|
|
char aAddrStr[NETADDR_MAXSTRSIZE];
|
2011-12-29 22:36:53 +00:00
|
|
|
net_addr_str(&Addr, aAddrStr, sizeof(aAddrStr), true);
|
2010-08-17 22:06:00 +00:00
|
|
|
char aBuf[256];
|
2011-03-30 10:08:33 +00:00
|
|
|
str_format(aBuf, sizeof(aBuf),"requesting server info from %s", aAddrStr);
|
2010-08-17 22:06:00 +00:00
|
|
|
m_pConsole->Print(IConsole::OUTPUT_LEVEL_DEBUG, "client_srvbrowse", aBuf);
|
2010-05-29 07:25:38 +00:00
|
|
|
}
|
|
|
|
|
2015-08-30 09:21:31 +00:00
|
|
|
CPacker Packer;
|
|
|
|
Packer.Reset();
|
|
|
|
Packer.AddRaw(SERVERBROWSE_GETINFO, sizeof(SERVERBROWSE_GETINFO));
|
|
|
|
Packer.AddInt(pEntry ? pEntry->m_CurrentToken : m_CurrentLanToken);
|
|
|
|
|
|
|
|
CNetChunk Packet;
|
2010-05-29 07:25:38 +00:00
|
|
|
Packet.m_ClientID = -1;
|
|
|
|
Packet.m_Address = Addr;
|
|
|
|
Packet.m_Flags = NETSENDFLAG_CONNLESS;
|
2015-08-30 09:21:31 +00:00
|
|
|
Packet.m_DataSize = Packer.Size();
|
|
|
|
Packet.m_pData = Packer.Data();
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
m_pNetClient->Send(&Packet);
|
|
|
|
|
|
|
|
if(pEntry)
|
2015-03-17 09:02:19 +00:00
|
|
|
{
|
2010-05-29 07:25:38 +00:00
|
|
|
pEntry->m_RequestTime = time_get();
|
2015-03-17 09:02:19 +00:00
|
|
|
pEntry->m_InfoState = CServerEntry::STATE_PENDING;
|
|
|
|
}
|
2010-05-29 07:25:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void CServerBrowser::Request(const NETADDR &Addr) const
|
|
|
|
{
|
|
|
|
RequestImpl(Addr, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-03-18 18:03:13 +00:00
|
|
|
void CServerBrowser::Update(bool ForceResort)
|
2010-05-29 07:25:38 +00:00
|
|
|
{
|
2011-03-30 10:08:33 +00:00
|
|
|
int64 Timeout = time_freq();
|
2010-05-29 07:25:38 +00:00
|
|
|
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;
|
|
|
|
|
|
|
|
m_NeedRefresh = 0;
|
|
|
|
|
|
|
|
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(i = 0; i < IMasterServer::MAX_MASTERSERVERS; i++)
|
|
|
|
{
|
2011-03-30 10:08:33 +00:00
|
|
|
if(!m_pMasterServer->IsValid(i))
|
2010-05-29 07:25:38 +00:00
|
|
|
continue;
|
|
|
|
|
2011-03-30 10:08:33 +00:00
|
|
|
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)
|
2010-08-17 22:06:00 +00:00
|
|
|
m_pConsole->Print(IConsole::OUTPUT_LEVEL_DEBUG, "client_srvbrowse", "requesting server list");
|
2010-05-29 07:25:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// do timeouts
|
|
|
|
pEntry = m_pFirstReqServer;
|
|
|
|
while(1)
|
|
|
|
{
|
|
|
|
if(!pEntry) // no more entries
|
|
|
|
break;
|
|
|
|
|
|
|
|
pNext = pEntry->m_pNextReq;
|
|
|
|
|
|
|
|
if(pEntry->m_RequestTime && pEntry->m_RequestTime+Timeout < Now)
|
|
|
|
{
|
|
|
|
// timeout
|
|
|
|
RemoveRequest(pEntry);
|
|
|
|
}
|
|
|
|
|
|
|
|
pEntry = pNext;
|
|
|
|
}
|
|
|
|
|
|
|
|
// do timeouts
|
|
|
|
pEntry = m_pFirstReqServer;
|
|
|
|
Count = 0;
|
|
|
|
while(1)
|
|
|
|
{
|
|
|
|
if(!pEntry) // no more entries
|
|
|
|
break;
|
|
|
|
|
|
|
|
// no more then 10 concurrent requests
|
|
|
|
if(Count == g_Config.m_BrMaxRequests)
|
|
|
|
break;
|
|
|
|
|
|
|
|
if(pEntry->m_RequestTime == 0)
|
|
|
|
RequestImpl(pEntry->m_Addr, pEntry);
|
|
|
|
|
|
|
|
Count++;
|
|
|
|
pEntry = pEntry->m_pNextReq;
|
|
|
|
}
|
|
|
|
|
2012-10-07 21:56:37 +00:00
|
|
|
// update favorites
|
|
|
|
UpdateFavorites();
|
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
// check if we need to resort
|
2011-11-10 19:18:05 +00:00
|
|
|
for(int i = 0; i < m_lFilters.size(); i++)
|
|
|
|
{
|
|
|
|
CServerFilter *pFilter = &m_lFilters[i];
|
|
|
|
if(pFilter->m_SortHash != pFilter->SortHash() || ForceResort)
|
|
|
|
pFilter->Sort();
|
|
|
|
}
|
2010-05-29 07:25:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-10-07 21:56:37 +00:00
|
|
|
void CServerBrowser::UpdateFavorites()
|
|
|
|
{
|
|
|
|
// check if hostname lookup for favourites is done
|
|
|
|
if(m_FavLookup.m_Active && m_FavLookup.m_HostLookup.m_Job.Status() == CJob::STATE_DONE)
|
|
|
|
{
|
|
|
|
// check if favourite has not been removed in the meanwhile
|
|
|
|
if(m_FavLookup.m_FavoriteIndex != -1)
|
|
|
|
{
|
|
|
|
if(m_FavLookup.m_HostLookup.m_Job.Result() == 0)
|
|
|
|
{
|
|
|
|
CFavoriteServer *pEntry = FindFavoriteByAddr(m_FavLookup.m_HostLookup.m_Addr, 0);
|
|
|
|
if(pEntry)
|
|
|
|
{
|
|
|
|
// address is already in the list -> acquire hostname if existing entry lacks it and drop multiple address entry
|
|
|
|
if(pEntry->m_State != FAVSTATE_HOST)
|
|
|
|
{
|
|
|
|
str_copy(pEntry->m_aHostname, m_aFavoriteServers[m_FavLookup.m_FavoriteIndex].m_aHostname, sizeof(pEntry->m_aHostname));
|
|
|
|
pEntry->m_State = FAVSTATE_HOST;
|
|
|
|
dbg_msg("test", "fav aquired hostname, %s", m_aFavoriteServers[m_FavLookup.m_FavoriteIndex].m_aHostname);
|
|
|
|
}
|
|
|
|
RemoveFavoriteEntry(m_FavLookup.m_FavoriteIndex);
|
|
|
|
dbg_msg("test", "fav removed multiple entry");
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// address wasn't in the list yet -> add it (optional check if hostname matches given address -> drop entry on fail)
|
|
|
|
if(m_aFavoriteServers[m_FavLookup.m_FavoriteIndex].m_State == FAVSTATE_LOOKUP ||
|
|
|
|
net_addr_comp(&m_aFavoriteServers[m_NumFavoriteServers].m_Addr, &m_FavLookup.m_HostLookup.m_Addr) == 0)
|
|
|
|
{
|
|
|
|
m_aFavoriteServers[m_FavLookup.m_FavoriteIndex].m_Addr = m_FavLookup.m_HostLookup.m_Addr;
|
|
|
|
m_aFavoriteServers[m_FavLookup.m_FavoriteIndex].m_State = FAVSTATE_HOST;
|
|
|
|
CServerEntry *pEntry = Find(m_aFavoriteServers[m_FavLookup.m_FavoriteIndex].m_Addr);
|
|
|
|
if(pEntry)
|
|
|
|
pEntry->m_Info.m_Favorite = 1;
|
|
|
|
dbg_msg("test", "fav added, %s", m_aFavoriteServers[m_FavLookup.m_FavoriteIndex].m_aHostname);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
RemoveFavoriteEntry(m_FavLookup.m_FavoriteIndex);
|
|
|
|
dbg_msg("test", "fav removed entry that failed hostname-address check");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// hostname lookup failed
|
|
|
|
if(m_aFavoriteServers[m_FavLookup.m_FavoriteIndex].m_State == FAVSTATE_LOOKUP)
|
|
|
|
{
|
|
|
|
m_aFavoriteServers[m_FavLookup.m_FavoriteIndex].m_State = FAVSTATE_INVALID;
|
|
|
|
dbg_msg("test", "fav invalid, %s", m_aFavoriteServers[m_FavLookup.m_FavoriteIndex].m_aHostname);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
RemoveFavoriteEntry(m_FavLookup.m_FavoriteIndex);
|
|
|
|
dbg_msg("test", "fav removed invalid check-based entry");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
m_FavLookup.m_Active = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// add hostname lookup for favourites
|
|
|
|
if(m_FavLookup.m_LookupCount > 0 && !m_FavLookup.m_Active)
|
|
|
|
{
|
|
|
|
for(int i = 0; i < m_NumFavoriteServers; i++)
|
|
|
|
{
|
|
|
|
if(m_aFavoriteServers[i].m_State <= FAVSTATE_LOOKUPCHECK)
|
|
|
|
{
|
|
|
|
m_pEngine->HostLookup(&m_FavLookup.m_HostLookup, m_aFavoriteServers[i].m_aHostname, m_pNetClient->NetType());
|
|
|
|
m_FavLookup.m_FavoriteIndex = i;
|
|
|
|
--m_FavLookup.m_LookupCount;
|
|
|
|
m_FavLookup.m_Active = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
CServerBrowser::CFavoriteServer *CServerBrowser::FindFavoriteByAddr(const NETADDR &Addr, int *Index)
|
|
|
|
{
|
|
|
|
for(int i = 0; i < m_NumFavoriteServers; i++)
|
|
|
|
{
|
|
|
|
if(net_addr_comp(&Addr, &m_aFavoriteServers[i].m_Addr) == 0)
|
|
|
|
{
|
|
|
|
if(Index)
|
|
|
|
*Index = i;
|
|
|
|
return &m_aFavoriteServers[i];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
CServerBrowser::CFavoriteServer *CServerBrowser::FindFavoriteByHostname(const char *pHostname, int *Index)
|
2010-05-29 07:25:38 +00:00
|
|
|
{
|
2012-10-07 21:56:37 +00:00
|
|
|
for(int i = 0; i < m_NumFavoriteServers; i++)
|
2010-05-29 07:25:38 +00:00
|
|
|
{
|
2012-10-07 21:56:37 +00:00
|
|
|
if(str_comp(pHostname, m_aFavoriteServers[i].m_aHostname) == 0)
|
|
|
|
{
|
|
|
|
if(Index)
|
|
|
|
*Index = i;
|
|
|
|
return &m_aFavoriteServers[i];
|
|
|
|
}
|
2010-05-29 07:25:38 +00:00
|
|
|
}
|
2012-10-07 21:56:37 +00:00
|
|
|
|
|
|
|
return 0;
|
2010-05-29 07:25:38 +00:00
|
|
|
}
|
|
|
|
|
2012-10-07 21:56:37 +00:00
|
|
|
void CServerBrowser::RemoveFavoriteEntry(int Index)
|
2010-05-29 07:25:38 +00:00
|
|
|
{
|
2012-10-07 21:56:37 +00:00
|
|
|
mem_move(&m_aFavoriteServers[Index], &m_aFavoriteServers[Index+1], sizeof(CFavoriteServer)*(m_NumFavoriteServers-(Index+1)));
|
|
|
|
m_NumFavoriteServers--;
|
|
|
|
}
|
2010-05-29 07:25:38 +00:00
|
|
|
|
2012-10-07 21:56:37 +00:00
|
|
|
void CServerBrowser::AddFavoriteEx(const char *pHostname, const NETADDR *pAddr, bool DoCheck)
|
|
|
|
{
|
|
|
|
if(m_NumFavoriteServers == MAX_FAVORITES || FindFavoriteByHostname(pHostname, 0))
|
2010-05-29 07:25:38 +00:00
|
|
|
return;
|
|
|
|
|
2012-10-07 21:56:37 +00:00
|
|
|
// check if hostname is a net address string
|
|
|
|
if(net_addr_from_str(&m_aFavoriteServers[m_NumFavoriteServers].m_Addr, pHostname) == 0)
|
2010-05-29 07:25:38 +00:00
|
|
|
{
|
2012-10-07 21:56:37 +00:00
|
|
|
// make sure that we don't already have the server in our list
|
|
|
|
if(FindFavoriteByAddr(m_aFavoriteServers[m_NumFavoriteServers].m_Addr, 0) != 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// check if hostname does not match given address
|
|
|
|
if(DoCheck && net_addr_comp(&m_aFavoriteServers[m_NumFavoriteServers].m_Addr, pAddr) != 0)
|
2010-05-29 07:25:38 +00:00
|
|
|
return;
|
2012-10-07 21:56:37 +00:00
|
|
|
|
|
|
|
// add the server to the list
|
|
|
|
m_aFavoriteServers[m_NumFavoriteServers].m_State = FAVSTATE_ADDR;
|
|
|
|
CServerEntry *pEntry = Find(m_aFavoriteServers[m_NumFavoriteServers].m_Addr);
|
|
|
|
if(pEntry)
|
|
|
|
pEntry->m_Info.m_Favorite = 1;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// prepare for hostname lookup
|
|
|
|
if(DoCheck)
|
|
|
|
{
|
|
|
|
m_aFavoriteServers[m_NumFavoriteServers].m_State = FAVSTATE_LOOKUPCHECK;
|
|
|
|
m_aFavoriteServers[m_NumFavoriteServers].m_Addr = *pAddr;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
m_aFavoriteServers[m_NumFavoriteServers].m_State = FAVSTATE_LOOKUP;
|
|
|
|
++m_FavLookup.m_LookupCount;
|
2010-05-29 07:25:38 +00:00
|
|
|
}
|
|
|
|
|
2012-10-07 21:56:37 +00:00
|
|
|
str_copy(m_aFavoriteServers[m_NumFavoriteServers].m_aHostname, pHostname, sizeof(m_aFavoriteServers[m_NumFavoriteServers].m_aHostname));
|
|
|
|
++m_NumFavoriteServers;
|
2010-05-29 07:25:38 +00:00
|
|
|
|
2011-04-13 18:37:12 +00:00
|
|
|
if(g_Config.m_Debug)
|
2010-08-17 22:06:00 +00:00
|
|
|
{
|
|
|
|
char aBuf[256];
|
2012-10-07 21:56:37 +00:00
|
|
|
str_format(aBuf, sizeof(aBuf), "added fav '%s' (%s)", pHostname);
|
2010-08-17 22:06:00 +00:00
|
|
|
m_pConsole->Print(IConsole::OUTPUT_LEVEL_DEBUG, "client_srvbrowse", aBuf);
|
|
|
|
}
|
2011-11-17 19:21:59 +00:00
|
|
|
|
|
|
|
// refresh servers in all filters where favorites are filtered
|
|
|
|
for(int i = 0; i < m_lFilters.size(); i++)
|
|
|
|
{
|
|
|
|
CServerFilter *pFilter = &m_lFilters[i];
|
|
|
|
if(pFilter->m_SortHash&FILTER_FAVORITE)
|
|
|
|
pFilter->Sort();
|
|
|
|
}
|
2010-05-29 07:25:38 +00:00
|
|
|
}
|
|
|
|
|
2012-10-07 21:56:37 +00:00
|
|
|
void CServerBrowser::RemoveFavoriteEx(const char *pHostname, const NETADDR *pAddr)
|
2010-05-29 07:25:38 +00:00
|
|
|
{
|
2012-10-07 21:56:37 +00:00
|
|
|
// find favorite entry
|
|
|
|
int Index = 0;
|
|
|
|
CFavoriteServer *pFavEntry = FindFavoriteByHostname(pHostname, &Index);
|
|
|
|
if(pFavEntry == 0 && pAddr)
|
|
|
|
pFavEntry = FindFavoriteByAddr(*pAddr, &Index);
|
|
|
|
if(pFavEntry)
|
2010-05-29 07:25:38 +00:00
|
|
|
{
|
2012-10-07 21:56:37 +00:00
|
|
|
if(pFavEntry->m_State >= FAVSTATE_ADDR)
|
2010-05-29 07:25:38 +00:00
|
|
|
{
|
2012-10-07 21:56:37 +00:00
|
|
|
// invalidate favorite state for server entry
|
|
|
|
CServerEntry *pEntry = Find(pFavEntry->m_Addr);
|
2010-05-29 07:25:38 +00:00
|
|
|
if(pEntry)
|
2012-10-07 21:56:37 +00:00
|
|
|
pEntry->m_Info.m_Favorite = 0;
|
|
|
|
}
|
|
|
|
else if(pFavEntry->m_State <= FAVSTATE_LOOKUPCHECK && m_FavLookup.m_FavoriteIndex == Index)
|
|
|
|
{
|
|
|
|
// skip result on favorite hostname lookup
|
|
|
|
m_FavLookup.m_FavoriteIndex = -1;
|
2010-05-29 07:25:38 +00:00
|
|
|
}
|
2012-10-07 21:56:37 +00:00
|
|
|
|
|
|
|
// remove favorite
|
|
|
|
RemoveFavoriteEntry(Index);
|
|
|
|
if(m_FavLookup.m_FavoriteIndex > Index)
|
|
|
|
--m_FavLookup.m_FavoriteIndex;
|
2010-05-29 07:25:38 +00:00
|
|
|
}
|
2011-11-17 19:21:59 +00:00
|
|
|
|
|
|
|
// refresh servers in all filters where favorites are filtered
|
|
|
|
for(int i = 0; i < m_lFilters.size(); i++)
|
|
|
|
{
|
|
|
|
CServerFilter *pFilter = &m_lFilters[i];
|
|
|
|
if(pFilter->m_SortHash&FILTER_FAVORITE)
|
|
|
|
pFilter->Sort();
|
|
|
|
}
|
2010-05-29 07:25:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-10-30 16:56:57 +00:00
|
|
|
int CServerBrowser::LoadingProgression() const
|
|
|
|
{
|
|
|
|
if(m_NumServers == 0)
|
|
|
|
return 0;
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2010-10-30 16:56:57 +00:00
|
|
|
int Servers = m_NumServers;
|
|
|
|
int Loaded = m_NumServers-m_NumRequests;
|
|
|
|
return 100.0f * Loaded/Servers;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-10-07 21:56:37 +00:00
|
|
|
void CServerBrowser::ConAddFavorite(IConsole::IResult *pResult, void *pUserData)
|
|
|
|
{
|
|
|
|
CServerBrowser *pSelf = static_cast<CServerBrowser *>(pUserData);
|
|
|
|
pSelf->AddFavoriteEx(pResult->GetString(0), 0, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
void CServerBrowser::ConRemoveFavorite(IConsole::IResult *pResult, void *pUserData)
|
|
|
|
{
|
|
|
|
CServerBrowser *pSelf = static_cast<CServerBrowser *>(pUserData);
|
|
|
|
pSelf->RemoveFavoriteEx(pResult->GetString(0), 0);
|
|
|
|
}
|
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
void CServerBrowser::ConfigSaveCallback(IConfig *pConfig, void *pUserData)
|
|
|
|
{
|
|
|
|
CServerBrowser *pSelf = (CServerBrowser *)pUserData;
|
|
|
|
|
|
|
|
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
|
|
|
{
|
2012-10-07 21:56:37 +00:00
|
|
|
str_format(aBuffer, sizeof(aBuffer), "add_favorite %s", pSelf->m_aFavoriteServers[i].m_aHostname);
|
2010-05-29 07:25:38 +00:00
|
|
|
pConfig->WriteLine(aBuffer);
|
|
|
|
}
|
|
|
|
}
|