ddnet/src/engine/client/favorites.cpp

250 lines
5.3 KiB
C++
Raw Normal View History

#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>();
}