2021-04-17 14:05:24 +00:00
# include "serverbrowser_ping_cache.h"
# include <engine/console.h>
# include <engine/sqlite.h>
# include <sqlite3.h>
# include <algorithm>
# include <vector>
class CServerBrowserPingCache : public IServerBrowserPingCache
{
public :
CServerBrowserPingCache ( IConsole * pConsole , IStorage * pStorage ) ;
2022-02-14 23:17:57 +00:00
virtual ~ CServerBrowserPingCache ( ) = default ;
2021-04-17 14:05:24 +00:00
2022-05-17 18:33:27 +00:00
void Load ( ) override ;
2021-04-17 14:05:24 +00:00
2022-05-17 18:33:27 +00:00
void CachePing ( NETADDR Addr , int Ping ) override ;
void GetPingCache ( const CEntry * * ppEntries , int * pNumEntries ) override ;
2021-04-17 14:05:24 +00:00
private :
IConsole * m_pConsole ;
CSqlite m_pDisk ;
CSqliteStmt m_pLoadStmt ;
CSqliteStmt m_pStoreStmt ;
2022-06-11 19:38:18 +00:00
std : : vector < CEntry > m_vEntries ;
std : : vector < CEntry > m_vNewEntries ;
2021-04-17 14:05:24 +00:00
} ;
CServerBrowserPingCache : : CServerBrowserPingCache ( IConsole * pConsole , IStorage * pStorage ) :
m_pConsole ( pConsole )
{
2021-05-27 19:45:12 +00:00
m_pDisk = SqliteOpen ( pConsole , pStorage , " ddnet-cache.sqlite3 " ) ;
2021-04-17 14:05:24 +00:00
if ( ! m_pDisk )
{
2021-05-27 19:45:12 +00:00
pConsole - > Print ( IConsole : : OUTPUT_LEVEL_STANDARD , " serverbrowse_ping_cache " , " failed to open ddnet-cache.sqlite3 " ) ;
2021-04-17 14:05:24 +00:00
return ;
}
sqlite3 * pSqlite = m_pDisk . get ( ) ;
static const char TABLE [ ] = " CREATE TABLE IF NOT EXISTS server_pings (ip_address TEXT PRIMARY KEY NOT NULL, ping INTEGER NOT NULL, utc_timestamp TEXT NOT NULL) " ;
if ( SQLITE_HANDLE_ERROR ( sqlite3_exec ( pSqlite , TABLE , nullptr , nullptr , nullptr ) ) )
{
m_pDisk = nullptr ;
pConsole - > Print ( IConsole : : OUTPUT_LEVEL_STANDARD , " serverbrowse_ping_cache " , " failed to create server_pings table " ) ;
return ;
}
m_pLoadStmt = SqlitePrepare ( pConsole , pSqlite , " SELECT ip_address, ping FROM server_pings " ) ;
m_pStoreStmt = SqlitePrepare ( pConsole , pSqlite , " INSERT OR REPLACE INTO server_pings (ip_address, ping, utc_timestamp) VALUES (?, ?, datetime('now')) " ) ;
}
void CServerBrowserPingCache : : Load ( )
{
if ( m_pDisk )
{
2022-06-11 19:38:18 +00:00
int PrevNewEntriesSize = m_vNewEntries . size ( ) ;
2021-04-17 14:05:24 +00:00
sqlite3 * pSqlite = m_pDisk . get ( ) ;
IConsole * pConsole = m_pConsole ;
bool Error = false ;
bool WarnedForBadAddress = false ;
Error = Error | | ! m_pLoadStmt ;
while ( ! Error )
{
int StepResult = SQLITE_HANDLE_ERROR ( sqlite3_step ( m_pLoadStmt . get ( ) ) ) ;
if ( StepResult = = SQLITE_DONE )
{
break ;
}
else if ( StepResult = = SQLITE_ROW )
{
const char * pIpAddress = ( const char * ) sqlite3_column_text ( m_pLoadStmt . get ( ) , 0 ) ;
int Ping = sqlite3_column_int ( m_pLoadStmt . get ( ) , 1 ) ;
NETADDR Addr ;
if ( net_addr_from_str ( & Addr , pIpAddress ) )
{
if ( ! WarnedForBadAddress )
{
char aBuf [ 64 ] ;
str_format ( aBuf , sizeof ( aBuf ) , " invalid address: %s " , pIpAddress ) ;
pConsole - > Print ( IConsole : : OUTPUT_LEVEL_STANDARD , " serverbrowse_ping_cache " , aBuf ) ;
WarnedForBadAddress = true ;
}
continue ;
}
2022-06-11 19:38:18 +00:00
m_vNewEntries . push_back ( CEntry { Addr , Ping } ) ;
2021-04-17 14:05:24 +00:00
}
else
{
Error = true ;
}
}
if ( Error )
{
pConsole - > Print ( IConsole : : OUTPUT_LEVEL_STANDARD , " serverbrowse_ping_cache " , " failed to load ping cache " ) ;
2022-06-11 19:38:18 +00:00
m_vNewEntries . resize ( PrevNewEntriesSize ) ;
2021-04-17 14:05:24 +00:00
}
}
}
void CServerBrowserPingCache : : CachePing ( NETADDR Addr , int Ping )
{
Addr . port = 0 ;
2022-06-11 19:38:18 +00:00
m_vNewEntries . push_back ( CEntry { Addr , Ping } ) ;
2021-04-17 14:05:24 +00:00
if ( m_pDisk )
{
sqlite3 * pSqlite = m_pDisk . get ( ) ;
IConsole * pConsole = m_pConsole ;
char aAddr [ NETADDR_MAXSTRSIZE ] ;
net_addr_str ( & Addr , aAddr , sizeof ( aAddr ) , false ) ;
bool Error = false ;
Error = Error | | ! m_pStoreStmt ;
Error = Error | | SQLITE_HANDLE_ERROR ( sqlite3_reset ( m_pStoreStmt . get ( ) ) ) ! = SQLITE_OK ;
Error = Error | | SQLITE_HANDLE_ERROR ( sqlite3_bind_text ( m_pStoreStmt . get ( ) , 1 , aAddr , - 1 , SQLITE_STATIC ) ) ! = SQLITE_OK ;
Error = Error | | SQLITE_HANDLE_ERROR ( sqlite3_bind_int ( m_pStoreStmt . get ( ) , 2 , Ping ) ) ! = SQLITE_OK ;
Error = Error | | SQLITE_HANDLE_ERROR ( sqlite3_step ( m_pStoreStmt . get ( ) ) ) ! = SQLITE_DONE ;
if ( Error )
{
pConsole - > Print ( IConsole : : OUTPUT_LEVEL_STANDARD , " serverbrowse_ping_cache " , " failed to store ping " ) ;
}
}
}
void CServerBrowserPingCache : : GetPingCache ( const CEntry * * ppEntries , int * pNumEntries )
{
2022-06-11 19:38:18 +00:00
if ( ! m_vNewEntries . empty ( ) )
2021-04-17 14:05:24 +00:00
{
class CAddrComparer
{
public :
bool operator ( ) ( const CEntry & a , const CEntry & b )
{
return net_addr_comp ( & a . m_Addr , & b . m_Addr ) < 0 ;
}
} ;
2022-06-15 17:34:41 +00:00
std : : vector < CEntry > vOldEntries ;
std : : swap ( m_vEntries , vOldEntries ) ;
2021-04-17 14:05:24 +00:00
// Remove duplicates, keeping newer ones.
2022-06-11 19:38:18 +00:00
std : : stable_sort ( m_vNewEntries . begin ( ) , m_vNewEntries . end ( ) , CAddrComparer ( ) ) ;
2021-04-17 14:05:24 +00:00
{
unsigned To = 0 ;
2022-06-11 19:38:18 +00:00
for ( unsigned int From = 0 ; From < m_vNewEntries . size ( ) ; From + + )
2021-04-17 14:05:24 +00:00
{
if ( To < From )
{
2022-06-11 19:38:18 +00:00
m_vNewEntries [ To ] = m_vNewEntries [ From ] ;
2021-04-17 14:05:24 +00:00
}
2022-06-11 19:38:18 +00:00
if ( From + 1 > = m_vNewEntries . size ( ) | |
net_addr_comp ( & m_vNewEntries [ From ] . m_Addr , & m_vNewEntries [ From + 1 ] . m_Addr ) ! = 0 )
2021-04-17 14:05:24 +00:00
{
To + + ;
}
}
2022-06-11 19:38:18 +00:00
m_vNewEntries . resize ( To ) ;
2021-04-17 14:05:24 +00:00
}
// Only keep the new entries where there are duplicates.
2022-06-15 17:34:41 +00:00
m_vEntries . reserve ( m_vNewEntries . size ( ) + vOldEntries . size ( ) ) ;
2021-04-17 14:05:24 +00:00
{
unsigned i = 0 ;
unsigned j = 0 ;
2022-06-15 17:34:41 +00:00
while ( i < vOldEntries . size ( ) & & j < m_vNewEntries . size ( ) )
2021-04-17 14:05:24 +00:00
{
2022-06-15 17:34:41 +00:00
int Cmp = net_addr_comp ( & vOldEntries [ i ] . m_Addr , & m_vNewEntries [ j ] . m_Addr ) ;
2021-04-17 14:05:24 +00:00
if ( Cmp ! = 0 )
{
if ( Cmp < 0 )
{
2022-06-15 17:34:41 +00:00
m_vEntries . push_back ( vOldEntries [ i ] ) ;
2021-04-17 14:05:24 +00:00
i + + ;
}
else
{
2022-06-11 19:38:18 +00:00
m_vEntries . push_back ( m_vNewEntries [ j ] ) ;
2021-04-17 14:05:24 +00:00
j + + ;
}
}
else
{
// Ignore the old element if we have both.
i + + ;
}
}
// Add the remaining elements.
2022-06-15 17:34:41 +00:00
for ( ; i < vOldEntries . size ( ) ; i + + )
2021-04-17 14:05:24 +00:00
{
2022-06-15 17:34:41 +00:00
m_vEntries . push_back ( vOldEntries [ i ] ) ;
2021-04-17 14:05:24 +00:00
}
2022-06-11 19:38:18 +00:00
for ( ; j < m_vNewEntries . size ( ) ; j + + )
2021-04-17 14:05:24 +00:00
{
2022-06-11 19:38:18 +00:00
m_vEntries . push_back ( m_vNewEntries [ j ] ) ;
2021-04-17 14:05:24 +00:00
}
}
2022-06-11 19:38:18 +00:00
m_vNewEntries . clear ( ) ;
2021-04-17 14:05:24 +00:00
}
2022-06-11 19:38:18 +00:00
* ppEntries = m_vEntries . data ( ) ;
* pNumEntries = m_vEntries . size ( ) ;
2021-04-17 14:05:24 +00:00
}
IServerBrowserPingCache * CreateServerBrowserPingCache ( IConsole * pConsole , IStorage * pStorage )
{
return new CServerBrowserPingCache ( pConsole , pStorage ) ;
}