mirror of
https://github.com/ddnet/ddnet.git
synced 2024-11-05 23:58:19 +00:00
Add SQLite interface
This commit is contained in:
parent
ad21ec1269
commit
7c31a15c93
|
@ -337,6 +337,7 @@ endif()
|
||||||
find_package(Pnglite)
|
find_package(Pnglite)
|
||||||
find_package(PythonInterp 3)
|
find_package(PythonInterp 3)
|
||||||
find_package(SDL2)
|
find_package(SDL2)
|
||||||
|
find_package(SQLite3)
|
||||||
if(VIDEORECORDER)
|
if(VIDEORECORDER)
|
||||||
find_package(FFMPEG)
|
find_package(FFMPEG)
|
||||||
endif()
|
endif()
|
||||||
|
@ -417,6 +418,7 @@ endif()
|
||||||
show_dependency_status("Pnglite" PNGLITE)
|
show_dependency_status("Pnglite" PNGLITE)
|
||||||
show_dependency_status("PythonInterp" PYTHONINTERP)
|
show_dependency_status("PythonInterp" PYTHONINTERP)
|
||||||
show_dependency_status("SDL2" SDL2)
|
show_dependency_status("SDL2" SDL2)
|
||||||
|
show_dependency_status("SQLite3" SQLite3)
|
||||||
if(VIDEORECORDER)
|
if(VIDEORECORDER)
|
||||||
show_dependency_status("FFmpeg" FFMPEG)
|
show_dependency_status("FFmpeg" FFMPEG)
|
||||||
endif()
|
endif()
|
||||||
|
@ -432,6 +434,9 @@ endif()
|
||||||
if(NOT(PYTHONINTERP_FOUND))
|
if(NOT(PYTHONINTERP_FOUND))
|
||||||
message(SEND_ERROR "You must install Python to compile DDNet")
|
message(SEND_ERROR "You must install Python to compile DDNet")
|
||||||
endif()
|
endif()
|
||||||
|
if(NOT(SQLite3_FOUND))
|
||||||
|
message(SEND_ERROR "You must install SQLite3 to compile the DDNet server")
|
||||||
|
endif()
|
||||||
|
|
||||||
if(MYSQL AND NOT(MYSQL_FOUND))
|
if(MYSQL AND NOT(MYSQL_FOUND))
|
||||||
message(SEND_ERROR "You must install MySQL to compile the DDNet server with MySQL support")
|
message(SEND_ERROR "You must install MySQL to compile the DDNet server with MySQL support")
|
||||||
|
@ -1844,6 +1849,8 @@ set_src(ENGINE_SERVER GLOB_RECURSE src/engine/server
|
||||||
databases/connection_pool.h
|
databases/connection_pool.h
|
||||||
databases/mysql.cpp
|
databases/mysql.cpp
|
||||||
databases/mysql.h
|
databases/mysql.h
|
||||||
|
databases/sqlite.cpp
|
||||||
|
databases/sqlite.h
|
||||||
name_ban.cpp
|
name_ban.cpp
|
||||||
name_ban.h
|
name_ban.h
|
||||||
register.cpp
|
register.cpp
|
||||||
|
@ -1931,6 +1938,7 @@ endif()
|
||||||
set(LIBS_SERVER
|
set(LIBS_SERVER
|
||||||
${LIBS}
|
${LIBS}
|
||||||
${MYSQL_LIBRARIES}
|
${MYSQL_LIBRARIES}
|
||||||
|
${SQLite3_LIBRARIES}
|
||||||
${TARGET_ANTIBOT}
|
${TARGET_ANTIBOT}
|
||||||
${MINIUPNPC_LIBRARIES}
|
${MINIUPNPC_LIBRARIES}
|
||||||
# Add pthreads (on non-Windows) at the end, so that other libraries can depend
|
# Add pthreads (on non-Windows) at the end, so that other libraries can depend
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
#if defined(CONF_SQL)
|
#if defined(CONF_SQL)
|
||||||
#include <cppconn/exception.h>
|
#include <cppconn/exception.h>
|
||||||
#endif
|
#endif
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
// helper struct to hold thread data
|
// helper struct to hold thread data
|
||||||
struct CSqlExecData
|
struct CSqlExecData
|
||||||
|
@ -176,6 +177,10 @@ bool CDbConnectionPool::ExecSqlFunc(IDbConnection *pConnection, CSqlExecData *pD
|
||||||
dbg_msg("sql", "MySQL Error: %s", e.what());
|
dbg_msg("sql", "MySQL Error: %s", e.what());
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
catch (std::runtime_error &e)
|
||||||
|
{
|
||||||
|
dbg_msg("sql", "SQLite Error: %s", e.what());
|
||||||
|
}
|
||||||
catch (...)
|
catch (...)
|
||||||
{
|
{
|
||||||
dbg_msg("sql", "Unexpected exception caught");
|
dbg_msg("sql", "Unexpected exception caught");
|
||||||
|
|
186
src/engine/server/databases/sqlite.cpp
Normal file
186
src/engine/server/databases/sqlite.cpp
Normal file
|
@ -0,0 +1,186 @@
|
||||||
|
#include "sqlite.h"
|
||||||
|
|
||||||
|
#include <base/math.h>
|
||||||
|
#include <engine/shared/protocol.h>
|
||||||
|
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <sqlite3.h>
|
||||||
|
|
||||||
|
CSqliteConnection::CSqliteConnection(const char *pFilename, bool Setup) :
|
||||||
|
IDbConnection("record"),
|
||||||
|
m_Setup(Setup),
|
||||||
|
m_pDb(nullptr),
|
||||||
|
m_pStmt(nullptr),
|
||||||
|
m_Done(true),
|
||||||
|
m_InUse(false)
|
||||||
|
{
|
||||||
|
str_copy(m_aFilename, pFilename, sizeof(m_aFilename));
|
||||||
|
}
|
||||||
|
|
||||||
|
CSqliteConnection::~CSqliteConnection()
|
||||||
|
{
|
||||||
|
if(m_pStmt != nullptr)
|
||||||
|
sqlite3_finalize(m_pStmt);
|
||||||
|
sqlite3_close(m_pDb);
|
||||||
|
m_pDb = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
CSqliteConnection *CSqliteConnection::Copy()
|
||||||
|
{
|
||||||
|
return new CSqliteConnection(m_aFilename, m_Setup);
|
||||||
|
}
|
||||||
|
|
||||||
|
IDbConnection::Status CSqliteConnection::Connect()
|
||||||
|
{
|
||||||
|
if(m_InUse.exchange(true))
|
||||||
|
return Status::IN_USE;
|
||||||
|
|
||||||
|
if(m_pDb != nullptr)
|
||||||
|
return Status::SUCCESS;
|
||||||
|
|
||||||
|
int Result = sqlite3_open(m_aFilename, &m_pDb);
|
||||||
|
if(Result != SQLITE_OK)
|
||||||
|
{
|
||||||
|
dbg_msg("sql", "Can't open sqlite database: '%s'", sqlite3_errmsg(m_pDb));
|
||||||
|
return Status::ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
// wait for database to unlock so we don't have to handle SQLITE_BUSY errors
|
||||||
|
sqlite3_busy_timeout(m_pDb, -1);
|
||||||
|
|
||||||
|
if(m_Setup)
|
||||||
|
{
|
||||||
|
char aBuf[1024];
|
||||||
|
str_format(aBuf, sizeof(aBuf), m_pCreateRace, GetPrefix(), MAX_NAME_LENGTH);
|
||||||
|
if(!Execute(aBuf))
|
||||||
|
return Status::ERROR;
|
||||||
|
str_format(aBuf, sizeof(aBuf), m_pCreateTeamrace, GetPrefix(), MAX_NAME_LENGTH);
|
||||||
|
if(!Execute(aBuf))
|
||||||
|
return Status::ERROR;
|
||||||
|
str_format(aBuf, sizeof(aBuf), m_pCreateMaps, GetPrefix());
|
||||||
|
if(!Execute(aBuf))
|
||||||
|
return Status::ERROR;
|
||||||
|
str_format(aBuf, sizeof(aBuf), m_pCreateSaves, GetPrefix());
|
||||||
|
if(!Execute(aBuf))
|
||||||
|
return Status::ERROR;
|
||||||
|
str_format(aBuf, sizeof(aBuf), m_pCreatePoints, GetPrefix(), MAX_NAME_LENGTH);
|
||||||
|
if(!Execute(aBuf))
|
||||||
|
return Status::ERROR;
|
||||||
|
m_Setup = false;
|
||||||
|
}
|
||||||
|
return Status::SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSqliteConnection::Disconnect()
|
||||||
|
{
|
||||||
|
if(m_pStmt != nullptr)
|
||||||
|
sqlite3_finalize(m_pStmt);
|
||||||
|
m_pStmt = nullptr;
|
||||||
|
m_InUse.store(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSqliteConnection::Lock(const char *pTable)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSqliteConnection::Unlock()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSqliteConnection::PrepareStatement(const char *pStmt)
|
||||||
|
{
|
||||||
|
if(m_pStmt != nullptr)
|
||||||
|
sqlite3_finalize(m_pStmt);
|
||||||
|
m_pStmt = nullptr;
|
||||||
|
int Result = sqlite3_prepare_v2(
|
||||||
|
m_pDb,
|
||||||
|
pStmt,
|
||||||
|
-1, // pStmt can be any length
|
||||||
|
&m_pStmt,
|
||||||
|
NULL);
|
||||||
|
ExceptionOnError(Result);
|
||||||
|
m_Done = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSqliteConnection::BindString(int Idx, const char *pString)
|
||||||
|
{
|
||||||
|
int Result = sqlite3_bind_text(m_pStmt, Idx, pString, -1, NULL);
|
||||||
|
ExceptionOnError(Result);
|
||||||
|
m_Done = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSqliteConnection::BindInt(int Idx, int Value)
|
||||||
|
{
|
||||||
|
int Result = sqlite3_bind_int(m_pStmt, Idx, Value);
|
||||||
|
ExceptionOnError(Result);
|
||||||
|
m_Done = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CSqliteConnection::Step()
|
||||||
|
{
|
||||||
|
if(m_Done)
|
||||||
|
return false;
|
||||||
|
int Result = sqlite3_step(m_pStmt);
|
||||||
|
if(Result == SQLITE_ROW)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if(Result == SQLITE_DONE)
|
||||||
|
{
|
||||||
|
m_Done = true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ExceptionOnError(Result);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CSqliteConnection::IsNull(int Col) const
|
||||||
|
{
|
||||||
|
return sqlite3_column_type(m_pStmt, Col - 1) == SQLITE_NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
float CSqliteConnection::GetFloat(int Col) const
|
||||||
|
{
|
||||||
|
return (float)sqlite3_column_double(m_pStmt, Col - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
int CSqliteConnection::GetInt(int Col) const
|
||||||
|
{
|
||||||
|
return sqlite3_column_int(m_pStmt, Col - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSqliteConnection::GetString(int Col, char *pBuffer, int BufferSize) const
|
||||||
|
{
|
||||||
|
str_copy(pBuffer, (const char *)sqlite3_column_text(m_pStmt, Col - 1), BufferSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
int CSqliteConnection::GetBlob(int Col, unsigned char *pBuffer, int BufferSize) const
|
||||||
|
{
|
||||||
|
int Size = sqlite3_column_bytes(m_pStmt, Col - 1);
|
||||||
|
Size = minimum(Size, BufferSize);
|
||||||
|
mem_copy(pBuffer, sqlite3_column_blob(m_pStmt, Col - 1), Size);
|
||||||
|
return Size;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CSqliteConnection::Execute(const char *pQuery)
|
||||||
|
{
|
||||||
|
char *pErrorMsg;
|
||||||
|
int Result = sqlite3_exec(m_pDb, pQuery, NULL, NULL, &pErrorMsg);
|
||||||
|
if(Result != SQLITE_OK)
|
||||||
|
{
|
||||||
|
dbg_msg("sql", "error executing query: '%s'", pErrorMsg);
|
||||||
|
sqlite3_free(pErrorMsg);
|
||||||
|
}
|
||||||
|
return Result == SQLITE_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSqliteConnection::ExceptionOnError(int Result)
|
||||||
|
{
|
||||||
|
if(Result != SQLITE_OK)
|
||||||
|
{
|
||||||
|
throw std::runtime_error(sqlite3_errmsg(m_pDb));
|
||||||
|
}
|
||||||
|
}
|
54
src/engine/server/databases/sqlite.h
Normal file
54
src/engine/server/databases/sqlite.h
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
#ifndef ENGINE_SERVER_DATABASES_SQLITE_H
|
||||||
|
#define ENGINE_SERVER_DATABASES_SQLITE_H
|
||||||
|
|
||||||
|
#include "connection.h"
|
||||||
|
#include <atomic>
|
||||||
|
|
||||||
|
struct sqlite3;
|
||||||
|
struct sqlite3_stmt;
|
||||||
|
|
||||||
|
class CSqliteConnection : public IDbConnection
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CSqliteConnection(const char *pFilename, bool Setup);
|
||||||
|
virtual ~CSqliteConnection();
|
||||||
|
|
||||||
|
virtual CSqliteConnection *Copy();
|
||||||
|
|
||||||
|
virtual Status Connect();
|
||||||
|
virtual void Disconnect();
|
||||||
|
|
||||||
|
virtual void Lock(const char *pTable);
|
||||||
|
virtual void Unlock();
|
||||||
|
|
||||||
|
virtual void PrepareStatement(const char *pStmt);
|
||||||
|
|
||||||
|
virtual void BindString(int Idx, const char *pString);
|
||||||
|
virtual void BindInt(int Idx, int Value);
|
||||||
|
|
||||||
|
virtual bool Step();
|
||||||
|
|
||||||
|
virtual bool IsNull(int Col) const;
|
||||||
|
virtual float GetFloat(int Col) const;
|
||||||
|
virtual int GetInt(int Col) const;
|
||||||
|
virtual void GetString(int Col, char *pBuffer, int BufferSize) const;
|
||||||
|
// passing a negative buffer size is undefined behavior
|
||||||
|
virtual int GetBlob(int Col, unsigned char *pBuffer, int BufferSize) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
// copy of config vars
|
||||||
|
char m_aFilename[512];
|
||||||
|
bool m_Setup;
|
||||||
|
|
||||||
|
sqlite3 *m_pDb;
|
||||||
|
sqlite3_stmt *m_pStmt;
|
||||||
|
bool m_Done; // no more rows available for Step
|
||||||
|
// returns true, if the query succeded
|
||||||
|
bool Execute(const char *pQuery);
|
||||||
|
|
||||||
|
void ExceptionOnError(int Result);
|
||||||
|
|
||||||
|
std::atomic_bool m_InUse;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // ENGINE_SERVER_DATABASES_SQLITE_H
|
|
@ -45,6 +45,7 @@
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <engine/server/databases/mysql.h>
|
#include <engine/server/databases/mysql.h>
|
||||||
|
#include <engine/server/databases/sqlite.h>
|
||||||
#include <engine/server/databases/connection_pool.h>
|
#include <engine/server/databases/connection_pool.h>
|
||||||
|
|
||||||
|
|
||||||
|
@ -2301,6 +2302,23 @@ int CServer::Run()
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(g_Config.m_SvSqliteFile[0] != '\0')
|
||||||
|
{
|
||||||
|
auto pSqlServers = std::unique_ptr<CSqliteConnection>(new CSqliteConnection(
|
||||||
|
g_Config.m_SvSqliteFile, true));
|
||||||
|
|
||||||
|
if(g_Config.m_SvUseSQL)
|
||||||
|
{
|
||||||
|
DbPool()->RegisterDatabase(std::move(pSqlServers), CDbConnectionPool::WRITE_BACKUP);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
auto pCopy = std::unique_ptr<CSqliteConnection>(pSqlServers->Copy());
|
||||||
|
DbPool()->RegisterDatabase(std::move(pSqlServers), CDbConnectionPool::READ);
|
||||||
|
DbPool()->RegisterDatabase(std::move(pCopy), CDbConnectionPool::WRITE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// start server
|
// start server
|
||||||
NETADDR BindAddr;
|
NETADDR BindAddr;
|
||||||
int NetType = g_Config.m_SvIpv4Only ? NETTYPE_IPV4 : NETTYPE_ALL;
|
int NetType = g_Config.m_SvIpv4Only ? NETTYPE_IPV4 : NETTYPE_ALL;
|
||||||
|
@ -3051,6 +3069,8 @@ void CServer::ConLogout(IConsole::IResult *pResult, void *pUser)
|
||||||
|
|
||||||
void CServer::ConShowIps(IConsole::IResult *pResult, void *pUser)
|
void CServer::ConShowIps(IConsole::IResult *pResult, void *pUser)
|
||||||
{
|
{
|
||||||
|
if(!g_Config.m_SvUseSQL)
|
||||||
|
return;
|
||||||
CServer *pServer = (CServer *)pUser;
|
CServer *pServer = (CServer *)pUser;
|
||||||
|
|
||||||
if(pServer->m_RconClientID >= 0 && pServer->m_RconClientID < MAX_CLIENTS &&
|
if(pServer->m_RconClientID >= 0 && pServer->m_RconClientID < MAX_CLIENTS &&
|
||||||
|
@ -3072,6 +3092,8 @@ void CServer::ConShowIps(IConsole::IResult *pResult, void *pUser)
|
||||||
|
|
||||||
void CServer::ConAddSqlServer(IConsole::IResult *pResult, void *pUserData)
|
void CServer::ConAddSqlServer(IConsole::IResult *pResult, void *pUserData)
|
||||||
{
|
{
|
||||||
|
if(!g_Config.m_SvUseSQL)
|
||||||
|
return;
|
||||||
CServer *pSelf = (CServer *)pUserData;
|
CServer *pSelf = (CServer *)pUserData;
|
||||||
|
|
||||||
if (pResult->NumArguments() != 7 && pResult->NumArguments() != 8)
|
if (pResult->NumArguments() != 7 && pResult->NumArguments() != 8)
|
||||||
|
|
|
@ -219,7 +219,7 @@ MACRO_CONFIG_INT(SvSaveGames, sv_savegames, 1, 0, 1, CFGFLAG_SERVER, "Enables sa
|
||||||
MACRO_CONFIG_INT(SvSaveGamesDelay, sv_savegames_delay, 60, 0, 10000, CFGFLAG_SERVER, "Delay in seconds for loading a savegame")
|
MACRO_CONFIG_INT(SvSaveGamesDelay, sv_savegames_delay, 60, 0, 10000, CFGFLAG_SERVER, "Delay in seconds for loading a savegame")
|
||||||
MACRO_CONFIG_INT(SvUseSQL, sv_use_sql, 0, 0, 1, CFGFLAG_SERVER, "Enables SQL DB instead of record file")
|
MACRO_CONFIG_INT(SvUseSQL, sv_use_sql, 0, 0, 1, CFGFLAG_SERVER, "Enables SQL DB instead of record file")
|
||||||
MACRO_CONFIG_INT(SvSqlQueriesDelay, sv_sql_queries_delay, 1, 0, 20, CFGFLAG_SERVER, "Delay in seconds between SQL queries of a single player")
|
MACRO_CONFIG_INT(SvSqlQueriesDelay, sv_sql_queries_delay, 1, 0, 20, CFGFLAG_SERVER, "Delay in seconds between SQL queries of a single player")
|
||||||
MACRO_CONFIG_STR(SvSqliteFile, sv_sqlite_file, 64, "ddnet.sqlite", CFGFLAG_SERVER, "File to store ranks in case sv_use_sql is turned off")
|
MACRO_CONFIG_STR(SvSqliteFile, sv_sqlite_file, 64, "ddnet.sqlite", CFGFLAG_SERVER, "File to store ranks in case sv_use_sql is turned off or used as backup sql server")
|
||||||
|
|
||||||
#if defined(CONF_UPNP)
|
#if defined(CONF_UPNP)
|
||||||
MACRO_CONFIG_INT(SvUseUPnP, sv_use_upnp, 0, 0, 1, CFGFLAG_SERVER, "Enables UPnP support.")
|
MACRO_CONFIG_INT(SvUseUPnP, sv_use_upnp, 0, 0, 1, CFGFLAG_SERVER, "Enables UPnP support.")
|
||||||
|
|
Loading…
Reference in a new issue