fixed editor zooming if gui is not active
[twcon.git] / src / engine / client / serverbrowser.cpp
blobddfc759726d9fa0a3285461dad2f326d8a543dbb
1 /* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */
2 /* If you are missing that file, acquire a complete release at teeworlds.com. */
3 #include <algorithm> // sort
5 #include <base/math.h>
6 #include <base/system.h>
8 #include <engine/shared/config.h>
9 #include <engine/shared/memheap.h>
10 #include <engine/shared/network.h>
11 #include <engine/shared/protocol.h>
13 #include <engine/config.h>
14 #include <engine/console.h>
15 #include <engine/friends.h>
16 #include <engine/masterserver.h>
18 #include <mastersrv/mastersrv.h>
20 #include "serverbrowser.h"
22 class SortWrap
24 typedef bool (CServerBrowser::*SortFunc)(int, int) const;
25 SortFunc m_pfnSort;
26 CServerBrowser *m_pThis;
27 public:
28 SortWrap(CServerBrowser *t, SortFunc f) : m_pfnSort(f), m_pThis(t) {}
29 bool operator()(int a, int b) { return (m_pThis->*m_pfnSort)(a, b); }
32 CServerBrowser::CServerBrowser()
34 m_pMasterServer = 0;
35 m_ppServerlist = 0;
36 m_pSortedServerlist = 0;
38 m_NumFavoriteServers = 0;
40 mem_zero(m_aServerlistIp, sizeof(m_aServerlistIp));
42 m_pFirstReqServer = 0; // request list
43 m_pLastReqServer = 0;
44 m_NumRequests = 0;
46 m_NeedRefresh = 0;
48 m_NumSortedServers = 0;
49 m_NumSortedServersCapacity = 0;
50 m_NumServers = 0;
51 m_NumServerCapacity = 0;
53 m_Sorthash = 0;
54 m_aFilterString[0] = 0;
55 m_aFilterGametypeString[0] = 0;
57 // the token is to keep server refresh separated from each other
58 m_CurrentToken = 1;
60 m_ServerlistType = 0;
61 m_BroadcastTime = 0;
64 void CServerBrowser::SetBaseInfo(class CNetClient *pClient, const char *pNetVersion)
66 m_pNetClient = pClient;
67 str_copy(m_aNetVersion, pNetVersion, sizeof(m_aNetVersion));
68 m_pMasterServer = Kernel()->RequestInterface<IMasterServer>();
69 m_pConsole = Kernel()->RequestInterface<IConsole>();
70 m_pFriends = Kernel()->RequestInterface<IFriends>();
71 IConfig *pConfig = Kernel()->RequestInterface<IConfig>();
72 if(pConfig)
73 pConfig->RegisterCallback(ConfigSaveCallback, this);
76 const CServerInfo *CServerBrowser::SortedGet(int Index) const
78 if(Index < 0 || Index >= m_NumSortedServers)
79 return 0;
80 return &m_ppServerlist[m_pSortedServerlist[Index]]->m_Info;
84 bool CServerBrowser::SortCompareName(int Index1, int Index2) const
86 CServerEntry *a = m_ppServerlist[Index1];
87 CServerEntry *b = m_ppServerlist[Index2];
88 // make sure empty entries are listed last
89 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 :
90 a->m_GotInfo ? true : false;
93 bool CServerBrowser::SortCompareMap(int Index1, int Index2) const
95 CServerEntry *a = m_ppServerlist[Index1];
96 CServerEntry *b = m_ppServerlist[Index2];
97 return str_comp(a->m_Info.m_aMap, b->m_Info.m_aMap) < 0;
100 bool CServerBrowser::SortComparePing(int Index1, int Index2) const
102 CServerEntry *a = m_ppServerlist[Index1];
103 CServerEntry *b = m_ppServerlist[Index2];
104 return a->m_Info.m_Latency < b->m_Info.m_Latency;
107 bool CServerBrowser::SortCompareGametype(int Index1, int Index2) const
109 CServerEntry *a = m_ppServerlist[Index1];
110 CServerEntry *b = m_ppServerlist[Index2];
111 return str_comp(a->m_Info.m_aGameType, b->m_Info.m_aGameType) < 0;
114 bool CServerBrowser::SortCompareNumPlayers(int Index1, int Index2) const
116 CServerEntry *a = m_ppServerlist[Index1];
117 CServerEntry *b = m_ppServerlist[Index2];
118 return a->m_Info.m_NumPlayers < b->m_Info.m_NumPlayers;
121 bool CServerBrowser::SortCompareNumClients(int Index1, int Index2) const
123 CServerEntry *a = m_ppServerlist[Index1];
124 CServerEntry *b = m_ppServerlist[Index2];
125 return a->m_Info.m_NumClients < b->m_Info.m_NumClients;
128 void CServerBrowser::Filter()
130 int i = 0, p = 0;
131 m_NumSortedServers = 0;
133 // allocate the sorted list
134 if(m_NumSortedServersCapacity < m_NumServers)
136 if(m_pSortedServerlist)
137 mem_free(m_pSortedServerlist);
138 m_NumSortedServersCapacity = m_NumServers;
139 m_pSortedServerlist = (int *)mem_alloc(m_NumSortedServersCapacity*sizeof(int), 1);
142 // filter the servers
143 for(i = 0; i < m_NumServers; i++)
145 int Filtered = 0;
147 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))
148 Filtered = 1;
149 else if(g_Config.m_BrFilterFull && ((g_Config.m_BrFilterSpectators && m_ppServerlist[i]->m_Info.m_NumPlayers == m_ppServerlist[i]->m_Info.m_MaxPlayers) ||
150 m_ppServerlist[i]->m_Info.m_NumClients == m_ppServerlist[i]->m_Info.m_MaxClients))
151 Filtered = 1;
152 else if(g_Config.m_BrFilterPw && m_ppServerlist[i]->m_Info.m_Flags&SERVER_FLAG_PASSWORD)
153 Filtered = 1;
154 else if(g_Config.m_BrFilterPure &&
155 (str_comp(m_ppServerlist[i]->m_Info.m_aGameType, "DM") != 0 &&
156 str_comp(m_ppServerlist[i]->m_Info.m_aGameType, "TDM") != 0 &&
157 str_comp(m_ppServerlist[i]->m_Info.m_aGameType, "CTF") != 0))
159 Filtered = 1;
161 else if(g_Config.m_BrFilterPureMap &&
162 !(str_comp(m_ppServerlist[i]->m_Info.m_aMap, "dm1") == 0 ||
163 str_comp(m_ppServerlist[i]->m_Info.m_aMap, "dm2") == 0 ||
164 str_comp(m_ppServerlist[i]->m_Info.m_aMap, "dm6") == 0 ||
165 str_comp(m_ppServerlist[i]->m_Info.m_aMap, "dm7") == 0 ||
166 str_comp(m_ppServerlist[i]->m_Info.m_aMap, "dm8") == 0 ||
167 str_comp(m_ppServerlist[i]->m_Info.m_aMap, "dm9") == 0 ||
168 str_comp(m_ppServerlist[i]->m_Info.m_aMap, "ctf1") == 0 ||
169 str_comp(m_ppServerlist[i]->m_Info.m_aMap, "ctf2") == 0 ||
170 str_comp(m_ppServerlist[i]->m_Info.m_aMap, "ctf3") == 0 ||
171 str_comp(m_ppServerlist[i]->m_Info.m_aMap, "ctf4") == 0 ||
172 str_comp(m_ppServerlist[i]->m_Info.m_aMap, "ctf5") == 0 ||
173 str_comp(m_ppServerlist[i]->m_Info.m_aMap, "ctf6") == 0 ||
174 str_comp(m_ppServerlist[i]->m_Info.m_aMap, "ctf7") == 0)
177 Filtered = 1;
179 else if(g_Config.m_BrFilterPing < m_ppServerlist[i]->m_Info.m_Latency)
180 Filtered = 1;
181 else if(g_Config.m_BrFilterCompatversion && str_comp_num(m_ppServerlist[i]->m_Info.m_aVersion, m_aNetVersion, 3) != 0)
182 Filtered = 1;
183 else if(g_Config.m_BrFilterServerAddress[0] && !str_find_nocase(m_ppServerlist[i]->m_Info.m_aAddress, g_Config.m_BrFilterServerAddress))
184 Filtered = 1;
185 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))
186 Filtered = 1;
187 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))
188 Filtered = 1;
189 else
191 if(g_Config.m_BrFilterCountry)
193 Filtered = 1;
194 // match against player country
195 for(p = 0; p < m_ppServerlist[i]->m_Info.m_NumClients; p++)
197 if(m_ppServerlist[i]->m_Info.m_aClients[p].m_Country == g_Config.m_BrFilterCountryIndex)
199 Filtered = 0;
200 break;
205 if(!Filtered && g_Config.m_BrFilterString[0] != 0)
207 int MatchFound = 0;
209 m_ppServerlist[i]->m_Info.m_QuickSearchHit = 0;
211 // match against server name
212 if(str_find_nocase(m_ppServerlist[i]->m_Info.m_aName, g_Config.m_BrFilterString))
214 MatchFound = 1;
215 m_ppServerlist[i]->m_Info.m_QuickSearchHit |= IServerBrowser::QUICK_SERVERNAME;
218 // match against players
219 for(p = 0; p < m_ppServerlist[i]->m_Info.m_NumClients; p++)
221 if(str_find_nocase(m_ppServerlist[i]->m_Info.m_aClients[p].m_aName, g_Config.m_BrFilterString) ||
222 str_find_nocase(m_ppServerlist[i]->m_Info.m_aClients[p].m_aClan, g_Config.m_BrFilterString))
224 MatchFound = 1;
225 m_ppServerlist[i]->m_Info.m_QuickSearchHit |= IServerBrowser::QUICK_PLAYER;
226 break;
230 // match against map
231 if(str_find_nocase(m_ppServerlist[i]->m_Info.m_aMap, g_Config.m_BrFilterString))
233 MatchFound = 1;
234 m_ppServerlist[i]->m_Info.m_QuickSearchHit |= IServerBrowser::QUICK_MAPNAME;
237 if(!MatchFound)
238 Filtered = 1;
242 if(Filtered == 0)
244 // check for friend
245 m_ppServerlist[i]->m_Info.m_FriendState = IFriends::FRIEND_NO;
246 for(p = 0; p < m_ppServerlist[i]->m_Info.m_NumClients; p++)
248 m_ppServerlist[i]->m_Info.m_aClients[p].m_FriendState = m_pFriends->GetFriendState(m_ppServerlist[i]->m_Info.m_aClients[p].m_aName,
249 m_ppServerlist[i]->m_Info.m_aClients[p].m_aClan);
250 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);
253 if(!g_Config.m_BrFilterFriends || m_ppServerlist[i]->m_Info.m_FriendState != IFriends::FRIEND_NO)
254 m_pSortedServerlist[m_NumSortedServers++] = i;
259 int CServerBrowser::SortHash() const
261 int i = g_Config.m_BrSort&0xf;
262 i |= g_Config.m_BrFilterEmpty<<4;
263 i |= g_Config.m_BrFilterFull<<5;
264 i |= g_Config.m_BrFilterSpectators<<6;
265 i |= g_Config.m_BrFilterFriends<<7;
266 i |= g_Config.m_BrFilterPw<<8;
267 i |= g_Config.m_BrSortOrder<<9;
268 i |= g_Config.m_BrFilterCompatversion<<10;
269 i |= g_Config.m_BrFilterPure<<11;
270 i |= g_Config.m_BrFilterPureMap<<12;
271 i |= g_Config.m_BrFilterGametypeStrict<<13;
272 i |= g_Config.m_BrFilterCountry<<14;
273 i |= g_Config.m_BrFilterPing<<15;
274 return i;
277 void CServerBrowser::Sort()
279 int i;
281 // create filtered list
282 Filter();
284 // sort
285 if(g_Config.m_BrSort == IServerBrowser::SORT_NAME)
286 std::sort(m_pSortedServerlist, m_pSortedServerlist+m_NumSortedServers, SortWrap(this, &CServerBrowser::SortCompareName));
287 else if(g_Config.m_BrSort == IServerBrowser::SORT_PING)
288 std::sort(m_pSortedServerlist, m_pSortedServerlist+m_NumSortedServers, SortWrap(this, &CServerBrowser::SortComparePing));
289 else if(g_Config.m_BrSort == IServerBrowser::SORT_MAP)
290 std::sort(m_pSortedServerlist, m_pSortedServerlist+m_NumSortedServers, SortWrap(this, &CServerBrowser::SortCompareMap));
291 else if(g_Config.m_BrSort == IServerBrowser::SORT_NUMPLAYERS)
292 std::sort(m_pSortedServerlist, m_pSortedServerlist+m_NumSortedServers, SortWrap(this,
293 g_Config.m_BrFilterSpectators ? &CServerBrowser::SortCompareNumPlayers : &CServerBrowser::SortCompareNumClients));
294 else if(g_Config.m_BrSort == IServerBrowser::SORT_GAMETYPE)
295 std::sort(m_pSortedServerlist, m_pSortedServerlist+m_NumSortedServers, SortWrap(this, &CServerBrowser::SortCompareGametype));
297 // invert the list if requested
298 if(g_Config.m_BrSortOrder)
300 for(i = 0; i < m_NumSortedServers/2; i++)
302 int Temp = m_pSortedServerlist[i];
303 m_pSortedServerlist[i] = m_pSortedServerlist[m_NumSortedServers-i-1];
304 m_pSortedServerlist[m_NumSortedServers-i-1] = Temp;
308 // set indexes
309 for(i = 0; i < m_NumSortedServers; i++)
310 m_ppServerlist[m_pSortedServerlist[i]]->m_Info.m_SortedIndex = i;
312 str_copy(m_aFilterGametypeString, g_Config.m_BrFilterGametype, sizeof(m_aFilterGametypeString));
313 str_copy(m_aFilterString, g_Config.m_BrFilterString, sizeof(m_aFilterString));
314 m_Sorthash = SortHash();
317 void CServerBrowser::RemoveRequest(CServerEntry *pEntry)
319 if(pEntry->m_pPrevReq || pEntry->m_pNextReq || m_pFirstReqServer == pEntry)
321 if(pEntry->m_pPrevReq)
322 pEntry->m_pPrevReq->m_pNextReq = pEntry->m_pNextReq;
323 else
324 m_pFirstReqServer = pEntry->m_pNextReq;
326 if(pEntry->m_pNextReq)
327 pEntry->m_pNextReq->m_pPrevReq = pEntry->m_pPrevReq;
328 else
329 m_pLastReqServer = pEntry->m_pPrevReq;
331 pEntry->m_pPrevReq = 0;
332 pEntry->m_pNextReq = 0;
333 m_NumRequests--;
337 CServerBrowser::CServerEntry *CServerBrowser::Find(const NETADDR &Addr)
339 CServerEntry *pEntry = m_aServerlistIp[Addr.ip[0]];
341 for(; pEntry; pEntry = pEntry->m_pNextIp)
343 if(net_addr_comp(&pEntry->m_Addr, &Addr) == 0)
344 return pEntry;
346 return (CServerEntry*)0;
349 void CServerBrowser::QueueRequest(CServerEntry *pEntry)
351 // add it to the list of servers that we should request info from
352 pEntry->m_pPrevReq = m_pLastReqServer;
353 if(m_pLastReqServer)
354 m_pLastReqServer->m_pNextReq = pEntry;
355 else
356 m_pFirstReqServer = pEntry;
357 m_pLastReqServer = pEntry;
359 m_NumRequests++;
362 void CServerBrowser::SetInfo(CServerEntry *pEntry, const CServerInfo &Info)
364 int Fav = pEntry->m_Info.m_Favorite;
365 pEntry->m_Info = Info;
366 pEntry->m_Info.m_Favorite = Fav;
367 pEntry->m_Info.m_NetAddr = pEntry->m_Addr;
369 // all these are just for nice compability
370 if(pEntry->m_Info.m_aGameType[0] == '0' && pEntry->m_Info.m_aGameType[1] == 0)
371 str_copy(pEntry->m_Info.m_aGameType, "DM", sizeof(pEntry->m_Info.m_aGameType));
372 else if(pEntry->m_Info.m_aGameType[0] == '1' && pEntry->m_Info.m_aGameType[1] == 0)
373 str_copy(pEntry->m_Info.m_aGameType, "TDM", sizeof(pEntry->m_Info.m_aGameType));
374 else if(pEntry->m_Info.m_aGameType[0] == '2' && pEntry->m_Info.m_aGameType[1] == 0)
375 str_copy(pEntry->m_Info.m_aGameType, "CTF", sizeof(pEntry->m_Info.m_aGameType));
377 /*if(!request)
379 pEntry->m_Info.latency = (time_get()-pEntry->request_time)*1000/time_freq();
380 RemoveRequest(pEntry);
383 pEntry->m_GotInfo = 1;
384 Sort();
387 CServerBrowser::CServerEntry *CServerBrowser::Add(const NETADDR &Addr)
389 int Hash = Addr.ip[0];
390 CServerEntry *pEntry = 0;
391 int i;
393 // create new pEntry
394 pEntry = (CServerEntry *)m_ServerlistHeap.Allocate(sizeof(CServerEntry));
395 mem_zero(pEntry, sizeof(CServerEntry));
397 // set the info
398 pEntry->m_Addr = Addr;
399 pEntry->m_Info.m_NetAddr = Addr;
401 pEntry->m_Info.m_Latency = 999;
402 net_addr_str(&Addr, pEntry->m_Info.m_aAddress, sizeof(pEntry->m_Info.m_aAddress));
403 str_copy(pEntry->m_Info.m_aName, pEntry->m_Info.m_aAddress, sizeof(pEntry->m_Info.m_aName));
405 // check if it's a favorite
406 for(i = 0; i < m_NumFavoriteServers; i++)
408 if(net_addr_comp(&Addr, &m_aFavoriteServers[i]) == 0)
409 pEntry->m_Info.m_Favorite = 1;
412 // add to the hash list
413 pEntry->m_pNextIp = m_aServerlistIp[Hash];
414 m_aServerlistIp[Hash] = pEntry;
416 if(m_NumServers == m_NumServerCapacity)
418 CServerEntry **ppNewlist;
419 m_NumServerCapacity += 100;
420 ppNewlist = (CServerEntry **)mem_alloc(m_NumServerCapacity*sizeof(CServerEntry*), 1);
421 mem_copy(ppNewlist, m_ppServerlist, m_NumServers*sizeof(CServerEntry*));
422 mem_free(m_ppServerlist);
423 m_ppServerlist = ppNewlist;
426 // add to list
427 m_ppServerlist[m_NumServers] = pEntry;
428 pEntry->m_Info.m_ServerIndex = m_NumServers;
429 m_NumServers++;
431 return pEntry;
434 void CServerBrowser::Set(const NETADDR &Addr, int Type, int Token, const CServerInfo *pInfo)
436 CServerEntry *pEntry = 0;
437 if(Type == IServerBrowser::SET_MASTER_ADD)
439 if(m_ServerlistType != IServerBrowser::TYPE_INTERNET)
440 return;
442 if(!Find(Addr))
444 pEntry = Add(Addr);
445 QueueRequest(pEntry);
448 else if(Type == IServerBrowser::SET_FAV_ADD)
450 if(m_ServerlistType != IServerBrowser::TYPE_FAVORITES)
451 return;
453 if(!Find(Addr))
455 pEntry = Add(Addr);
456 QueueRequest(pEntry);
459 else if(Type == IServerBrowser::SET_TOKEN)
461 if(Token != m_CurrentToken)
462 return;
464 pEntry = Find(Addr);
465 if(!pEntry)
466 pEntry = Add(Addr);
467 if(pEntry)
469 SetInfo(pEntry, *pInfo);
470 if(m_ServerlistType == IServerBrowser::TYPE_LAN)
471 pEntry->m_Info.m_Latency = min(static_cast<int>((time_get()-m_BroadcastTime)*1000/time_freq()), 999);
472 else
473 pEntry->m_Info.m_Latency = min(static_cast<int>((time_get()-pEntry->m_RequestTime)*1000/time_freq()), 999);
474 RemoveRequest(pEntry);
478 Sort();
481 void CServerBrowser::Refresh(int Type)
483 // clear out everything
484 m_ServerlistHeap.Reset();
485 m_NumServers = 0;
486 m_NumSortedServers = 0;
487 mem_zero(m_aServerlistIp, sizeof(m_aServerlistIp));
488 m_pFirstReqServer = 0;
489 m_pLastReqServer = 0;
490 m_NumRequests = 0;
492 // next token
493 m_CurrentToken = (m_CurrentToken+1)&0xff;
496 m_ServerlistType = Type;
498 if(Type == IServerBrowser::TYPE_LAN)
500 unsigned char Buffer[sizeof(SERVERBROWSE_GETINFO)+1];
501 CNetChunk Packet;
502 int i;
504 mem_copy(Buffer, SERVERBROWSE_GETINFO, sizeof(SERVERBROWSE_GETINFO));
505 Buffer[sizeof(SERVERBROWSE_GETINFO)] = m_CurrentToken;
507 /* do the broadcast version */
508 Packet.m_ClientID = -1;
509 mem_zero(&Packet, sizeof(Packet));
510 Packet.m_Address.type = NETTYPE_ALL|NETTYPE_LINK_BROADCAST;
511 Packet.m_Flags = NETSENDFLAG_CONNLESS;
512 Packet.m_DataSize = sizeof(Buffer);
513 Packet.m_pData = Buffer;
514 m_BroadcastTime = time_get();
516 for(i = 8303; i <= 8310; i++)
518 Packet.m_Address.port = i;
519 m_pNetClient->Send(&Packet);
522 if(g_Config.m_Debug)
523 m_pConsole->Print(IConsole::OUTPUT_LEVEL_DEBUG, "client_srvbrowse", "broadcasting for servers");
525 else if(Type == IServerBrowser::TYPE_INTERNET)
526 m_NeedRefresh = 1;
527 else if(Type == IServerBrowser::TYPE_FAVORITES)
529 for(int i = 0; i < m_NumFavoriteServers; i++)
530 Set(m_aFavoriteServers[i], IServerBrowser::SET_FAV_ADD, -1, 0);
534 void CServerBrowser::RequestImpl(const NETADDR &Addr, CServerEntry *pEntry) const
536 unsigned char Buffer[sizeof(SERVERBROWSE_GETINFO)+1];
537 CNetChunk Packet;
539 if(g_Config.m_Debug)
541 char aAddrStr[NETADDR_MAXSTRSIZE];
542 net_addr_str(&Addr, aAddrStr, sizeof(aAddrStr));
543 char aBuf[256];
544 str_format(aBuf, sizeof(aBuf),"requesting server info from %s", aAddrStr);
545 m_pConsole->Print(IConsole::OUTPUT_LEVEL_DEBUG, "client_srvbrowse", aBuf);
548 mem_copy(Buffer, SERVERBROWSE_GETINFO, sizeof(SERVERBROWSE_GETINFO));
549 Buffer[sizeof(SERVERBROWSE_GETINFO)] = m_CurrentToken;
551 Packet.m_ClientID = -1;
552 Packet.m_Address = Addr;
553 Packet.m_Flags = NETSENDFLAG_CONNLESS;
554 Packet.m_DataSize = sizeof(Buffer);
555 Packet.m_pData = Buffer;
557 m_pNetClient->Send(&Packet);
559 if(pEntry)
560 pEntry->m_RequestTime = time_get();
563 void CServerBrowser::Request(const NETADDR &Addr) const
565 RequestImpl(Addr, 0);
569 void CServerBrowser::Update(bool ForceResort)
571 int64 Timeout = time_freq();
572 int64 Now = time_get();
573 int Count;
574 CServerEntry *pEntry, *pNext;
576 // do server list requests
577 if(m_NeedRefresh && !m_pMasterServer->IsRefreshing())
579 NETADDR Addr;
580 CNetChunk Packet;
581 int i;
583 m_NeedRefresh = 0;
585 mem_zero(&Packet, sizeof(Packet));
586 Packet.m_ClientID = -1;
587 Packet.m_Flags = NETSENDFLAG_CONNLESS;
588 Packet.m_DataSize = sizeof(SERVERBROWSE_GETLIST);
589 Packet.m_pData = SERVERBROWSE_GETLIST;
591 for(i = 0; i < IMasterServer::MAX_MASTERSERVERS; i++)
593 if(!m_pMasterServer->IsValid(i))
594 continue;
596 Addr = m_pMasterServer->GetAddr(i);
597 Packet.m_Address = Addr;
598 m_pNetClient->Send(&Packet);
601 if(g_Config.m_Debug)
602 m_pConsole->Print(IConsole::OUTPUT_LEVEL_DEBUG, "client_srvbrowse", "requesting server list");
605 // do timeouts
606 pEntry = m_pFirstReqServer;
607 while(1)
609 if(!pEntry) // no more entries
610 break;
612 pNext = pEntry->m_pNextReq;
614 if(pEntry->m_RequestTime && pEntry->m_RequestTime+Timeout < Now)
616 // timeout
617 RemoveRequest(pEntry);
620 pEntry = pNext;
623 // do timeouts
624 pEntry = m_pFirstReqServer;
625 Count = 0;
626 while(1)
628 if(!pEntry) // no more entries
629 break;
631 // no more then 10 concurrent requests
632 if(Count == g_Config.m_BrMaxRequests)
633 break;
635 if(pEntry->m_RequestTime == 0)
636 RequestImpl(pEntry->m_Addr, pEntry);
638 Count++;
639 pEntry = pEntry->m_pNextReq;
642 // check if we need to resort
643 if(m_Sorthash != SortHash() || ForceResort)
644 Sort();
648 bool CServerBrowser::IsFavorite(const NETADDR &Addr) const
650 // search for the address
651 int i;
652 for(i = 0; i < m_NumFavoriteServers; i++)
654 if(net_addr_comp(&Addr, &m_aFavoriteServers[i]) == 0)
655 return true;
657 return false;
660 void CServerBrowser::AddFavorite(const NETADDR &Addr)
662 CServerEntry *pEntry;
664 if(m_NumFavoriteServers == MAX_FAVORITES)
665 return;
667 // make sure that we don't already have the server in our list
668 for(int i = 0; i < m_NumFavoriteServers; i++)
670 if(net_addr_comp(&Addr, &m_aFavoriteServers[i]) == 0)
671 return;
674 // add the server to the list
675 m_aFavoriteServers[m_NumFavoriteServers++] = Addr;
676 pEntry = Find(Addr);
677 if(pEntry)
678 pEntry->m_Info.m_Favorite = 1;
680 if(g_Config.m_Debug)
682 char aAddrStr[NETADDR_MAXSTRSIZE];
683 net_addr_str(&Addr, aAddrStr, sizeof(aAddrStr));
684 char aBuf[256];
685 str_format(aBuf, sizeof(aBuf), "added fav, %s", aAddrStr);
686 m_pConsole->Print(IConsole::OUTPUT_LEVEL_DEBUG, "client_srvbrowse", aBuf);
690 void CServerBrowser::RemoveFavorite(const NETADDR &Addr)
692 int i;
693 CServerEntry *pEntry;
695 for(i = 0; i < m_NumFavoriteServers; i++)
697 if(net_addr_comp(&Addr, &m_aFavoriteServers[i]) == 0)
699 mem_move(&m_aFavoriteServers[i], &m_aFavoriteServers[i+1], sizeof(NETADDR)*(m_NumFavoriteServers-(i+1)));
700 m_NumFavoriteServers--;
702 pEntry = Find(Addr);
703 if(pEntry)
704 pEntry->m_Info.m_Favorite = 0;
706 return;
711 bool CServerBrowser::IsRefreshing() const
713 return m_pFirstReqServer != 0;
716 bool CServerBrowser::IsRefreshingMasters() const
718 return m_pMasterServer->IsRefreshing();
722 int CServerBrowser::LoadingProgression() const
724 if(m_NumServers == 0)
725 return 0;
727 int Servers = m_NumServers;
728 int Loaded = m_NumServers-m_NumRequests;
729 return 100.0f * Loaded/Servers;
733 void CServerBrowser::ConfigSaveCallback(IConfig *pConfig, void *pUserData)
735 CServerBrowser *pSelf = (CServerBrowser *)pUserData;
737 int i;
738 char aAddrStr[128];
739 char aBuffer[256];
740 for(i = 0; i < pSelf->m_NumFavoriteServers; i++)
742 net_addr_str(&pSelf->m_aFavoriteServers[i], aAddrStr, sizeof(aAddrStr));
743 str_format(aBuffer, sizeof(aBuffer), "add_favorite %s", aAddrStr);
744 pConfig->WriteLine(aBuffer);