2022-05-23 18:16:18 +00:00
|
|
|
#include <engine/favorites.h>
|
|
|
|
#include <engine/shared/config.h>
|
|
|
|
#include <engine/shared/protocol.h>
|
|
|
|
|
2022-09-27 11:39:03 +00:00
|
|
|
#include <algorithm>
|
2022-05-23 18:16:18 +00:00
|
|
|
#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:
|
2022-07-18 19:43:59 +00:00
|
|
|
std::vector<CEntry> m_vEntries;
|
2022-05-23 18:16:18 +00:00
|
|
|
std::unordered_map<NETADDR, int> m_ByAddr;
|
|
|
|
|
|
|
|
CEntry *Entry(const NETADDR &Addr);
|
|
|
|
const CEntry *Entry(const NETADDR &Addr) const;
|
2022-07-18 19:43:59 +00:00
|
|
|
// `pEntry` must come from the `m_vEntries` vector.
|
2022-05-23 18:16:18 +00:00
|
|
|
void RemoveEntry(CEntry *pEntry);
|
|
|
|
};
|
|
|
|
|
|
|
|
void CFavorites::OnConfigSave(IConfigManager *pConfigManager)
|
|
|
|
{
|
2022-07-18 19:43:59 +00:00
|
|
|
for(const auto &Entry : m_vEntries)
|
2022-05-23 18:16:18 +00:00
|
|
|
{
|
|
|
|
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];
|
2022-07-18 19:43:59 +00:00
|
|
|
m_ByAddr[pAddrs[i]] = m_vEntries.size();
|
2022-05-23 18:16:18 +00:00
|
|
|
}
|
|
|
|
NewEntry.m_AllowPing = false;
|
2022-07-18 19:43:59 +00:00
|
|
|
m_vEntries.push_back(NewEntry);
|
2022-05-23 18:16:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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)
|
|
|
|
{
|
2022-07-18 19:43:59 +00:00
|
|
|
*ppEntries = m_vEntries.data();
|
|
|
|
*pNumEntries = m_vEntries.size();
|
2022-05-23 18:16:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
CFavorites::CEntry *CFavorites::Entry(const NETADDR &Addr)
|
|
|
|
{
|
|
|
|
auto Entry = m_ByAddr.find(Addr);
|
|
|
|
if(Entry == m_ByAddr.end())
|
|
|
|
{
|
|
|
|
return nullptr;
|
|
|
|
}
|
2022-07-18 19:43:59 +00:00
|
|
|
return &m_vEntries[Entry->second];
|
2022-05-23 18:16:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
const CFavorites::CEntry *CFavorites::Entry(const NETADDR &Addr) const
|
|
|
|
{
|
|
|
|
auto Entry = m_ByAddr.find(Addr);
|
|
|
|
if(Entry == m_ByAddr.end())
|
|
|
|
{
|
|
|
|
return nullptr;
|
|
|
|
}
|
2022-07-18 19:43:59 +00:00
|
|
|
return &m_vEntries[Entry->second];
|
2022-05-23 18:16:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void CFavorites::RemoveEntry(CEntry *pEntry)
|
|
|
|
{
|
|
|
|
// Replace the entry
|
2022-07-18 19:43:59 +00:00
|
|
|
int Index = pEntry - m_vEntries.data();
|
|
|
|
*pEntry = m_vEntries[m_vEntries.size() - 1];
|
|
|
|
m_vEntries.pop_back();
|
|
|
|
if(Index != (int)m_vEntries.size())
|
2022-05-23 18:16:18 +00:00
|
|
|
{
|
|
|
|
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>();
|
|
|
|
}
|