Allow multiple addresses per server in the serverbrowser

Support is incomplete for `leak_ip_address_to_all_servers` (will only
ping the first address of each server) and for the `leak_ip` setting
(which will also only ping the first address of each server).
This commit is contained in:
heinrich5991 2022-05-23 20:16:18 +02:00
parent 2e34cf4d4b
commit 6600024f24
25 changed files with 805 additions and 682 deletions

View file

@ -1706,6 +1706,7 @@ set_src(BASE GLOB_RECURSE src/base
system.cpp
system.h
tl/threading.h
types.h
unicode/confusables.cpp
unicode/confusables.h
unicode/confusables_data.h
@ -1723,6 +1724,7 @@ set_src(ENGINE_INTERFACE GLOB src/engine
discord.h
editor.h
engine.h
favorites.h
friends.h
ghost.h
graphics.h
@ -1963,6 +1965,7 @@ if(CLIENT)
demoedit.cpp
demoedit.h
discord.cpp
favorites.cpp
friends.cpp
friends.h
ghost.cpp

11
src/base/types.h Normal file
View file

@ -0,0 +1,11 @@
#ifndef BASE_TYPES_H
#define BASE_TYPES_H
enum class TRISTATE
{
NONE,
SOME,
ALL,
};
#endif // BASE_TYPES_H

View file

@ -22,6 +22,7 @@
#include <engine/discord.h>
#include <engine/editor.h>
#include <engine/engine.h>
#include <engine/favorites.h>
#include <engine/graphics.h>
#include <engine/input.h>
#include <engine/keys.h>
@ -1519,7 +1520,7 @@ void CClient::ProcessServerInfo(int RawType, NETADDR *pFrom, const void *pData,
{
if(!DuplicatedPacket && (!pEntry || !pEntry->m_GotInfo || SavedType >= pEntry->m_Info.m_Type))
{
m_ServerBrowser.Set(*pFrom, IServerBrowser::SET_TOKEN, Token, &Info);
m_ServerBrowser.OnServerInfoUpdate(*pFrom, Token, &Info);
}
// Player info is irrelevant for the client (while connected),
@ -1535,7 +1536,8 @@ void CClient::ProcessServerInfo(int RawType, NETADDR *pFrom, const void *pData,
if(SavedType >= m_CurrentServerInfo.m_Type)
{
mem_copy(&m_CurrentServerInfo, &Info, sizeof(m_CurrentServerInfo));
m_CurrentServerInfo.m_NetAddr = m_ServerAddress;
m_CurrentServerInfo.m_NumAddresses = 1;
m_CurrentServerInfo.m_aAddresses[0] = m_ServerAddress;
m_CurrentServerInfoRequestTime = -1;
}
@ -2954,6 +2956,7 @@ void CClient::InitInterfaces()
// fetch interfaces
m_pEngine = Kernel()->RequestInterface<IEngine>();
m_pEditor = Kernel()->RequestInterface<IEditor>();
m_pFavorites = Kernel()->RequestInterface<IFavorites>();
//m_pGraphics = Kernel()->RequestInterface<IEngineGraphics>();
m_pSound = Kernel()->RequestInterface<IEngineSound>();
m_pGameClient = Kernel()->RequestInterface<IGameClient>();
@ -2978,6 +2981,7 @@ void CClient::InitInterfaces()
m_Updater.Init();
#endif
m_pConfigManager->RegisterCallback(IFavorites::ConfigSaveCallback, m_pFavorites);
m_Friends.Init();
m_Foes.Init(true);
@ -3672,6 +3676,41 @@ void CClient::Con_RconLogin(IConsole::IResult *pResult, void *pUserData)
pSelf->RconAuth(pResult->GetString(0), pResult->GetString(1));
}
void CClient::Con_BeginFavoriteGroup(IConsole::IResult *pResult, void *pUserData)
{
CClient *pSelf = (CClient *)pUserData;
if(pSelf->m_FavoritesGroup)
{
log_error("client", "opening favorites group while there is already one, discarding old one");
for(int i = 0; i < pSelf->m_FavoritesGroupNum; i++)
{
char aAddr[NETADDR_MAXSTRSIZE];
net_addr_str(&pSelf->m_aFavoritesGroupAddresses[i], aAddr, sizeof(aAddr), true);
log_warn("client", "discarding %s", aAddr);
}
}
pSelf->m_FavoritesGroup = true;
pSelf->m_FavoritesGroupAllowPing = false;
pSelf->m_FavoritesGroupNum = 0;
}
void CClient::Con_EndFavoriteGroup(IConsole::IResult *pResult, void *pUserData)
{
CClient *pSelf = (CClient *)pUserData;
if(!pSelf->m_FavoritesGroup)
{
log_error("client", "closing favorites group while there is none, ignoring");
return;
}
log_info("client", "adding group of %d favorites", pSelf->m_FavoritesGroupNum);
pSelf->m_pFavorites->Add(pSelf->m_aFavoritesGroupAddresses, pSelf->m_FavoritesGroupNum);
if(pSelf->m_FavoritesGroupAllowPing)
{
pSelf->m_pFavorites->AllowPing(pSelf->m_aFavoritesGroupAddresses, pSelf->m_FavoritesGroupNum, true);
}
pSelf->m_FavoritesGroup = false;
}
void CClient::Con_AddFavorite(IConsole::IResult *pResult, void *pUserData)
{
CClient *pSelf = (CClient *)pUserData;
@ -3683,10 +3722,29 @@ void CClient::Con_AddFavorite(IConsole::IResult *pResult, void *pUserData)
pSelf->m_pConsole->Print(IConsole::OUTPUT_LEVEL_STANDARD, "client", aBuf);
return;
}
pSelf->m_ServerBrowser.AddFavorite(Addr);
if(pResult->NumArguments() > 1 && str_find(pResult->GetString(1), "allow_ping"))
bool AllowPing = pResult->NumArguments() > 1 && str_find(pResult->GetString(1), "allow_ping");
char aAddr[NETADDR_MAXSTRSIZE];
net_addr_str(&Addr, aAddr, sizeof(aAddr), true);
if(pSelf->m_FavoritesGroup)
{
pSelf->m_ServerBrowser.FavoriteAllowPing(Addr, true);
if(pSelf->m_FavoritesGroupNum == (int)std::size(pSelf->m_aFavoritesGroupAddresses))
{
log_error("client", "discarding %s because groups can have at most a size of %d", aAddr, pSelf->m_FavoritesGroupNum);
return;
}
log_info("client", "adding %s to favorites group", aAddr);
pSelf->m_aFavoritesGroupAddresses[pSelf->m_FavoritesGroupNum] = Addr;
pSelf->m_FavoritesGroupAllowPing = pSelf->m_FavoritesGroupAllowPing || AllowPing;
pSelf->m_FavoritesGroupNum += 1;
}
else
{
log_info("client", "adding %s to favorites", aAddr);
pSelf->m_pFavorites->Add(&Addr, 1);
if(AllowPing)
{
pSelf->m_pFavorites->AllowPing(&Addr, 1, true);
}
}
}
@ -3695,7 +3753,7 @@ void CClient::Con_RemoveFavorite(IConsole::IResult *pResult, void *pUserData)
CClient *pSelf = (CClient *)pUserData;
NETADDR Addr;
if(net_addr_from_str(&Addr, pResult->GetString(0)) == 0)
pSelf->m_ServerBrowser.RemoveFavorite(Addr);
pSelf->m_pFavorites->Remove(&Addr, 1);
}
void CClient::DemoSliceBegin()
@ -4383,6 +4441,8 @@ void CClient::RegisterCommands()
m_pConsole->Register("record", "?r[file]", CFGFLAG_CLIENT, Con_Record, this, "Record to the file");
m_pConsole->Register("stoprecord", "", CFGFLAG_CLIENT, Con_StopRecord, this, "Stop recording");
m_pConsole->Register("add_demomarker", "", CFGFLAG_CLIENT, Con_AddDemoMarker, this, "Add demo timeline marker");
m_pConsole->Register("begin_favorite_group", "", CFGFLAG_CLIENT, Con_BeginFavoriteGroup, this, "Use this before `add_favorite` to group favorites. End with `end_favorite_group`");
m_pConsole->Register("end_favorite_group", "", CFGFLAG_CLIENT, Con_EndFavoriteGroup, this, "Use this after `add_favorite` to group favorites. Start with `begin_favorite_group`");
m_pConsole->Register("add_favorite", "s[host|ip] ?s['allow_ping']", CFGFLAG_CLIENT, Con_AddFavorite, this, "Add a server as a favorite");
m_pConsole->Register("remove_favorite", "r[host|ip]", CFGFLAG_CLIENT, Con_RemoveFavorite, this, "Remove a server from favorites");
m_pConsole->Register("demo_slice_start", "", CFGFLAG_CLIENT, Con_DemoSliceBegin, this, "");
@ -4403,6 +4463,9 @@ void CClient::RegisterCommands()
m_pConsole->Chain("br_filter_string", ConchainServerBrowserUpdate, this);
m_pConsole->Chain("br_filter_gametype", ConchainServerBrowserUpdate, this);
m_pConsole->Chain("br_filter_serveraddress", ConchainServerBrowserUpdate, this);
m_pConsole->Chain("add_favorite", ConchainServerBrowserUpdate, this);
m_pConsole->Chain("remove_favorite", ConchainServerBrowserUpdate, this);
m_pConsole->Chain("end_favorite_group", ConchainServerBrowserUpdate, this);
m_pConsole->Chain("gfx_screen", ConchainWindowScreen, this);
m_pConsole->Chain("gfx_fullscreen", ConchainFullscreen, this);
@ -4567,6 +4630,7 @@ int main(int argc, const char **argv)
RegisterFail = RegisterFail || !pKernel->RegisterInterface(static_cast<IMap *>(pEngineMap), false);
RegisterFail = RegisterFail || !pKernel->RegisterInterface(CreateEditor(), false);
RegisterFail = RegisterFail || !pKernel->RegisterInterface(CreateFavorites().release());
RegisterFail = RegisterFail || !pKernel->RegisterInterface(CreateGameClient());
RegisterFail = RegisterFail || !pKernel->RegisterInterface(pStorage);
RegisterFail = RegisterFail || !pKernel->RegisterInterface(pDiscord);

View file

@ -110,6 +110,7 @@ class CClient : public IClient, public CDemoPlayer::IListener
IEngineInput *m_pInput;
IEngineGraphics *m_pGraphics;
IEngineSound *m_pSound;
IFavorites *m_pFavorites;
IGameClient *m_pGameClient;
IEngineMap *m_pMap;
IConfigManager *m_pConfigManager;
@ -291,6 +292,12 @@ class CClient : public IClient, public CDemoPlayer::IListener
int m_OwnExecutableSize = 0;
IOHANDLE m_OwnExecutable;
// favorite command handling
bool m_FavoritesGroup = false;
bool m_FavoritesGroupAllowPing = false;
int m_FavoritesGroupNum = 0;
NETADDR m_aFavoritesGroupAddresses[MAX_SERVER_ADDRESSES];
void UpdateDemoIntraTimers();
int MaxLatencyTicks() const;
int PredictionMargin() const;
@ -446,6 +453,8 @@ public:
static void Con_Rcon(IConsole::IResult *pResult, void *pUserData);
static void Con_RconAuth(IConsole::IResult *pResult, void *pUserData);
static void Con_RconLogin(IConsole::IResult *pResult, void *pUserData);
static void Con_BeginFavoriteGroup(IConsole::IResult *pResult, void *pUserData);
static void Con_EndFavoriteGroup(IConsole::IResult *pResult, void *pUserData);
static void Con_AddFavorite(IConsole::IResult *pResult, void *pUserData);
static void Con_RemoveFavorite(IConsole::IResult *pResult, void *pUserData);
static void Con_Play(IConsole::IResult *pResult, void *pUserData);

View file

@ -0,0 +1,249 @@
#include <engine/favorites.h>
#include <engine/shared/config.h>
#include <engine/shared/protocol.h>
#include <unordered_map>
#include <vector>
class CFavorites : public IFavorites
{
protected:
void OnConfigSave(IConfigManager *pConfigManager) override;
public:
TRISTATE IsFavorite(const NETADDR *pAddrs, int NumAddrs) const override;
TRISTATE IsPingAllowed(const NETADDR *pAddrs, int NumAddrs) const override;
void Add(const NETADDR *pAddrs, int NumAddrs) override;
void AllowPing(const NETADDR *pAddrs, int NumAddrs, bool AllowPing) override;
void Remove(const NETADDR *pAddrs, int NumAddrs) override;
void AllEntries(const CEntry **ppEntries, int *pNumEntries) override;
private:
std::vector<CEntry> m_aEntries;
std::unordered_map<NETADDR, int> m_ByAddr;
CEntry *Entry(const NETADDR &Addr);
const CEntry *Entry(const NETADDR &Addr) const;
// `pEntry` must come from the `m_aEntries` vector.
void RemoveEntry(CEntry *pEntry);
};
void CFavorites::OnConfigSave(IConfigManager *pConfigManager)
{
for(const auto &Entry : m_aEntries)
{
if(Entry.m_NumAddrs > 1)
{
pConfigManager->WriteLine("begin_favorite_group");
}
for(int i = 0; i < Entry.m_NumAddrs; i++)
{
char aAddr[NETADDR_MAXSTRSIZE];
char aBuffer[128];
net_addr_str(&Entry.m_aAddrs[i], aAddr, sizeof(aAddr), true);
if(!Entry.m_AllowPing)
{
str_format(aBuffer, sizeof(aBuffer), "add_favorite %s", aAddr);
}
else
{
// Add quotes to the first parameter for backward
// compatibility with versions that took a `r` console
// parameter.
str_format(aBuffer, sizeof(aBuffer), "add_favorite \"%s\" allow_ping", aAddr);
}
pConfigManager->WriteLine(aBuffer);
}
if(Entry.m_NumAddrs > 1)
{
pConfigManager->WriteLine("end_favorite_group");
}
}
}
TRISTATE CFavorites::IsFavorite(const NETADDR *pAddrs, int NumAddrs) const
{
bool All = true;
bool None = true;
for(int i = 0; i < NumAddrs && (All || None); i++)
{
const CEntry *pEntry = Entry(pAddrs[i]);
if(pEntry)
{
None = false;
}
else
{
All = false;
}
}
// Return ALL if no addresses were passed.
if(All)
{
return TRISTATE::ALL;
}
else if(None)
{
return TRISTATE::NONE;
}
else
{
return TRISTATE::SOME;
}
}
TRISTATE CFavorites::IsPingAllowed(const NETADDR *pAddrs, int NumAddrs) const
{
bool All = true;
bool None = true;
for(int i = 0; i < NumAddrs && (All || None); i++)
{
const CEntry *pEntry = Entry(pAddrs[i]);
if(pEntry == nullptr)
{
continue;
}
if(pEntry->m_AllowPing)
{
None = false;
}
else
{
All = false;
}
}
// Return ALL if no addresses were passed.
if(All)
{
return TRISTATE::ALL;
}
else if(None)
{
return TRISTATE::NONE;
}
else
{
return TRISTATE::SOME;
}
}
void CFavorites::Add(const NETADDR *pAddrs, int NumAddrs)
{
// First make sure that all the addresses are not registered for some
// other favorite.
for(int i = 0; i < NumAddrs; i++)
{
CEntry *pEntry = Entry(pAddrs[i]);
if(pEntry == nullptr)
{
continue;
}
for(int j = 0; j < pEntry->m_NumAddrs; j++)
{
if(pEntry->m_aAddrs[j] == pAddrs[i])
{
pEntry->m_aAddrs[j] = pEntry->m_aAddrs[pEntry->m_NumAddrs - 1];
pEntry->m_NumAddrs -= 1;
break;
}
}
// If the entry has become empty due to the cleaning, remove it
// completely.
if(pEntry->m_NumAddrs == 0)
{
RemoveEntry(pEntry);
}
}
// Add the new entry.
CEntry NewEntry;
mem_zero(&NewEntry, sizeof(NewEntry));
NewEntry.m_NumAddrs = std::min(NumAddrs, (int)std::size(NewEntry.m_aAddrs));
for(int i = 0; i < NewEntry.m_NumAddrs; i++)
{
NewEntry.m_aAddrs[i] = pAddrs[i];
m_ByAddr[pAddrs[i]] = m_aEntries.size();
}
NewEntry.m_AllowPing = false;
m_aEntries.push_back(NewEntry);
}
void CFavorites::AllowPing(const NETADDR *pAddrs, int NumAddrs, bool AllowPing)
{
for(int i = 0; i < NumAddrs; i++)
{
CEntry *pEntry = Entry(pAddrs[i]);
if(pEntry == nullptr)
{
continue;
}
pEntry->m_AllowPing = AllowPing;
}
}
void CFavorites::Remove(const NETADDR *pAddrs, int NumAddrs)
{
for(int i = 0; i < NumAddrs; i++)
{
CEntry *pEntry = Entry(pAddrs[i]);
if(pEntry == nullptr)
{
continue;
}
for(int j = 0; j < pEntry->m_NumAddrs; j++)
{
m_ByAddr.erase(pEntry->m_aAddrs[j]);
}
RemoveEntry(pEntry);
}
}
void CFavorites::AllEntries(const CEntry **ppEntries, int *pNumEntries)
{
*ppEntries = m_aEntries.data();
*pNumEntries = m_aEntries.size();
}
CFavorites::CEntry *CFavorites::Entry(const NETADDR &Addr)
{
auto Entry = m_ByAddr.find(Addr);
if(Entry == m_ByAddr.end())
{
return nullptr;
}
return &m_aEntries[Entry->second];
}
const CFavorites::CEntry *CFavorites::Entry(const NETADDR &Addr) const
{
auto Entry = m_ByAddr.find(Addr);
if(Entry == m_ByAddr.end())
{
return nullptr;
}
return &m_aEntries[Entry->second];
}
void CFavorites::RemoveEntry(CEntry *pEntry)
{
// Replace the entry
int Index = pEntry - &m_aEntries[0];
*pEntry = m_aEntries[m_aEntries.size() - 1];
m_aEntries.pop_back();
if(Index != (int)m_aEntries.size())
{
for(int i = 0; i < pEntry->m_NumAddrs; i++)
{
m_ByAddr.at(pEntry->m_aAddrs[i]) = Index;
}
}
}
void IFavorites::ConfigSaveCallback(IConfigManager *pConfigManager, void *pUserData)
{
((IFavorites *)pUserData)->OnConfigSave(pConfigManager);
}
std::unique_ptr<IFavorites> CreateFavorites()
{
return std::make_unique<CFavorites>();
}

View file

@ -7,6 +7,7 @@
#include <algorithm>
#include <climits>
#include <unordered_set>
#include <vector>
#include <base/hash_ctxt.h>
@ -24,6 +25,7 @@
#include <engine/config.h>
#include <engine/console.h>
#include <engine/engine.h>
#include <engine/favorites.h>
#include <engine/friends.h>
#include <engine/serverbrowser.h>
#include <engine/storage.h>
@ -49,10 +51,6 @@ CServerBrowser::CServerBrowser()
m_ppServerlist = 0;
m_pSortedServerlist = 0;
m_NumFavoriteServers = 0;
mem_zero(m_apServerlistIp, sizeof(m_apServerlistIp));
m_pFirstReqServer = 0; // request list
m_pLastReqServer = 0;
m_NumRequests = 0;
@ -93,11 +91,9 @@ void CServerBrowser::SetBaseInfo(class CNetClient *pClient, const char *pNetVers
str_copy(m_aNetVersion, pNetVersion);
m_pConsole = Kernel()->RequestInterface<IConsole>();
m_pEngine = Kernel()->RequestInterface<IEngine>();
m_pFavorites = Kernel()->RequestInterface<IFavorites>();
m_pFriends = Kernel()->RequestInterface<IFriends>();
m_pStorage = Kernel()->RequestInterface<IStorage>();
IConfigManager *pConfigManager = Kernel()->RequestInterface<IConfigManager>();
if(pConfigManager)
pConfigManager->RegisterCallback(ConfigSaveCallback, this);
m_pPingCache = CreateServerBrowserPingCache(m_pConsole, m_pStorage);
RegisterCommands();
@ -117,6 +113,8 @@ void CServerBrowser::Con_LeakIpAddress(IConsole::IResult *pResult, void *pUserDa
{
CServerBrowser *pThis = (CServerBrowser *)pUserData;
// We only consider the first address of every server.
std::vector<int> vSortedServers;
// Sort servers by IP address, ignoring port.
class CAddrComparer
@ -125,8 +123,8 @@ void CServerBrowser::Con_LeakIpAddress(IConsole::IResult *pResult, void *pUserDa
CServerBrowser *m_pThis;
bool operator()(int i, int j)
{
NETADDR Addr1 = m_pThis->m_ppServerlist[i]->m_Addr;
NETADDR Addr2 = m_pThis->m_ppServerlist[j]->m_Addr;
NETADDR Addr1 = m_pThis->m_ppServerlist[i]->m_Info.m_aAddresses[0];
NETADDR Addr2 = m_pThis->m_ppServerlist[j]->m_Info.m_aAddresses[0];
Addr1.port = 0;
Addr2.port = 0;
return net_addr_comp(&Addr1, &Addr2) < 0;
@ -148,7 +146,7 @@ void CServerBrowser::Con_LeakIpAddress(IConsole::IResult *pResult, void *pUserDa
NETADDR NextAddr;
if(i < (int)vSortedServers.size())
{
NextAddr = pThis->m_ppServerlist[vSortedServers[i]]->m_Addr;
NextAddr = pThis->m_ppServerlist[vSortedServers[i]]->m_Info.m_aAddresses[0];
NextAddr.port = 0;
}
bool New = Start == -1 || i == (int)vSortedServers.size() || net_addr_comp(&Addr, &NextAddr) != 0;
@ -159,7 +157,7 @@ void CServerBrowser::Con_LeakIpAddress(IConsole::IResult *pResult, void *pUserDa
pChosen->m_RequestIgnoreInfo = true;
pThis->QueueRequest(pChosen);
char aAddr[NETADDR_MAXSTRSIZE];
net_addr_str(&pChosen->m_Addr, aAddr, sizeof(aAddr), true);
net_addr_str(&pChosen->m_Info.m_aAddresses[0], aAddr, sizeof(aAddr), true);
dbg_msg("serverbrowse/dbg", "queuing ping request for %s", aAddr);
}
if(i < (int)vSortedServers.size() && New)
@ -465,14 +463,12 @@ void CServerBrowser::RemoveRequest(CServerEntry *pEntry)
CServerBrowser::CServerEntry *CServerBrowser::Find(const NETADDR &Addr)
{
CServerEntry *pEntry = m_apServerlistIp[Addr.ip[0]];
for(; pEntry; pEntry = pEntry->m_pNextIp)
auto Entry = m_ByAddr.find(Addr);
if(Entry == m_ByAddr.end())
{
if(net_addr_comp(&pEntry->m_Addr, &Addr) == 0)
return pEntry;
return nullptr;
}
return (CServerEntry *)0;
return m_ppServerlist[Entry->second];
}
void CServerBrowser::QueueRequest(CServerEntry *pEntry)
@ -488,15 +484,47 @@ void CServerBrowser::QueueRequest(CServerEntry *pEntry)
m_NumRequests++;
}
void ServerBrowserFormatAddresses(char *pBuffer, int BufferSize, NETADDR *pAddrs, int NumAddrs)
{
for(int i = 0; i < NumAddrs; i++)
{
if(i != 0)
{
if(BufferSize <= 1)
{
return;
}
pBuffer[0] = ',';
pBuffer[1] = 0;
pBuffer += 1;
BufferSize -= 1;
}
if(BufferSize <= 1)
{
return;
}
net_addr_str(&pAddrs[i], pBuffer, BufferSize, true);
int Length = str_length(pBuffer);
pBuffer += Length;
BufferSize -= Length;
}
}
void CServerBrowser::SetInfo(CServerEntry *pEntry, const CServerInfo &Info)
{
bool Fav = pEntry->m_Info.m_Favorite;
TRISTATE Fav = pEntry->m_Info.m_Favorite;
TRISTATE FavAllowPing = pEntry->m_Info.m_FavoriteAllowPing;
bool Off = pEntry->m_Info.m_Official;
NETADDR aAddresses[MAX_SERVER_ADDRESSES];
mem_copy(aAddresses, pEntry->m_Info.m_aAddresses, sizeof(aAddresses));
int NumAddresses = pEntry->m_Info.m_NumAddresses;
pEntry->m_Info = Info;
pEntry->m_Info.m_Favorite = Fav;
pEntry->m_Info.m_FavoriteAllowPing = FavAllowPing;
pEntry->m_Info.m_Official = Off;
pEntry->m_Info.m_NetAddr = pEntry->m_Addr;
net_addr_str(&pEntry->m_Info.m_NetAddr, pEntry->m_Info.m_aAddress, sizeof(pEntry->m_Info.m_aAddress), 1);
mem_copy(pEntry->m_Info.m_aAddresses, aAddresses, sizeof(pEntry->m_Info.m_aAddresses));
pEntry->m_Info.m_NumAddresses = NumAddresses;
ServerBrowserFormatAddresses(pEntry->m_Info.m_aAddress, sizeof(pEntry->m_Info.m_aAddress), pEntry->m_Info.m_aAddresses, pEntry->m_Info.m_NumAddresses);
class CPlayerScoreNameLess
{
@ -530,23 +558,42 @@ void CServerBrowser::SetInfo(CServerEntry *pEntry, const CServerInfo &Info)
void CServerBrowser::SetLatency(NETADDR Addr, int Latency)
{
Addr.port = 0;
for(CServerEntry *pEntry = m_apServerlistIp[Addr.ip[0]]; pEntry; pEntry = pEntry->m_pNextIp)
{
NETADDR Other = pEntry->m_Addr;
Other.port = 0;
if(net_addr_comp(&Addr, &Other) == 0 && pEntry->m_GotInfo)
{
pEntry->m_Info.m_Latency = Latency;
pEntry->m_Info.m_LatencyIsEstimated = false;
}
}
m_pPingCache->CachePing(Addr, Latency);
Addr.port = 0;
for(int i = 0; i < m_NumServers; i++)
{
if(!m_ppServerlist[i]->m_GotInfo)
{
continue;
}
bool Found = false;
for(int j = 0; j < m_ppServerlist[i]->m_Info.m_NumAddresses; j++)
{
NETADDR Other = m_ppServerlist[i]->m_Info.m_aAddresses[j];
Other.port = 0;
if(Addr == Other)
{
Found = true;
break;
}
}
if(!Found)
{
continue;
}
int Ping = m_pPingCache->GetPing(m_ppServerlist[i]->m_Info.m_aAddresses, m_ppServerlist[i]->m_Info.m_NumAddresses);
if(Ping == -1)
{
continue;
}
m_ppServerlist[i]->m_Info.m_Latency = Ping;
m_ppServerlist[i]->m_Info.m_LatencyIsEstimated = false;
}
}
CServerBrowser::CServerEntry *CServerBrowser::Add(const NETADDR &Addr)
CServerBrowser::CServerEntry *CServerBrowser::Add(const NETADDR *pAddrs, int NumAddrs)
{
int Hash = Addr.ip[0];
CServerEntry *pEntry = 0;
// create new pEntry
@ -554,37 +601,43 @@ CServerBrowser::CServerEntry *CServerBrowser::Add(const NETADDR &Addr)
mem_zero(pEntry, sizeof(CServerEntry));
// set the info
pEntry->m_Addr = Addr;
pEntry->m_Info.m_NetAddr = Addr;
mem_copy(pEntry->m_Info.m_aAddresses, pAddrs, NumAddrs * sizeof(pAddrs[0]));
pEntry->m_Info.m_NumAddresses = NumAddrs;
pEntry->m_Info.m_Latency = 999;
pEntry->m_Info.m_HasRank = -1;
net_addr_str(&Addr, pEntry->m_Info.m_aAddress, sizeof(pEntry->m_Info.m_aAddress), true);
str_copy(pEntry->m_Info.m_aName, pEntry->m_Info.m_aAddress);
ServerBrowserFormatAddresses(pEntry->m_Info.m_aAddress, sizeof(pEntry->m_Info.m_aAddress), pEntry->m_Info.m_aAddresses, pEntry->m_Info.m_NumAddresses);
str_copy(pEntry->m_Info.m_aName, pEntry->m_Info.m_aAddress, sizeof(pEntry->m_Info.m_aName));
// check if it's a favorite
pEntry->m_Info.m_Favorite = IsFavorite(Addr);
pEntry->m_Info.m_Favorite = m_pFavorites->IsFavorite(pEntry->m_Info.m_aAddresses, pEntry->m_Info.m_NumAddresses);
pEntry->m_Info.m_FavoriteAllowPing = m_pFavorites->IsPingAllowed(pEntry->m_Info.m_aAddresses, pEntry->m_Info.m_NumAddresses);
// check if it's an official server
for(auto &Network : m_aNetworks)
bool Official = false;
for(int i = 0; !Official && i < (int)std::size(m_aNetworks); i++)
{
for(int i = 0; i < Network.m_NumCountries; i++)
for(int j = 0; !Official && j < m_aNetworks[i].m_NumCountries; j++)
{
CNetworkCountry *pCntr = &Network.m_aCountries[i];
for(int j = 0; j < pCntr->m_NumServers; j++)
CNetworkCountry *pCntr = &m_aNetworks[i].m_aCountries[j];
for(int k = 0; !Official && k < pCntr->m_NumServers; k++)
{
if(net_addr_comp(&Addr, &pCntr->m_aServers[j]) == 0)
for(int l = 0; !Official && l < NumAddrs; l++)
{
pEntry->m_Info.m_Official = true;
break;
if(pAddrs[l] == pCntr->m_aServers[k])
{
Official = true;
}
}
}
}
}
pEntry->m_Info.m_Official = Official;
// add to the hash list
pEntry->m_pNextIp = m_apServerlistIp[Hash];
m_apServerlistIp[Hash] = pEntry;
for(int i = 0; i < NumAddrs; i++)
{
m_ByAddr[pAddrs[i]] = m_NumServers;
}
if(m_NumServers == m_NumServerCapacity)
{
@ -605,153 +658,94 @@ CServerBrowser::CServerEntry *CServerBrowser::Add(const NETADDR &Addr)
return pEntry;
}
void CServerBrowser::Set(const NETADDR &Addr, int Type, int Token, const CServerInfo *pInfo)
void CServerBrowser::OnServerInfoUpdate(const NETADDR &Addr, int Token, const CServerInfo *pInfo)
{
CServerEntry *pEntry = 0;
if(Type == IServerBrowser::SET_MASTER_ADD)
int BasicToken = Token;
int ExtraToken = 0;
if(pInfo->m_Type == SERVERINFO_EXTENDED)
{
if(m_ServerlistType != IServerBrowser::TYPE_INTERNET)
return;
if(!Find(Addr))
{
pEntry = Add(Addr);
QueueRequest(pEntry);
}
BasicToken = Token & 0xff;
ExtraToken = Token >> 8;
}
else if(Type == IServerBrowser::SET_FAV_ADD)
{
if(m_ServerlistType != IServerBrowser::TYPE_FAVORITES)
return;
if(!Find(Addr))
{
pEntry = Add(Addr);
QueueRequest(pEntry);
}
}
else if(Type == IServerBrowser::SET_DDNET_ADD)
{
if(m_ServerlistType != IServerBrowser::TYPE_DDNET)
return;
CServerEntry *pEntry = Find(Addr);
if(!Find(Addr))
{
pEntry = Add(Addr);
QueueRequest(pEntry);
}
}
else if(Type == IServerBrowser::SET_KOG_ADD)
if(m_ServerlistType == IServerBrowser::TYPE_LAN)
{
if(m_ServerlistType != IServerBrowser::TYPE_KOG)
return;
if(!Find(Addr))
NETADDR Broadcast;
mem_zero(&Broadcast, sizeof(Broadcast));
Broadcast.type = m_pNetClient->NetType() | NETTYPE_LINK_BROADCAST;
int TokenBC = GenerateToken(Broadcast);
bool Drop = false;
Drop = Drop || BasicToken != GetBasicToken(TokenBC);
Drop = Drop || (pInfo->m_Type == SERVERINFO_EXTENDED && ExtraToken != GetExtraToken(TokenBC));
if(Drop)
{
pEntry = Add(Addr);
QueueRequest(pEntry);
return;
}
if(!pEntry)
pEntry = Add(&Addr, 1);
}
else if(Type == IServerBrowser::SET_HTTPINFO)
else
{
if(!pEntry)
{
pEntry = Add(Addr);
return;
}
if(pEntry)
int TokenAddr = GenerateToken(Addr);
bool Drop = false;
Drop = Drop || BasicToken != GetBasicToken(TokenAddr);
Drop = Drop || (pInfo->m_Type == SERVERINFO_EXTENDED && ExtraToken != GetExtraToken(TokenAddr));
if(Drop)
{
SetInfo(pEntry, *pInfo);
pEntry->m_RequestIgnoreInfo = true;
return;
}
}
else if(Type == IServerBrowser::SET_TOKEN)
if(m_ServerlistType == IServerBrowser::TYPE_LAN)
{
int BasicToken = Token;
int ExtraToken = 0;
if(pInfo->m_Type == SERVERINFO_EXTENDED)
SetInfo(pEntry, *pInfo);
pEntry->m_Info.m_Latency = minimum(static_cast<int>((time_get() - m_BroadcastTime) * 1000 / time_freq()), 999);
if(pInfo->m_Type == SERVERINFO_VANILLA && Is64Player(pInfo))
{
BasicToken = Token & 0xff;
ExtraToken = Token >> 8;
pEntry->m_Request64Legacy = true;
// Force a quick update.
RequestImpl64(Addr, pEntry);
}
}
else if(pEntry->m_RequestTime > 0)
{
if(!pEntry->m_RequestIgnoreInfo)
{
SetInfo(pEntry, *pInfo);
}
pEntry = Find(Addr);
if(m_ServerlistType == IServerBrowser::TYPE_LAN)
int Latency = minimum(static_cast<int>((time_get() - pEntry->m_RequestTime) * 1000 / time_freq()), 999);
if(!pEntry->m_RequestIgnoreInfo)
{
NETADDR Broadcast;
mem_zero(&Broadcast, sizeof(Broadcast));
Broadcast.type = m_pNetClient->NetType() | NETTYPE_LINK_BROADCAST;
int TokenBC = GenerateToken(Broadcast);
bool Drop = false;
Drop = Drop || BasicToken != GetBasicToken(TokenBC);
Drop = Drop || (pInfo->m_Type == SERVERINFO_EXTENDED && ExtraToken != GetExtraToken(TokenBC));
if(Drop)
{
return;
}
if(!pEntry)
pEntry = Add(Addr);
pEntry->m_Info.m_Latency = Latency;
}
else
{
if(!pEntry)
{
return;
}
int TokenAddr = GenerateToken(Addr);
bool Drop = false;
Drop = Drop || BasicToken != GetBasicToken(TokenAddr);
Drop = Drop || (pInfo->m_Type == SERVERINFO_EXTENDED && ExtraToken != GetExtraToken(TokenAddr));
if(Drop)
{
return;
}
char aAddr[NETADDR_MAXSTRSIZE];
net_addr_str(&Addr, aAddr, sizeof(aAddr), true);
dbg_msg("serverbrowse/dbg", "received ping response from %s", aAddr);
SetLatency(Addr, Latency);
}
pEntry->m_RequestTime = -1; // Request has been answered
if(m_ServerlistType == IServerBrowser::TYPE_LAN)
if(!pEntry->m_RequestIgnoreInfo)
{
SetInfo(pEntry, *pInfo);
pEntry->m_Info.m_Latency = minimum(static_cast<int>((time_get() - m_BroadcastTime) * 1000 / time_freq()), 999);
if(pInfo->m_Type == SERVERINFO_VANILLA && Is64Player(pInfo))
{
pEntry->m_Request64Legacy = true;
// Force a quick update.
RequestImpl64(pEntry->m_Addr, pEntry);
RequestImpl64(Addr, pEntry);
}
}
else if(pEntry->m_RequestTime > 0)
{
if(!pEntry->m_RequestIgnoreInfo)
{
SetInfo(pEntry, *pInfo);
}
int Latency = minimum(static_cast<int>((time_get() - pEntry->m_RequestTime) * 1000 / time_freq()), 999);
if(!pEntry->m_RequestIgnoreInfo)
{
pEntry->m_Info.m_Latency = Latency;
}
else
{
char aAddr[NETADDR_MAXSTRSIZE];
net_addr_str(&Addr, aAddr, sizeof(aAddr), true);
dbg_msg("serverbrowse/dbg", "received ping response from %s", aAddr);
SetLatency(Addr, Latency);
}
pEntry->m_RequestTime = -1; // Request has been answered
if(!pEntry->m_RequestIgnoreInfo)
{
if(pInfo->m_Type == SERVERINFO_VANILLA && Is64Player(pInfo))
{
pEntry->m_Request64Legacy = true;
// Force a quick update.
RequestImpl64(pEntry->m_Addr, pEntry);
}
}
}
RemoveRequest(pEntry);
}
RemoveRequest(pEntry);
m_SortOnNextUpdate = true;
}
@ -909,28 +903,8 @@ void CServerBrowser::SetCurrentServerPing(const NETADDR &Addr, int Ping)
SetLatency(Addr, minimum(Ping, 999));
}
void ServerBrowserFillEstimatedLatency(int OwnLocation, const IServerBrowserPingCache::CEntry *pEntries, int NumEntries, int *pIndex, NETADDR Addr, CServerInfo *pInfo)
{
Addr.port = 0;
while(*pIndex < NumEntries && net_addr_comp(&pEntries[*pIndex].m_Addr, &Addr) < 0)
{
*pIndex += 1;
}
if(*pIndex >= NumEntries || net_addr_comp(&pEntries[*pIndex].m_Addr, &Addr) != 0)
{
pInfo->m_LatencyIsEstimated = true;
pInfo->m_Latency = CServerInfo::EstimateLatency(OwnLocation, pInfo->m_Location);
return;
}
pInfo->m_LatencyIsEstimated = false;
pInfo->m_Latency = pEntries[*pIndex].m_Ping;
}
void CServerBrowser::UpdateFromHttp()
{
const IServerBrowserPingCache::CEntry *pPingEntries;
int NumPingEntries;
m_pPingCache->GetPingCache(&pPingEntries, &NumPingEntries);
int OwnLocation;
if(str_comp(g_Config.m_BrLocation, "auto") == 0)
{
@ -948,24 +922,13 @@ void CServerBrowser::UpdateFromHttp()
int NumServers = m_pHttp->NumServers();
int NumLegacyServers = m_pHttp->NumLegacyServers();
std::unordered_set<NETADDR> WantedAddrs;
std::function<bool(const NETADDR *, int)> Want = [](const NETADDR *pAddrs, int NumAddrs) { return true; };
if(m_ServerlistType != IServerBrowser::TYPE_INTERNET)
{
class CWantedAddr
{
public:
NETADDR m_Addr;
bool m_FallbackToPing;
bool m_Got;
};
std::vector<CWantedAddr> vWantedAddresses;
int LegacySetType;
if(m_ServerlistType == IServerBrowser::TYPE_FAVORITES)
{
for(int i = 0; i < m_NumFavoriteServers; i++)
{
vWantedAddresses.push_back(CWantedAddr{m_aFavoriteServers[i], m_aFavoriteServersAllowPing[i], false});
}
LegacySetType = IServerBrowser::SET_FAV_ADD;
Want = [&](const NETADDR *pAddrs, int NumAddrs) -> bool { return m_pFavorites->IsFavorite(pAddrs, NumAddrs) != TRISTATE::NONE; };
}
else
{
@ -976,13 +939,11 @@ void CServerBrowser::UpdateFromHttp()
{
case IServerBrowser::TYPE_DDNET:
Network = NETWORK_DDNET;
LegacySetType = IServerBrowser::SET_DDNET_ADD;
pExcludeCountries = g_Config.m_BrFilterExcludeCountries;
pExcludeTypes = g_Config.m_BrFilterExcludeTypes;
break;
case IServerBrowser::TYPE_KOG:
Network = NETWORK_KOG;
LegacySetType = IServerBrowser::SET_KOG_ADD;
pExcludeCountries = g_Config.m_BrFilterExcludeCountriesKoG;
pExcludeTypes = g_Config.m_BrFilterExcludeTypesKoG;
break;
@ -1016,139 +977,83 @@ void CServerBrowser::UpdateFromHttp()
if(DDNetFiltered(pExcludeTypes, pCntr->m_aTypes[g]))
continue;
vWantedAddresses.push_back(CWantedAddr{pCntr->m_aServers[g], false, false});
WantedAddrs.insert(pCntr->m_aServers[g]);
}
}
}
std::vector<int> vSortedServers;
std::vector<int> vSortedLegacyServers;
vSortedServers.reserve(NumServers);
for(int i = 0; i < NumServers; i++)
{
vSortedServers.push_back(i);
}
vSortedLegacyServers.reserve(NumLegacyServers);
for(int i = 0; i < NumLegacyServers; i++)
{
vSortedLegacyServers.push_back(i);
}
class CWantedAddrComparer
{
public:
bool operator()(const CWantedAddr &a, const CWantedAddr &b)
{
return net_addr_comp(&a.m_Addr, &b.m_Addr) < 0;
}
};
class CAddrComparer
{
public:
IServerBrowserHttp *m_pHttp;
bool operator()(int i, int j)
{
return net_addr_comp(&m_pHttp->ServerAddress(i), &m_pHttp->ServerAddress(j)) < 0;
}
};
class CLegacyAddrComparer
{
public:
IServerBrowserHttp *m_pHttp;
bool operator()(int i, int j)
{
return net_addr_comp(&m_pHttp->LegacyServer(i), &m_pHttp->LegacyServer(j)) < 0;
}
};
std::sort(vWantedAddresses.begin(), vWantedAddresses.end(), CWantedAddrComparer());
std::sort(vSortedServers.begin(), vSortedServers.end(), CAddrComparer{m_pHttp});
std::sort(vSortedLegacyServers.begin(), vSortedLegacyServers.end(), CLegacyAddrComparer{m_pHttp});
unsigned i = 0;
unsigned j = 0;
int p = 0;
while(i < vWantedAddresses.size() && j < vSortedServers.size())
{
int Cmp = net_addr_comp(&vWantedAddresses[i].m_Addr, &m_pHttp->ServerAddress(vSortedServers[j]));
if(Cmp != 0)
{
if(Cmp < 0)
Want = [&](const NETADDR *pAddrs, int NumAddrs) -> bool {
for(int i = 0; i < NumAddrs; i++)
{
i++;
}
else
{
j++;
}
continue;
}
vWantedAddresses[i].m_Got = true;
NETADDR Addr;
CServerInfo Info;
m_pHttp->Server(vSortedServers[j], &Addr, &Info);
ServerBrowserFillEstimatedLatency(OwnLocation, pPingEntries, NumPingEntries, &p, Addr, &Info);
Info.m_HasRank = HasRank(Info.m_aMap);
Set(Addr, IServerBrowser::SET_HTTPINFO, -1, &Info);
i++;
j++;
}
i = 0;
j = 0;
while(i < vWantedAddresses.size() && j < vSortedLegacyServers.size())
{
int Cmp = net_addr_comp(&vWantedAddresses[i].m_Addr, &m_pHttp->LegacyServer(vSortedLegacyServers[j]));
if(Cmp != 0)
{
if(Cmp < 0)
{
i++;
}
else
{
j++;
}
continue;
}
vWantedAddresses[i].m_Got = true;
Set(m_pHttp->LegacyServer(vSortedLegacyServers[j]), LegacySetType, -1, nullptr);
i++;
j++;
}
for(const CWantedAddr &Wanted : vWantedAddresses)
{
if(!Wanted.m_Got)
{
if(Wanted.m_FallbackToPing)
{
Set(Wanted.m_Addr, LegacySetType, -1, nullptr);
}
else
{
// Also add favorites we're not allowed to ping.
if(LegacySetType == IServerBrowser::SET_FAV_ADD && !Find(Wanted.m_Addr))
if(WantedAddrs.count(pAddrs[i]))
{
Add(Wanted.m_Addr);
return true;
}
}
}
return false;
};
}
return;
}
int p = 0;
for(int i = 0; i < NumServers; i++)
{
NETADDR Addr;
CServerInfo Info;
m_pHttp->Server(i, &Addr, &Info);
ServerBrowserFillEstimatedLatency(OwnLocation, pPingEntries, NumPingEntries, &p, Addr, &Info);
CServerInfo Info = m_pHttp->Server(i);
if(!Want(Info.m_aAddresses, Info.m_NumAddresses))
{
continue;
}
int Ping = m_pPingCache->GetPing(Info.m_aAddresses, Info.m_NumAddresses);
Info.m_LatencyIsEstimated = Ping == -1;
if(Info.m_LatencyIsEstimated)
{
Info.m_Latency = CServerInfo::EstimateLatency(OwnLocation, Info.m_Location);
}
else
{
Info.m_Latency = Ping;
}
Info.m_HasRank = HasRank(Info.m_aMap);
Set(Addr, IServerBrowser::SET_HTTPINFO, -1, &Info);
CServerEntry *pEntry = Add(Info.m_aAddresses, Info.m_NumAddresses);
SetInfo(pEntry, Info);
pEntry->m_RequestIgnoreInfo = true;
}
for(int i = 0; i < NumLegacyServers; i++)
{
NETADDR Addr = m_pHttp->LegacyServer(i);
Set(Addr, IServerBrowser::SET_MASTER_ADD, -1, nullptr);
if(!Want(&Addr, 1))
{
continue;
}
QueueRequest(Add(&Addr, 1));
}
if(m_ServerlistType == IServerBrowser::TYPE_FAVORITES)
{
const IFavorites::CEntry *pFavorites;
int NumFavorites;
m_pFavorites->AllEntries(&pFavorites, &NumFavorites);
for(int i = 0; i < NumFavorites; i++)
{
bool Found = false;
for(int j = 0; j < pFavorites[i].m_NumAddrs; j++)
{
if(Find(pFavorites[i].m_aAddrs[j]))
{
Found = true;
break;
}
}
if(Found)
{
continue;
}
// (Also add favorites we're not allowed to ping.)
CServerEntry *pEntry = Add(pFavorites[i].m_aAddrs, pFavorites[i].m_NumAddrs);
if(pFavorites->m_AllowPing)
{
QueueRequest(pEntry);
}
}
}
m_SortOnNextUpdate = true;
}
void CServerBrowser::CleanUp()
@ -1157,7 +1062,7 @@ void CServerBrowser::CleanUp()
m_ServerlistHeap.Reset();
m_NumServers = 0;
m_NumSortedServers = 0;
mem_zero(m_apServerlistIp, sizeof(m_apServerlistIp));
m_ByAddr.clear();
m_pFirstReqServer = 0;
m_pLastReqServer = 0;
m_NumRequests = 0;
@ -1206,9 +1111,9 @@ void CServerBrowser::Update(bool ForceResort)
if(pEntry->m_RequestTime == 0)
{
if(pEntry->m_Request64Legacy)
RequestImpl64(pEntry->m_Addr, pEntry);
RequestImpl64(pEntry->m_Info.m_aAddresses[0], pEntry);
else
RequestImpl(pEntry->m_Addr, pEntry, nullptr, nullptr, false);
RequestImpl(pEntry->m_Info.m_aAddresses[0], pEntry, nullptr, nullptr, false);
}
Count++;
@ -1248,94 +1153,17 @@ void CServerBrowser::Update(bool ForceResort)
// check if we need to resort
if(m_Sorthash != SortHash() || ForceResort || m_SortOnNextUpdate)
{
for(int i = 0; i < m_NumServers; i++)
{
CServerInfo *pInfo = &m_ppServerlist[i]->m_Info;
pInfo->m_Favorite = m_pFavorites->IsFavorite(pInfo->m_aAddresses, pInfo->m_NumAddresses);
pInfo->m_FavoriteAllowPing = m_pFavorites->IsPingAllowed(pInfo->m_aAddresses, pInfo->m_NumAddresses);
}
Sort();
m_SortOnNextUpdate = false;
}
}
int CServerBrowser::FindFavorite(const NETADDR &Addr) const
{
// search for the address
for(int i = 0; i < m_NumFavoriteServers; i++)
{
if(net_addr_comp(&Addr, &m_aFavoriteServers[i]) == 0)
return i;
}
return -1;
}
bool CServerBrowser::GotInfo(const NETADDR &Addr) const
{
CServerEntry *pEntry = ((CServerBrowser *)this)->Find(Addr);
return pEntry && pEntry->m_GotInfo;
}
bool CServerBrowser::IsFavorite(const NETADDR &Addr) const
{
return FindFavorite(Addr) >= 0;
}
bool CServerBrowser::IsFavoritePingAllowed(const NETADDR &Addr) const
{
int i = FindFavorite(Addr);
dbg_assert(i >= 0, "invalid favorite");
return i >= 0 && m_aFavoriteServersAllowPing[i];
}
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
if(IsFavorite(Addr))
{
return;
}
// add the server to the list
m_aFavoriteServers[m_NumFavoriteServers] = Addr;
m_aFavoriteServersAllowPing[m_NumFavoriteServers] = false;
m_NumFavoriteServers++;
pEntry = Find(Addr);
if(pEntry)
pEntry->m_Info.m_Favorite = true;
if(g_Config.m_Debug)
{
char aAddrStr[NETADDR_MAXSTRSIZE];
net_addr_str(&Addr, aAddrStr, sizeof(aAddrStr), true);
char aBuf[256];
str_format(aBuf, sizeof(aBuf), "added fav, %s", aAddrStr);
m_pConsole->Print(IConsole::OUTPUT_LEVEL_DEBUG, "client_srvbrowse", aBuf);
}
}
void CServerBrowser::FavoriteAllowPing(const NETADDR &Addr, bool AllowPing)
{
int i = FindFavorite(Addr);
dbg_assert(i >= 0, "invalid favorite");
m_aFavoriteServersAllowPing[i] = AllowPing;
}
void CServerBrowser::RemoveFavorite(const NETADDR &Addr)
{
int i = FindFavorite(Addr);
if(i < 0)
{
return;
}
mem_move(&m_aFavoriteServers[i], &m_aFavoriteServers[i + 1], sizeof(NETADDR) * (m_NumFavoriteServers - (i + 1)));
mem_move(&m_aFavoriteServersAllowPing[i], &m_aFavoriteServersAllowPing[i + 1], sizeof(bool) * (m_NumFavoriteServers - (i + 1)));
m_NumFavoriteServers--;
CServerEntry *pEntry = Find(Addr);
if(pEntry)
pEntry->m_Info.m_Favorite = false;
}
void CServerBrowser::LoadDDNetServers()
{
if(!m_pDDNetInfo)
@ -1573,30 +1401,6 @@ int CServerBrowser::LoadingProgression() const
return 100.0f * Loaded / Servers;
}
void CServerBrowser::ConfigSaveCallback(IConfigManager *pConfigManager, void *pUserData)
{
CServerBrowser *pSelf = (CServerBrowser *)pUserData;
char aAddrStr[128];
char aBuffer[256];
for(int i = 0; i < pSelf->m_NumFavoriteServers; i++)
{
net_addr_str(&pSelf->m_aFavoriteServers[i], aAddrStr, sizeof(aAddrStr), true);
if(!pSelf->m_aFavoriteServersAllowPing[i])
{
str_format(aBuffer, sizeof(aBuffer), "add_favorite %s", aAddrStr);
}
else
{
// Add quotes to the first parameter for backward
// compatibility with versions that took a `r` console
// parameter.
str_format(aBuffer, sizeof(aBuffer), "add_favorite \"%s\" allow_ping", aAddrStr);
}
pConfigManager->WriteLine(aBuffer);
}
}
void CServerBrowser::DDNetFilterAdd(char *pFilter, const char *pName)
{
if(DDNetFiltered(pFilter, pName))

View file

@ -11,10 +11,17 @@
#include <engine/shared/http.h>
#include <engine/shared/memheap.h>
#include <unordered_map>
class CNetClient;
class IConfigManager;
class IConsole;
class IEngine;
class IFavorites;
class IFriends;
class IServerBrowserHttp;
class IServerBrowserPingCache;
class IStorage;
class CServerBrowser : public IServerBrowser
{
@ -22,15 +29,12 @@ public:
class CServerEntry
{
public:
NETADDR m_Addr;
int64_t m_RequestTime;
bool m_RequestIgnoreInfo;
int m_GotInfo;
bool m_Request64Legacy;
CServerInfo m_Info;
CServerEntry *m_pNextIp; // ip hashed list
CServerEntry *m_pPrevReq; // request list
CServerEntry *m_pNextReq;
};
@ -54,14 +58,6 @@ public:
m_FlagID = -1;
m_aName[0] = '\0';
};
/*void Add(NETADDR Addr, char* pType) {
if (m_NumServers < MAX_SERVERS)
{
m_vServers[m_NumServers] = Addr;
str_copy(m_aTypes[m_NumServers], pType, sizeof(m_aTypes[0]));
m_NumServers++;
}
};*/
};
enum
@ -104,13 +100,6 @@ public:
int NumSortedServers() const override { return m_NumSortedServers; }
const CServerInfo *SortedGet(int Index) const override;
bool GotInfo(const NETADDR &Addr) const override;
bool IsFavorite(const NETADDR &Addr) const override;
bool IsFavoritePingAllowed(const NETADDR &Addr) const override;
void AddFavorite(const NETADDR &Addr) override;
void FavoriteAllowPing(const NETADDR &Addr, bool AllowPing) override;
void RemoveFavorite(const NETADDR &Addr) override;
const char *GetTutorialServer() override;
void LoadDDNetRanks();
void RecheckOfficial();
@ -133,7 +122,8 @@ public:
//
void Update(bool ForceResort);
void Set(const NETADDR &Addr, int Type, int Token, const CServerInfo *pInfo);
void OnServerInfoUpdate(const NETADDR &Addr, int Token, const CServerInfo *pInfo);
void SetHttpInfo(const CServerInfo *pInfo);
void RequestCurrentServer(const NETADDR &Addr) const;
void RequestCurrentServerWithRandomToken(const NETADDR &Addr, int *pBasicToken, int *pToken) const;
void SetCurrentServerPing(const NETADDR &Addr, int Ping);
@ -147,11 +137,12 @@ public:
int GetCurrentType() override { return m_ServerlistType; }
private:
CNetClient *m_pNetClient;
class IConsole *m_pConsole;
class IEngine *m_pEngine;
class IFriends *m_pFriends;
class IStorage *m_pStorage;
CNetClient *m_pNetClient = nullptr;
IConsole *m_pConsole = nullptr;
IEngine *m_pEngine = nullptr;
IFriends *m_pFriends = nullptr;
IFavorites *m_pFavorites = nullptr;
IStorage *m_pStorage = nullptr;
char m_aNetVersion[128];
bool m_RefreshingHttp = false;
@ -162,18 +153,13 @@ private:
CHeap m_ServerlistHeap;
CServerEntry **m_ppServerlist;
int *m_pSortedServerlist;
NETADDR m_aFavoriteServers[MAX_FAVORITES];
bool m_aFavoriteServersAllowPing[MAX_FAVORITES];
int m_NumFavoriteServers;
std::unordered_map<NETADDR, int> m_ByAddr;
CNetwork m_aNetworks[NUM_NETWORKS];
int m_OwnLocation = CServerInfo::LOC_UNKNOWN;
json_value *m_pDDNetInfo;
CServerEntry *m_apServerlistIp[256]; // ip hash list
CServerEntry *m_pFirstReqServer; // request list
CServerEntry *m_pLastReqServer;
int m_NumRequests;
@ -198,8 +184,6 @@ private:
bool m_SortOnNextUpdate;
int FindFavorite(const NETADDR &Addr) const;
int GenerateToken(const NETADDR &Addr) const;
static int GetBasicToken(int Token);
static int GetExtraToken(int Token);
@ -221,7 +205,7 @@ private:
void CleanUp();
void UpdateFromHttp();
CServerEntry *Add(const NETADDR &Addr);
CServerEntry *Add(const NETADDR *pAddrs, int NumAddrs);
void RemoveRequest(CServerEntry *pEntry);
@ -232,8 +216,6 @@ private:
void SetInfo(CServerEntry *pEntry, const CServerInfo &Info);
void SetLatency(NETADDR Addr, int Latency);
static void ConfigSaveCallback(IConfigManager *pConfigManager, void *pUserData);
};
#endif

View file

@ -260,15 +260,9 @@ public:
{
return m_vServers.size();
}
const NETADDR &ServerAddress(int Index) const override
const CServerInfo &Server(int Index) const override
{
return m_vServers[Index].m_Addr;
}
void Server(int Index, NETADDR *pAddr, CServerInfo *pInfo) const override
{
const CEntry &Entry = m_vServers[Index];
*pAddr = Entry.m_Addr;
*pInfo = Entry.m_Info;
return m_vServers[Index];
}
int NumLegacyServers() const override
{
@ -288,15 +282,8 @@ private:
STATE_NO_MASTER,
};
class CEntry
{
public:
NETADDR m_Addr;
CServerInfo m_Info;
};
static bool Validate(json_value *pJson);
static bool Parse(json_value *pJson, std::vector<CEntry> *pvServers, std::vector<NETADDR> *pvLegacyServers);
static bool Parse(json_value *pJson, std::vector<CServerInfo> *pvServers, std::vector<NETADDR> *pvLegacyServers);
IEngine *m_pEngine;
IConsole *m_pConsole;
@ -305,7 +292,7 @@ private:
std::shared_ptr<CHttpRequest> m_pGetServers;
std::unique_ptr<CChooseMaster> m_pChooseMaster;
std::vector<CEntry> m_vServers;
std::vector<CServerInfo> m_vServers;
std::vector<NETADDR> m_vLegacyServers;
};
@ -413,13 +400,13 @@ bool ServerbrowserParseUrl(NETADDR *pOut, const char *pUrl)
}
bool CServerBrowserHttp::Validate(json_value *pJson)
{
std::vector<CEntry> vServers;
std::vector<CServerInfo> vServers;
std::vector<NETADDR> vLegacyServers;
return Parse(pJson, &vServers, &vLegacyServers);
}
bool CServerBrowserHttp::Parse(json_value *pJson, std::vector<CEntry> *pvServers, std::vector<NETADDR> *pvLegacyServers)
bool CServerBrowserHttp::Parse(json_value *pJson, std::vector<CServerInfo> *pvServers, std::vector<NETADDR> *pvLegacyServers)
{
std::vector<CEntry> vServers;
std::vector<CServerInfo> vServers;
std::vector<NETADDR> vLegacyServers;
const json_value &Json = *pJson;
@ -459,6 +446,7 @@ bool CServerBrowserHttp::Parse(json_value *pJson, std::vector<CEntry> *pvServers
}
CServerInfo SetInfo = ParsedInfo;
SetInfo.m_Location = ParsedLocation;
SetInfo.m_NumAddresses = 0;
for(unsigned int a = 0; a < Addresses.u.array.length; a++)
{
const json_value &Address = Addresses[a];
@ -466,7 +454,6 @@ bool CServerBrowserHttp::Parse(json_value *pJson, std::vector<CEntry> *pvServers
{
return true;
}
// TODO: Address address handling :P
NETADDR ParsedAddr;
if(ServerbrowserParseUrl(&ParsedAddr, Addresses[a]))
{
@ -474,7 +461,15 @@ bool CServerBrowserHttp::Parse(json_value *pJson, std::vector<CEntry> *pvServers
// Skip unknown addresses.
continue;
}
vServers.push_back({ParsedAddr, SetInfo});
if(SetInfo.m_NumAddresses < (int)std::size(SetInfo.m_aAddresses))
{
SetInfo.m_aAddresses[SetInfo.m_NumAddresses] = ParsedAddr;
SetInfo.m_NumAddresses += 1;
}
}
if(SetInfo.m_NumAddresses > 0)
{
vServers.push_back(SetInfo);
}
}
if(LegacyServers.type == json_array)

View file

@ -20,8 +20,7 @@ public:
virtual bool GetBestUrl(const char **pBestUrl) const = 0;
virtual int NumServers() const = 0;
virtual const NETADDR &ServerAddress(int Index) const = 0;
virtual void Server(int Index, NETADDR *pAddr, CServerInfo *pInfo) const = 0;
virtual const CServerInfo &Server(int Index) const = 0;
virtual int NumLegacyServers() const = 0;
virtual const NETADDR &LegacyServer(int Index) const = 0;
};

View file

@ -5,19 +5,27 @@
#include <sqlite3.h>
#include <algorithm>
#include <unordered_map>
#include <vector>
class CServerBrowserPingCache : public IServerBrowserPingCache
{
public:
class CEntry
{
public:
NETADDR m_Addr;
int m_Ping;
};
CServerBrowserPingCache(IConsole *pConsole, IStorage *pStorage);
virtual ~CServerBrowserPingCache() = default;
void Load() override;
void CachePing(NETADDR Addr, int Ping) override;
void GetPingCache(const CEntry **ppEntries, int *pNumEntries) override;
int NumEntries() const override;
void CachePing(const NETADDR &Addr, int Ping) override;
int GetPing(const NETADDR *pAddrs, int NumAddrs) const override;
private:
IConsole *m_pConsole;
@ -26,8 +34,7 @@ private:
CSqliteStmt m_pLoadStmt;
CSqliteStmt m_pStoreStmt;
std::vector<CEntry> m_vEntries;
std::vector<CEntry> m_vNewEntries;
std::unordered_map<NETADDR, int> m_Entries;
};
CServerBrowserPingCache::CServerBrowserPingCache(IConsole *pConsole, IStorage *pStorage) :
@ -55,7 +62,7 @@ void CServerBrowserPingCache::Load()
{
if(m_pDisk)
{
int PrevNewEntriesSize = m_vNewEntries.size();
std::vector<CEntry> vNewEntries;
sqlite3 *pSqlite = m_pDisk.get();
IConsole *pConsole = m_pConsole;
@ -85,7 +92,7 @@ void CServerBrowserPingCache::Load()
}
continue;
}
m_vNewEntries.push_back(CEntry{Addr, Ping});
vNewEntries.push_back(CEntry{Addr, Ping});
}
else
{
@ -95,21 +102,31 @@ void CServerBrowserPingCache::Load()
if(Error)
{
pConsole->Print(IConsole::OUTPUT_LEVEL_STANDARD, "serverbrowse_ping_cache", "failed to load ping cache");
m_vNewEntries.resize(PrevNewEntriesSize);
return;
}
for(const auto &Entry : vNewEntries)
{
m_Entries[Entry.m_Addr] = Entry.m_Ping;
}
}
}
void CServerBrowserPingCache::CachePing(NETADDR Addr, int Ping)
int CServerBrowserPingCache::NumEntries() const
{
Addr.port = 0;
m_vNewEntries.push_back(CEntry{Addr, Ping});
return m_Entries.size();
}
void CServerBrowserPingCache::CachePing(const NETADDR &Addr, int Ping)
{
NETADDR AddrWithoutPort = Addr;
AddrWithoutPort.port = 0;
m_Entries[AddrWithoutPort] = Ping;
if(m_pDisk)
{
sqlite3 *pSqlite = m_pDisk.get();
IConsole *pConsole = m_pConsole;
char aAddr[NETADDR_MAXSTRSIZE];
net_addr_str(&Addr, aAddr, sizeof(aAddr), false);
net_addr_str(&AddrWithoutPort, aAddr, sizeof(aAddr), false);
bool Error = false;
Error = Error || !m_pStoreStmt;
@ -124,80 +141,24 @@ void CServerBrowserPingCache::CachePing(NETADDR Addr, int Ping)
}
}
void CServerBrowserPingCache::GetPingCache(const CEntry **ppEntries, int *pNumEntries)
int CServerBrowserPingCache::GetPing(const NETADDR *pAddrs, int NumAddrs) const
{
if(!m_vNewEntries.empty())
int Ping = -1;
for(int i = 0; i < NumAddrs; i++)
{
class CAddrComparer
NETADDR Addr = pAddrs[i];
Addr.port = 0;
auto Entry = m_Entries.find(Addr);
if(Entry == m_Entries.end())
{
public:
bool operator()(const CEntry &a, const CEntry &b)
{
return net_addr_comp(&a.m_Addr, &b.m_Addr) < 0;
}
};
std::vector<CEntry> vOldEntries;
std::swap(m_vEntries, vOldEntries);
// Remove duplicates, keeping newer ones.
std::stable_sort(m_vNewEntries.begin(), m_vNewEntries.end(), CAddrComparer());
{
unsigned To = 0;
for(unsigned int From = 0; From < m_vNewEntries.size(); From++)
{
if(To < From)
{
m_vNewEntries[To] = m_vNewEntries[From];
}
if(From + 1 >= m_vNewEntries.size() ||
net_addr_comp(&m_vNewEntries[From].m_Addr, &m_vNewEntries[From + 1].m_Addr) != 0)
{
To++;
}
}
m_vNewEntries.resize(To);
continue;
}
// Only keep the new entries where there are duplicates.
m_vEntries.reserve(m_vNewEntries.size() + vOldEntries.size());
if(Ping == -1 || Entry->second < Ping)
{
unsigned i = 0;
unsigned j = 0;
while(i < vOldEntries.size() && j < m_vNewEntries.size())
{
int Cmp = net_addr_comp(&vOldEntries[i].m_Addr, &m_vNewEntries[j].m_Addr);
if(Cmp != 0)
{
if(Cmp < 0)
{
m_vEntries.push_back(vOldEntries[i]);
i++;
}
else
{
m_vEntries.push_back(m_vNewEntries[j]);
j++;
}
}
else
{
// Ignore the old element if we have both.
i++;
}
}
// Add the remaining elements.
for(; i < vOldEntries.size(); i++)
{
m_vEntries.push_back(vOldEntries[i]);
}
for(; j < m_vNewEntries.size(); j++)
{
m_vEntries.push_back(m_vNewEntries[j]);
}
Ping = Entry->second;
}
m_vNewEntries.clear();
}
*ppEntries = m_vEntries.data();
*pNumEntries = m_vEntries.size();
return Ping;
}
IServerBrowserPingCache *CreateServerBrowserPingCache(IConsole *pConsole, IStorage *pStorage)

View file

@ -8,21 +8,14 @@ class IStorage;
class IServerBrowserPingCache
{
public:
class CEntry
{
public:
NETADDR m_Addr;
int m_Ping;
};
virtual ~IServerBrowserPingCache() {}
virtual void Load() = 0;
virtual void CachePing(NETADDR Addr, int Ping) = 0;
// The returned list is sorted by address, the addresses don't have a
// port.
virtual void GetPingCache(const CEntry **ppEntries, int *pNumEntries) = 0;
virtual int NumEntries() const = 0;
virtual void CachePing(const NETADDR &Addr, int Ping) = 0;
// Returns -1 if the ping isn't cached.
virtual int GetPing(const NETADDR *pAddrs, int NumAddrs) const = 0;
};
IServerBrowserPingCache *CreateServerBrowserPingCache(IConsole *pConsole, IStorage *pStorage);

45
src/engine/favorites.h Normal file
View file

@ -0,0 +1,45 @@
#ifndef ENGINE_FAVORITES_H
#define ENGINE_FAVORITES_H
#include <memory>
#include <base/types.h>
#include <engine/shared/protocol.h>
#include "kernel.h"
class IConfigManager;
class IFavorites : public IInterface
{
MACRO_INTERFACE("favorites", 0)
protected:
virtual void OnConfigSave(IConfigManager *pConfigManager) = 0;
public:
class CEntry
{
public:
int m_NumAddrs;
NETADDR m_aAddrs[MAX_SERVER_ADDRESSES];
bool m_AllowPing;
};
virtual ~IFavorites() {}
virtual TRISTATE IsFavorite(const NETADDR *pAddrs, int NumAddrs) const = 0;
// Only considers the addresses that are actually favorites.
virtual TRISTATE IsPingAllowed(const NETADDR *pAddrs, int NumAddrs) const = 0;
virtual void Add(const NETADDR *pAddrs, int NumAddrs) = 0;
// Only considers the addresses that are actually favorites.
virtual void AllowPing(const NETADDR *pAddrs, int NumAddrs, bool AllowPing) = 0;
virtual void Remove(const NETADDR *pAddrs, int NumAddrs) = 0;
virtual void AllEntries(const CEntry **ppEntries, int *pNumEntries) = 0;
// Pass the `IFavorites` instance as callback.
static void ConfigSaveCallback(IConfigManager *pConfigManager, void *pUserData);
};
std::unique_ptr<IFavorites> CreateFavorites();
#endif // ENGINE_FAVORITES_H

View file

@ -3,6 +3,7 @@
#ifndef ENGINE_SERVERBROWSER_H
#define ENGINE_SERVERBROWSER_H
#include <base/types.h>
#include <engine/map.h>
#include <engine/shared/protocol.h>
@ -50,7 +51,8 @@ public:
uint64_t m_ReceivedPackets;
int m_NumReceivedClients;
NETADDR m_NetAddr;
int m_NumAddresses;
NETADDR m_aAddresses[MAX_SERVER_ADDRESSES];
int m_QuickSearchHit;
int m_FriendState;
@ -60,7 +62,8 @@ public:
int m_MaxPlayers;
int m_NumPlayers;
int m_Flags;
bool m_Favorite;
TRISTATE m_Favorite;
TRISTATE m_FavoriteAllowPing;
bool m_Official;
int m_Location;
bool m_LatencyIsEstimated;
@ -72,7 +75,7 @@ public:
int m_MapCrc;
int m_MapSize;
char m_aVersion[32];
char m_aAddress[NETADDR_MAXSTRSIZE];
char m_aAddress[MAX_SERVER_ADDRESSES * NETADDR_MAXSTRSIZE];
CClient m_aClients[SERVERINFO_MAX_CLIENTS];
mutable int m_NumFilteredPlayers;
@ -151,13 +154,6 @@ public:
virtual int NumSortedServers() const = 0;
virtual const CServerInfo *SortedGet(int Index) const = 0;
virtual bool GotInfo(const NETADDR &Addr) const = 0;
virtual bool IsFavorite(const NETADDR &Addr) const = 0;
virtual bool IsFavoritePingAllowed(const NETADDR &Addr) const = 0;
virtual void AddFavorite(const NETADDR &Addr) = 0;
virtual void FavoriteAllowPing(const NETADDR &Addr, bool AllowPing) = 0;
virtual void RemoveFavorite(const NETADDR &Addr) = 0;
virtual int NumCountries(int Network) = 0;
virtual int GetCountryFlag(int Network, int Index) = 0;
virtual const char *GetCountryName(int Network, int Index) = 0;

View file

@ -82,6 +82,7 @@ enum
SERVERINFO_LEVEL_MIN = 0,
SERVERINFO_LEVEL_MAX = 2,
MAX_SERVER_ADDRESSES = 16,
SERVERINFO_MAX_CLIENTS = 128,
MAX_CLIENTS = 64,
VANILLA_MAX_CLIENTS = 16,

View file

@ -15,6 +15,7 @@ class CConfig *CComponent::Config() const { return m_pClient->Config(); }
class IConsole *CComponent::Console() const { return m_pClient->Console(); }
class IDemoPlayer *CComponent::DemoPlayer() const { return m_pClient->DemoPlayer(); }
class IDemoRecorder *CComponent::DemoRecorder(int Recorder) const { return m_pClient->DemoRecorder(Recorder); }
class IFavorites *CComponent::Favorites() const { return m_pClient->Favorites(); }
class IServerBrowser *CComponent::ServerBrowser() const { return m_pClient->ServerBrowser(); }
class CLayers *CComponent::Layers() const { return m_pClient->Layers(); }
class CCollision *CComponent::Collision() const { return m_pClient->Collision(); }

View file

@ -80,6 +80,7 @@ protected:
* @see RECORDER_REPLAYS
*/
class IDemoRecorder *DemoRecorder(int Recorder) const;
class IFavorites *Favorites() const;
/**
* Get the server browser interface.
*/

View file

@ -2663,19 +2663,20 @@ bool CMenus::CheckHotKey(int Key) const
Input()->KeyIsPressed(Key) && m_pClient->m_GameConsole.IsClosed();
}
int CMenus::DoButton_CheckBox_DontCare(const void *pID, const char *pText, int Checked, const CUIRect *pRect)
int CMenus::DoButton_CheckBox_Tristate(const void *pID, const char *pText, TRISTATE Checked, const CUIRect *pRect)
{
switch(Checked)
{
case 0:
case TRISTATE::NONE:
return DoButton_CheckBox_Common(pID, pText, "", pRect);
case 1:
return DoButton_CheckBox_Common(pID, pText, "X", pRect);
case 2:
case TRISTATE::SOME:
return DoButton_CheckBox_Common(pID, pText, "O", pRect);
case TRISTATE::ALL:
return DoButton_CheckBox_Common(pID, pText, "X", pRect);
default:
return DoButton_CheckBox_Common(pID, pText, "", pRect);
dbg_assert(false, "invalid tristate");
}
dbg_break();
}
int CMenus::MenuImageScan(const char *pName, int IsDir, int DirType, void *pUser)

View file

@ -3,6 +3,7 @@
#ifndef GAME_CLIENT_COMPONENTS_MENUS_H
#define GAME_CLIENT_COMPONENTS_MENUS_H
#include <base/types.h>
#include <base/vmath.h>
#include <chrono>
@ -631,7 +632,7 @@ public:
SUIAnimator m_aAnimatorsSettingsTab[SETTINGS_LENGTH];
// DDRace
int DoButton_CheckBox_DontCare(const void *pID, const char *pText, int Checked, const CUIRect *pRect);
int DoButton_CheckBox_Tristate(const void *pID, const char *pText, TRISTATE Checked, const CUIRect *pRect);
std::vector<CDemoItem> m_vDemos;
void DemolistPopulate();
bool m_Dummy;

View file

@ -1,5 +1,8 @@
/* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */
/* If you are missing that file, acquire a complete release at teeworlds.com. */
#include <base/log.h>
#include <engine/favorites.h>
#include <engine/friends.h>
#include <engine/keys.h>
#include <engine/serverbrowser.h>
@ -330,7 +333,7 @@ void CMenus::RenderServerbrowserServerList(CUIRect View)
}
else if(ID == COL_FLAG_FAV)
{
if(pItem->m_Favorite)
if(pItem->m_Favorite != TRISTATE::NONE)
{
RenderBrowserIcons(*pItem->m_pUIElement->Get(gs_OffsetColFav + 0), &Button, {0.94f, 0.4f, 0.4f, 1}, TextRender()->DefaultTextOutlineColor(), "\xEF\x80\x84", TEXTALIGN_CENTER);
}
@ -1072,28 +1075,29 @@ void CMenus::RenderServerbrowserServerDetail(CUIRect View)
ButtonAddFav.VSplitLeft(5.0f, 0, &ButtonAddFav);
static int s_AddFavButton = 0;
static int s_LeakIpButton = 0;
if(DoButton_CheckBox(&s_AddFavButton, Localize("Favorite"), pSelectedServer->m_Favorite, &ButtonAddFav))
if(DoButton_CheckBox_Tristate(&s_AddFavButton, Localize("Favorite"), pSelectedServer->m_Favorite, &ButtonAddFav))
{
if(pSelectedServer->m_Favorite)
if(pSelectedServer->m_Favorite != TRISTATE::NONE)
{
ServerBrowser()->RemoveFavorite(pSelectedServer->m_NetAddr);
Favorites()->Remove(pSelectedServer->m_aAddresses, pSelectedServer->m_NumAddresses);
}
else
{
ServerBrowser()->AddFavorite(pSelectedServer->m_NetAddr);
Favorites()->Add(pSelectedServer->m_aAddresses, pSelectedServer->m_NumAddresses);
if(g_Config.m_UiPage == PAGE_LAN)
{
ServerBrowser()->FavoriteAllowPing(pSelectedServer->m_NetAddr, true);
Favorites()->AllowPing(pSelectedServer->m_aAddresses, pSelectedServer->m_NumAddresses, true);
}
}
Client()->ServerBrowserUpdate();
}
if(pSelectedServer->m_Favorite)
if(pSelectedServer->m_Favorite != TRISTATE::NONE)
{
bool IpLeak = ServerBrowser()->IsFavoritePingAllowed(pSelectedServer->m_NetAddr);
if(DoButton_CheckBox(&s_LeakIpButton, Localize("Leak IP"), IpLeak, &ButtonLeakIp))
if(DoButton_CheckBox_Tristate(&s_LeakIpButton, Localize("Leak IP"), pSelectedServer->m_FavoriteAllowPing, &ButtonLeakIp))
{
ServerBrowser()->FavoriteAllowPing(pSelectedServer->m_NetAddr, !IpLeak);
Favorites()->AllowPing(pSelectedServer->m_aAddresses, pSelectedServer->m_NumAddresses, pSelectedServer->m_FavoriteAllowPing == TRISTATE::NONE);
}
Client()->ServerBrowserUpdate();
}
}

View file

@ -4,6 +4,7 @@
#include <base/system.h>
#include <engine/demo.h>
#include <engine/favorites.h>
#include <engine/friends.h>
#include <engine/ghost.h>
#include <engine/graphics.h>
@ -402,15 +403,16 @@ void CMenus::RenderServerInfo(CUIRect MainView)
{
CUIRect Button;
int IsFavorite = ServerBrowser()->IsFavorite(CurrentServerInfo.m_NetAddr);
NETADDR ServerAddr = Client()->ServerAddress();
TRISTATE IsFavorite = Favorites()->IsFavorite(&ServerAddr, 1);
ServerInfo.HSplitBottom(20.0f, &ServerInfo, &Button);
static int s_AddFavButton = 0;
if(DoButton_CheckBox(&s_AddFavButton, Localize("Favorite"), IsFavorite, &Button))
if(DoButton_CheckBox(&s_AddFavButton, Localize("Favorite"), IsFavorite != TRISTATE::NONE, &Button))
{
if(IsFavorite)
ServerBrowser()->RemoveFavorite(CurrentServerInfo.m_NetAddr);
if(IsFavorite != TRISTATE::NONE)
Favorites()->Remove(&ServerAddr, 1);
else
ServerBrowser()->AddFavorite(CurrentServerInfo.m_NetAddr);
Favorites()->Add(&ServerAddr, 1);
}
}

View file

@ -8,6 +8,7 @@
#include <engine/demo.h>
#include <engine/editor.h>
#include <engine/engine.h>
#include <engine/favorites.h>
#include <engine/friends.h>
#include <engine/graphics.h>
#include <engine/map.h>
@ -91,6 +92,7 @@ void CGameClient::OnConsoleInit()
m_pDemoPlayer = Kernel()->RequestInterface<IDemoPlayer>();
m_pServerBrowser = Kernel()->RequestInterface<IServerBrowser>();
m_pEditor = Kernel()->RequestInterface<IEditor>();
m_pFavorites = Kernel()->RequestInterface<IFavorites>();
m_pFriends = Kernel()->RequestInterface<IFriends>();
m_pFoes = Client()->Foes();
#if defined(CONF_AUTOUPDATE)

View file

@ -165,6 +165,7 @@ private:
class IConsole *m_pConsole;
class IStorage *m_pStorage;
class IDemoPlayer *m_pDemoPlayer;
class IFavorites *m_pFavorites;
class IServerBrowser *m_pServerBrowser;
class IEditor *m_pEditor;
class IFriends *m_pFriends;
@ -218,6 +219,7 @@ public:
class ITextRender *TextRender() const { return m_pTextRender; }
class IDemoPlayer *DemoPlayer() const { return m_pDemoPlayer; }
class IDemoRecorder *DemoRecorder(int Recorder) const { return Client()->DemoRecorder(Recorder); }
class IFavorites *Favorites() const { return m_pFavorites; }
class IServerBrowser *ServerBrowser() const { return m_pServerBrowser; }
class CRenderTools *RenderTools() { return &m_RenderTools; }
class CLayers *Layers() { return &m_Layers; }

View file

@ -374,11 +374,12 @@ int CEditorMap::Save(class IStorage *pStorage, const char *pFileName)
{
CServerInfo CurrentServerInfo;
m_pEditor->Client()->GetServerInfo(&CurrentServerInfo);
const unsigned char aIPv4Localhost[16] = {127, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
const unsigned char aIPv6Localhost[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1};
NETADDR ServerAddr = m_pEditor->Client()->ServerAddress();
const unsigned char aIpv4Localhost[16] = {127, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
const unsigned char aIpv6Localhost[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1};
// and if we're on localhost
if(!mem_comp(CurrentServerInfo.m_NetAddr.ip, aIPv4Localhost, sizeof(aIPv4Localhost)) || !mem_comp(CurrentServerInfo.m_NetAddr.ip, aIPv6Localhost, sizeof(aIPv6Localhost)))
if(!mem_comp(ServerAddr.ip, aIpv4Localhost, sizeof(aIpv4Localhost)) || !mem_comp(ServerAddr.ip, aIpv6Localhost, sizeof(aIpv6Localhost)))
{
char aMapName[128];
IStorage::StripPathAndExtension(pFileName, aMapName, sizeof(aMapName));

View file

@ -126,7 +126,7 @@ MACRO_CONFIG_INT(ClFatSkins, cl_fat_skins, 0, 0, 1, CFGFLAG_CLIENT | CFGFLAG_SAV
MACRO_CONFIG_INT(UiPage, ui_page, 9, 6, 10, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Interface page")
MACRO_CONFIG_INT(UiSettingsPage, ui_settings_page, 0, 0, 9, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Interface settings page")
MACRO_CONFIG_INT(UiToolboxPage, ui_toolbox_page, 0, 0, 2, CFGFLAG_CLIENT | CFGFLAG_SAVE, "Toolbox page")
MACRO_CONFIG_STR(UiServerAddress, ui_server_address, 64, "localhost:8303", CFGFLAG_CLIENT | CFGFLAG_SAVE | CFGFLAG_INSENSITIVE, "Interface server address")
MACRO_CONFIG_STR(UiServerAddress, ui_server_address, 1024, "localhost:8303", CFGFLAG_CLIENT | CFGFLAG_SAVE | CFGFLAG_INSENSITIVE, "Interface server address")
MACRO_CONFIG_INT(UiMousesens, ui_mousesens, 200, 1, 100000, CFGFLAG_SAVE | CFGFLAG_CLIENT, "Mouse sensitivity for menus/editor")
MACRO_CONFIG_INT(UiControllerSens, ui_controller_sens, 100, 1, 100000, CFGFLAG_SAVE | CFGFLAG_CLIENT, "Controller sensitivity for menus/editor")

View file

@ -17,87 +17,83 @@ TEST(ServerBrowser, PingCache)
auto pStorage = std::unique_ptr<IStorage>(Info.CreateTestStorage());
auto pPingCache = std::unique_ptr<IServerBrowserPingCache>(CreateServerBrowserPingCache(pConsole.get(), pStorage.get()));
const IServerBrowserPingCache::CEntry *pEntries;
int NumEntries;
NETADDR Localhost4, Localhost6, OtherLocalhost4, OtherLocalhost6;
ASSERT_FALSE(net_addr_from_str(&Localhost4, "127.0.0.1:8303"));
ASSERT_FALSE(net_addr_from_str(&Localhost6, "[::1]:8304"));
ASSERT_FALSE(net_addr_from_str(&OtherLocalhost4, "127.0.0.1:8305"));
ASSERT_FALSE(net_addr_from_str(&OtherLocalhost6, "[::1]:8306"));
EXPECT_LT(net_addr_comp(&Localhost4, &Localhost6), 0);
NETADDR aLocalhostBoth[2] = {Localhost4, Localhost6};
pPingCache->GetPingCache(&pEntries, &NumEntries);
EXPECT_EQ(NumEntries, 0);
EXPECT_EQ(pPingCache->NumEntries(), 0);
EXPECT_EQ(pPingCache->GetPing(&Localhost4, 1), -1);
EXPECT_EQ(pPingCache->GetPing(&Localhost6, 1), -1);
EXPECT_EQ(pPingCache->GetPing(aLocalhostBoth, 2), -1);
EXPECT_EQ(pPingCache->GetPing(&OtherLocalhost4, 1), -1);
EXPECT_EQ(pPingCache->GetPing(&OtherLocalhost6, 1), -1);
pPingCache->Load();
pPingCache->GetPingCache(&pEntries, &NumEntries);
EXPECT_EQ(NumEntries, 0);
NETADDR Localhost4, Localhost4Tw, Localhost6, Localhost6Tw;
ASSERT_FALSE(net_addr_from_str(&Localhost4, "127.0.0.1"));
ASSERT_FALSE(net_addr_from_str(&Localhost4Tw, "127.0.0.1:8303"));
ASSERT_FALSE(net_addr_from_str(&Localhost6, "[::1]"));
ASSERT_FALSE(net_addr_from_str(&Localhost6Tw, "[::1]:8304"));
EXPECT_LT(net_addr_comp(&Localhost4, &Localhost6), 0);
EXPECT_EQ(pPingCache->NumEntries(), 0);
EXPECT_EQ(pPingCache->GetPing(&Localhost4, 1), -1);
EXPECT_EQ(pPingCache->GetPing(&Localhost6, 1), -1);
EXPECT_EQ(pPingCache->GetPing(aLocalhostBoth, 2), -1);
EXPECT_EQ(pPingCache->GetPing(&OtherLocalhost4, 1), -1);
EXPECT_EQ(pPingCache->GetPing(&OtherLocalhost6, 1), -1);
// Newer pings overwrite older.
pPingCache->CachePing(Localhost4Tw, 123);
pPingCache->CachePing(Localhost4Tw, 234);
pPingCache->CachePing(Localhost4Tw, 345);
pPingCache->CachePing(Localhost4Tw, 456);
pPingCache->CachePing(Localhost4Tw, 567);
pPingCache->CachePing(Localhost4Tw, 678);
pPingCache->CachePing(Localhost4Tw, 789);
pPingCache->CachePing(Localhost4Tw, 890);
pPingCache->CachePing(Localhost4Tw, 901);
pPingCache->CachePing(Localhost4Tw, 135);
pPingCache->CachePing(Localhost4Tw, 246);
pPingCache->CachePing(Localhost4Tw, 357);
pPingCache->CachePing(Localhost4Tw, 468);
pPingCache->CachePing(Localhost4Tw, 579);
pPingCache->CachePing(Localhost4Tw, 680);
pPingCache->CachePing(Localhost4Tw, 791);
pPingCache->CachePing(Localhost4Tw, 802);
pPingCache->CachePing(Localhost4Tw, 913);
pPingCache->CachePing(Localhost4, 123);
pPingCache->CachePing(Localhost4, 234);
pPingCache->CachePing(Localhost4, 345);
pPingCache->CachePing(Localhost4, 456);
pPingCache->CachePing(Localhost4, 567);
pPingCache->CachePing(Localhost4, 678);
pPingCache->CachePing(Localhost4, 789);
pPingCache->CachePing(Localhost4, 890);
pPingCache->CachePing(Localhost4, 901);
pPingCache->CachePing(Localhost4, 135);
pPingCache->CachePing(Localhost4, 246);
pPingCache->CachePing(Localhost4, 357);
pPingCache->CachePing(Localhost4, 468);
pPingCache->CachePing(Localhost4, 579);
pPingCache->CachePing(Localhost4, 680);
pPingCache->CachePing(Localhost4, 791);
pPingCache->CachePing(Localhost4, 802);
pPingCache->CachePing(Localhost4, 913);
pPingCache->GetPingCache(&pEntries, &NumEntries);
EXPECT_EQ(NumEntries, 1);
if(NumEntries >= 1)
{
EXPECT_TRUE(net_addr_comp(&pEntries[0].m_Addr, &Localhost4) == 0);
EXPECT_EQ(pEntries[0].m_Ping, 913);
}
EXPECT_EQ(pPingCache->NumEntries(), 1);
EXPECT_EQ(pPingCache->GetPing(&Localhost4, 1), 913);
EXPECT_EQ(pPingCache->GetPing(&Localhost6, 1), -1);
EXPECT_EQ(pPingCache->GetPing(aLocalhostBoth, 2), 913);
EXPECT_EQ(pPingCache->GetPing(&OtherLocalhost4, 1), 913);
EXPECT_EQ(pPingCache->GetPing(&OtherLocalhost6, 1), -1);
pPingCache->CachePing(Localhost4Tw, 234);
pPingCache->CachePing(Localhost6Tw, 345);
pPingCache->GetPingCache(&pEntries, &NumEntries);
EXPECT_EQ(NumEntries, 2);
if(NumEntries >= 2)
{
EXPECT_TRUE(net_addr_comp(&pEntries[0].m_Addr, &Localhost4) == 0);
EXPECT_TRUE(net_addr_comp(&pEntries[1].m_Addr, &Localhost6) == 0);
EXPECT_EQ(pEntries[0].m_Ping, 234);
EXPECT_EQ(pEntries[1].m_Ping, 345);
}
pPingCache->CachePing(Localhost4, 234);
pPingCache->CachePing(Localhost6, 345);
EXPECT_EQ(pPingCache->NumEntries(), 2);
EXPECT_EQ(pPingCache->GetPing(&Localhost4, 1), 234);
EXPECT_EQ(pPingCache->GetPing(&Localhost6, 1), 345);
EXPECT_EQ(pPingCache->GetPing(aLocalhostBoth, 2), 234);
EXPECT_EQ(pPingCache->GetPing(&OtherLocalhost4, 1), 234);
EXPECT_EQ(pPingCache->GetPing(&OtherLocalhost6, 1), 345);
// Port doesn't matter for overwriting.
pPingCache->CachePing(Localhost4, 1337);
pPingCache->GetPingCache(&pEntries, &NumEntries);
EXPECT_EQ(NumEntries, 2);
if(NumEntries >= 2)
{
EXPECT_TRUE(net_addr_comp(&pEntries[0].m_Addr, &Localhost4) == 0);
EXPECT_TRUE(net_addr_comp(&pEntries[1].m_Addr, &Localhost6) == 0);
EXPECT_EQ(pEntries[0].m_Ping, 1337);
EXPECT_EQ(pEntries[1].m_Ping, 345);
}
EXPECT_EQ(pPingCache->NumEntries(), 2);
EXPECT_EQ(pPingCache->GetPing(&Localhost4, 1), 1337);
EXPECT_EQ(pPingCache->GetPing(&Localhost6, 1), 345);
EXPECT_EQ(pPingCache->GetPing(aLocalhostBoth, 2), 345);
EXPECT_EQ(pPingCache->GetPing(&OtherLocalhost4, 1), 1337);
EXPECT_EQ(pPingCache->GetPing(&OtherLocalhost6, 1), 345);
pPingCache.reset(CreateServerBrowserPingCache(pConsole.get(), pStorage.get()));
// Persistence.
pPingCache->Load();
pPingCache->GetPingCache(&pEntries, &NumEntries);
EXPECT_EQ(NumEntries, 2);
if(NumEntries >= 2)
{
EXPECT_TRUE(net_addr_comp(&pEntries[0].m_Addr, &Localhost4) == 0);
EXPECT_TRUE(net_addr_comp(&pEntries[1].m_Addr, &Localhost6) == 0);
EXPECT_EQ(pEntries[0].m_Ping, 1337);
EXPECT_EQ(pEntries[1].m_Ping, 345);
}
EXPECT_EQ(pPingCache->NumEntries(), 2);
EXPECT_EQ(pPingCache->GetPing(&Localhost4, 1), 1337);
EXPECT_EQ(pPingCache->GetPing(&Localhost6, 1), 345);
EXPECT_EQ(pPingCache->GetPing(aLocalhostBoth, 2), 345);
EXPECT_EQ(pPingCache->GetPing(&OtherLocalhost4, 1), 1337);
EXPECT_EQ(pPingCache->GetPing(&OtherLocalhost6, 1), 345);
}