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
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"
24 typedef bool (CServerBrowser::*SortFunc
)(int, int) const;
26 CServerBrowser
*m_pThis
;
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()
36 m_pSortedServerlist
= 0;
38 m_NumFavoriteServers
= 0;
40 mem_zero(m_aServerlistIp
, sizeof(m_aServerlistIp
));
42 m_pFirstReqServer
= 0; // request list
48 m_NumSortedServers
= 0;
49 m_NumSortedServersCapacity
= 0;
51 m_NumServerCapacity
= 0;
54 m_aFilterString
[0] = 0;
55 m_aFilterGametypeString
[0] = 0;
57 // the token is to keep server refresh separated from each other
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
>();
73 pConfig
->RegisterCallback(ConfigSaveCallback
, this);
76 const CServerInfo
*CServerBrowser::SortedGet(int Index
) const
78 if(Index
< 0 || Index
>= m_NumSortedServers
)
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()
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
++)
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))
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
))
152 else if(g_Config
.m_BrFilterPw
&& m_ppServerlist
[i
]->m_Info
.m_Flags
&SERVER_FLAG_PASSWORD
)
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))
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)
179 else if(g_Config
.m_BrFilterPing
< m_ppServerlist
[i
]->m_Info
.m_Latency
)
181 else if(g_Config
.m_BrFilterCompatversion
&& str_comp_num(m_ppServerlist
[i
]->m_Info
.m_aVersion
, m_aNetVersion
, 3) != 0)
183 else if(g_Config
.m_BrFilterServerAddress
[0] && !str_find_nocase(m_ppServerlist
[i
]->m_Info
.m_aAddress
, g_Config
.m_BrFilterServerAddress
))
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
))
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
))
191 if(g_Config
.m_BrFilterCountry
)
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
)
205 if(!Filtered
&& g_Config
.m_BrFilterString
[0] != 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
))
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
))
225 m_ppServerlist
[i
]->m_Info
.m_QuickSearchHit
|= IServerBrowser::QUICK_PLAYER
;
231 if(str_find_nocase(m_ppServerlist
[i
]->m_Info
.m_aMap
, g_Config
.m_BrFilterString
))
234 m_ppServerlist
[i
]->m_Info
.m_QuickSearchHit
|= IServerBrowser::QUICK_MAPNAME
;
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;
277 void CServerBrowser::Sort()
281 // create filtered list
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
;
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
;
324 m_pFirstReqServer
= pEntry
->m_pNextReq
;
326 if(pEntry
->m_pNextReq
)
327 pEntry
->m_pNextReq
->m_pPrevReq
= pEntry
->m_pPrevReq
;
329 m_pLastReqServer
= pEntry
->m_pPrevReq
;
331 pEntry
->m_pPrevReq
= 0;
332 pEntry
->m_pNextReq
= 0;
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)
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
;
354 m_pLastReqServer
->m_pNextReq
= pEntry
;
356 m_pFirstReqServer
= pEntry
;
357 m_pLastReqServer
= pEntry
;
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
));
379 pEntry->m_Info.latency = (time_get()-pEntry->request_time)*1000/time_freq();
380 RemoveRequest(pEntry);
383 pEntry
->m_GotInfo
= 1;
387 CServerBrowser::CServerEntry
*CServerBrowser::Add(const NETADDR
&Addr
)
389 int Hash
= Addr
.ip
[0];
390 CServerEntry
*pEntry
= 0;
394 pEntry
= (CServerEntry
*)m_ServerlistHeap
.Allocate(sizeof(CServerEntry
));
395 mem_zero(pEntry
, sizeof(CServerEntry
));
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
;
427 m_ppServerlist
[m_NumServers
] = pEntry
;
428 pEntry
->m_Info
.m_ServerIndex
= m_NumServers
;
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
)
445 QueueRequest(pEntry
);
448 else if(Type
== IServerBrowser::SET_FAV_ADD
)
450 if(m_ServerlistType
!= IServerBrowser::TYPE_FAVORITES
)
456 QueueRequest(pEntry
);
459 else if(Type
== IServerBrowser::SET_TOKEN
)
461 if(Token
!= m_CurrentToken
)
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);
473 pEntry
->m_Info
.m_Latency
= min(static_cast<int>((time_get()-pEntry
->m_RequestTime
)*1000/time_freq()), 999);
474 RemoveRequest(pEntry
);
481 void CServerBrowser::Refresh(int Type
)
483 // clear out everything
484 m_ServerlistHeap
.Reset();
486 m_NumSortedServers
= 0;
487 mem_zero(m_aServerlistIp
, sizeof(m_aServerlistIp
));
488 m_pFirstReqServer
= 0;
489 m_pLastReqServer
= 0;
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];
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
);
523 m_pConsole
->Print(IConsole::OUTPUT_LEVEL_DEBUG
, "client_srvbrowse", "broadcasting for servers");
525 else if(Type
== IServerBrowser::TYPE_INTERNET
)
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];
541 char aAddrStr
[NETADDR_MAXSTRSIZE
];
542 net_addr_str(&Addr
, aAddrStr
, sizeof(aAddrStr
));
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
);
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();
574 CServerEntry
*pEntry
, *pNext
;
576 // do server list requests
577 if(m_NeedRefresh
&& !m_pMasterServer
->IsRefreshing())
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
))
596 Addr
= m_pMasterServer
->GetAddr(i
);
597 Packet
.m_Address
= Addr
;
598 m_pNetClient
->Send(&Packet
);
602 m_pConsole
->Print(IConsole::OUTPUT_LEVEL_DEBUG
, "client_srvbrowse", "requesting server list");
606 pEntry
= m_pFirstReqServer
;
609 if(!pEntry
) // no more entries
612 pNext
= pEntry
->m_pNextReq
;
614 if(pEntry
->m_RequestTime
&& pEntry
->m_RequestTime
+Timeout
< Now
)
617 RemoveRequest(pEntry
);
624 pEntry
= m_pFirstReqServer
;
628 if(!pEntry
) // no more entries
631 // no more then 10 concurrent requests
632 if(Count
== g_Config
.m_BrMaxRequests
)
635 if(pEntry
->m_RequestTime
== 0)
636 RequestImpl(pEntry
->m_Addr
, pEntry
);
639 pEntry
= pEntry
->m_pNextReq
;
642 // check if we need to resort
643 if(m_Sorthash
!= SortHash() || ForceResort
)
648 bool CServerBrowser::IsFavorite(const NETADDR
&Addr
) const
650 // search for the address
652 for(i
= 0; i
< m_NumFavoriteServers
; i
++)
654 if(net_addr_comp(&Addr
, &m_aFavoriteServers
[i
]) == 0)
660 void CServerBrowser::AddFavorite(const NETADDR
&Addr
)
662 CServerEntry
*pEntry
;
664 if(m_NumFavoriteServers
== MAX_FAVORITES
)
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)
674 // add the server to the list
675 m_aFavoriteServers
[m_NumFavoriteServers
++] = Addr
;
678 pEntry
->m_Info
.m_Favorite
= 1;
682 char aAddrStr
[NETADDR_MAXSTRSIZE
];
683 net_addr_str(&Addr
, aAddrStr
, sizeof(aAddrStr
));
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
)
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
--;
704 pEntry
->m_Info
.m_Favorite
= 0;
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)
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
;
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
);