mirror of
https://github.com/ddnet/ddnet.git
synced 2024-11-05 15:48:19 +00:00
Rewrite score using the new interface
delete file_score for now as it will be replaced by a sqlite backend
This commit is contained in:
parent
295ce95d64
commit
2eb3d23ef4
|
@ -1902,10 +1902,6 @@ set_src(GAME_SERVER GLOB_RECURSE src/game/server
|
|||
save.h
|
||||
score.cpp
|
||||
score.h
|
||||
score/file_score.cpp
|
||||
score/file_score.h
|
||||
score/sql_score.cpp
|
||||
score/sql_score.h
|
||||
teams.cpp
|
||||
teams.h
|
||||
teehistorian.cpp
|
||||
|
|
|
@ -252,7 +252,7 @@ public:
|
|||
virtual void OnMapChange(char *pNewMapName, int MapNameSize) = 0;
|
||||
|
||||
// FullShutdown is true if the program is about to exit (not if the map is changed)
|
||||
virtual void OnShutdown(bool FullShutdown = false) = 0;
|
||||
virtual void OnShutdown() = 0;
|
||||
|
||||
virtual void OnTick() = 0;
|
||||
virtual void OnPreSnap() = 0;
|
||||
|
|
|
@ -12,6 +12,7 @@ public:
|
|||
str_copy(m_aPrefix, pPrefix, sizeof(m_aPrefix));
|
||||
}
|
||||
virtual ~IDbConnection() {}
|
||||
IDbConnection& operator=(const IDbConnection&) = delete;
|
||||
|
||||
// copies the credentials, not the active connection
|
||||
virtual IDbConnection *Copy() = 0;
|
||||
|
@ -31,7 +32,7 @@ public:
|
|||
virtual void Disconnect() = 0;
|
||||
|
||||
// get exclusive read/write access to the database
|
||||
virtual void Lock() = 0;
|
||||
virtual void Lock(const char *pTable) = 0;
|
||||
virtual void Unlock() = 0;
|
||||
|
||||
// ? for Placeholders, connection has to be established, can overwrite previous prepared statements
|
||||
|
@ -41,11 +42,12 @@ public:
|
|||
virtual void BindString(int Idx, const char *pString) = 0;
|
||||
virtual void BindInt(int Idx, int Value) = 0;
|
||||
|
||||
virtual void Execute() = 0;
|
||||
|
||||
// if true if another result row exists and selects it
|
||||
// executes the query and returns if a result row exists and selects it
|
||||
// when called multiple times the next row is selected
|
||||
virtual bool Step() = 0;
|
||||
|
||||
virtual bool IsNull(int Col) const = 0;
|
||||
virtual float GetFloat(int Col) const = 0;
|
||||
virtual int GetInt(int Col) const = 0;
|
||||
// ensures that the string is null terminated
|
||||
virtual void GetString(int Col, char *pBuffer, int BufferSize) const = 0;
|
||||
|
@ -54,6 +56,65 @@ public:
|
|||
|
||||
protected:
|
||||
char m_aPrefix[64];
|
||||
|
||||
static constexpr const char *m_pCreateRace =
|
||||
"CREATE TABLE IF NOT EXISTS %s_race ("
|
||||
"Map VARCHAR(128) BINARY NOT NULL, "
|
||||
"Name VARCHAR(%d) BINARY NOT NULL, "
|
||||
"Timestamp TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, "
|
||||
"Time FLOAT DEFAULT 0, "
|
||||
"Server CHAR(4), "
|
||||
"cp1 FLOAT DEFAULT 0, cp2 FLOAT DEFAULT 0, cp3 FLOAT DEFAULT 0, "
|
||||
"cp4 FLOAT DEFAULT 0, cp5 FLOAT DEFAULT 0, cp6 FLOAT DEFAULT 0, "
|
||||
"cp7 FLOAT DEFAULT 0, cp8 FLOAT DEFAULT 0, cp9 FLOAT DEFAULT 0, "
|
||||
"cp10 FLOAT DEFAULT 0, cp11 FLOAT DEFAULT 0, cp12 FLOAT DEFAULT 0, "
|
||||
"cp13 FLOAT DEFAULT 0, cp14 FLOAT DEFAULT 0, cp15 FLOAT DEFAULT 0, "
|
||||
"cp16 FLOAT DEFAULT 0, cp17 FLOAT DEFAULT 0, cp18 FLOAT DEFAULT 0, "
|
||||
"cp19 FLOAT DEFAULT 0, cp20 FLOAT DEFAULT 0, cp21 FLOAT DEFAULT 0, "
|
||||
"cp22 FLOAT DEFAULT 0, cp23 FLOAT DEFAULT 0, cp24 FLOAT DEFAULT 0, "
|
||||
"cp25 FLOAT DEFAULT 0, "
|
||||
"GameID VARCHAR(64), "
|
||||
"DDNet7 BOOL DEFAULT FALSE, "
|
||||
"KEY (Map, Name)"
|
||||
") CHARACTER SET utf8mb4;";
|
||||
static constexpr const char *m_pCreateTeamrace =
|
||||
"CREATE TABLE IF NOT EXISTS %s_teamrace ("
|
||||
"Map VARCHAR(128) BINARY NOT NULL, "
|
||||
"Name VARCHAR(%d) BINARY NOT NULL, "
|
||||
"Timestamp TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, "
|
||||
"Time FLOAT DEFAULT 0, "
|
||||
"ID VARBINARY(16) NOT NULL, "
|
||||
"GameID VARCHAR(64), "
|
||||
"DDNet7 BOOL DEFAULT FALSE, "
|
||||
"KEY Map (Map)"
|
||||
") CHARACTER SET utf8mb4;";
|
||||
static constexpr const char *m_pCreateMaps =
|
||||
"CREATE TABLE IF NOT EXISTS %s_maps ("
|
||||
"Map VARCHAR(128) BINARY NOT NULL, "
|
||||
"Server VARCHAR(32) BINARY NOT NULL, "
|
||||
"Mapper VARCHAR(128) BINARY NOT NULL, "
|
||||
"Points INT DEFAULT 0, "
|
||||
"Stars INT DEFAULT 0, "
|
||||
"Timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP, "
|
||||
"UNIQUE KEY Map (Map)"
|
||||
") CHARACTER SET utf8mb4;";
|
||||
static constexpr const char *m_pCreateSaves =
|
||||
"CREATE TABLE IF NOT EXISTS %s_saves ("
|
||||
"Savegame TEXT CHARACTER SET utf8mb4 BINARY NOT NULL, "
|
||||
"Map VARCHAR(128) BINARY NOT NULL, "
|
||||
"Code VARCHAR(128) BINARY NOT NULL, "
|
||||
"Timestamp TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, "
|
||||
"Server CHAR(4), "
|
||||
"DDNet7 BOOL DEFAULT FALSE, "
|
||||
"SaveID VARCHAR(36) DEFAULT NULL, "
|
||||
"UNIQUE KEY (Map, Code)"
|
||||
") CHARACTER SET utf8mb4;";
|
||||
static constexpr const char *m_pCreatePoints =
|
||||
"CREATE TABLE IF NOT EXISTS %s_points ("
|
||||
"Name VARCHAR(%d) BINARY NOT NULL, "
|
||||
"Points INT DEFAULT 0, "
|
||||
"UNIQUE KEY Name (Name)"
|
||||
") CHARACTER SET utf8mb4;";
|
||||
};
|
||||
|
||||
#endif // ENGINE_SERVER_DATABASES_CONNECTION_H
|
||||
|
|
|
@ -1,20 +1,65 @@
|
|||
#include "connection_pool.h"
|
||||
|
||||
#if defined(CONF_SQL)
|
||||
#include <cppconn/exception.h>
|
||||
#endif
|
||||
|
||||
// helper struct to hold thread data
|
||||
struct CSqlExecData
|
||||
{
|
||||
CSqlExecData(
|
||||
bool (*pFuncPtr) (IDbConnection *, const ISqlData *),
|
||||
const ISqlData *pSqlRequestData
|
||||
);
|
||||
~CSqlExecData();
|
||||
std::unique_ptr<const ISqlData> pThreadData,
|
||||
const char *pName);
|
||||
CSqlExecData(
|
||||
bool (*pFuncPtr) (IDbConnection *, const ISqlData *, bool),
|
||||
std::unique_ptr<const ISqlData> pThreadData,
|
||||
const char *pName);
|
||||
~CSqlExecData() {}
|
||||
|
||||
bool (*m_pFuncPtr) (IDbConnection*, const ISqlData *);
|
||||
std::unique_ptr<const ISqlData> m_pSqlRequestData;
|
||||
enum
|
||||
{
|
||||
READ_ACCESS,
|
||||
WRITE_ACCESS,
|
||||
} m_Mode;
|
||||
union
|
||||
{
|
||||
bool (*m_pWriteFunc) (IDbConnection*, const ISqlData *, bool);
|
||||
bool (*m_pReadFunc) (IDbConnection*, const ISqlData *);
|
||||
} m_Ptr;
|
||||
|
||||
std::unique_ptr<const ISqlData> m_pThreadData;
|
||||
const char *m_pName;
|
||||
};
|
||||
|
||||
CDbConnectionPool::CDbConnectionPool()
|
||||
CSqlExecData::CSqlExecData(
|
||||
bool (*pFuncPtr) (IDbConnection *, const ISqlData *),
|
||||
std::unique_ptr<const ISqlData> pThreadData,
|
||||
const char *pName) :
|
||||
m_Mode(READ_ACCESS),
|
||||
m_pThreadData(std::move(pThreadData)),
|
||||
m_pName(pName)
|
||||
{
|
||||
m_Ptr.m_pReadFunc = pFuncPtr;
|
||||
}
|
||||
|
||||
CSqlExecData::CSqlExecData(
|
||||
bool (*pFuncPtr) (IDbConnection *, const ISqlData *, bool),
|
||||
std::unique_ptr<const ISqlData> pThreadData,
|
||||
const char *pName) :
|
||||
m_Mode(WRITE_ACCESS),
|
||||
m_pThreadData(std::move(pThreadData)),
|
||||
m_pName(pName)
|
||||
{
|
||||
m_Ptr.m_pWriteFunc = pFuncPtr;
|
||||
}
|
||||
|
||||
CDbConnectionPool::CDbConnectionPool() :
|
||||
m_NumElem(),
|
||||
FirstElem(0),
|
||||
LastElem(0)
|
||||
{
|
||||
thread_init_and_detach(CDbConnectionPool::SqlWorker, this, "database worker thread");
|
||||
}
|
||||
|
||||
CDbConnectionPool::~CDbConnectionPool()
|
||||
|
@ -23,20 +68,122 @@ CDbConnectionPool::~CDbConnectionPool()
|
|||
|
||||
void CDbConnectionPool::RegisterDatabase(std::unique_ptr<IDbConnection> pDatabase, Mode DatabaseMode)
|
||||
{
|
||||
if(DatabaseMode < 0 || NUM_MODES <= DatabaseMode)
|
||||
return;
|
||||
m_aapDbConnections[DatabaseMode].push_back(std::move(pDatabase));
|
||||
}
|
||||
|
||||
void CDbConnectionPool::Execute(
|
||||
bool (*pFuncPtr) (IDbConnection *, const ISqlData *),
|
||||
std::unique_ptr<ISqlData> pSqlRequestData)
|
||||
std::unique_ptr<const ISqlData> pThreadData,
|
||||
const char *pName)
|
||||
{
|
||||
m_aTasks[FirstElem++].reset(new CSqlExecData(pFuncPtr, std::move(pThreadData), pName));
|
||||
FirstElem %= sizeof(m_aTasks) / sizeof(m_aTasks[0]);
|
||||
m_NumElem.signal();
|
||||
}
|
||||
|
||||
void CDbConnectionPool::ExecuteWrite(
|
||||
bool (*pFuncPtr) (IDbConnection *, const ISqlData *),
|
||||
std::unique_ptr<ISqlData> pSqlRequestData)
|
||||
bool (*pFuncPtr) (IDbConnection *, const ISqlData *, bool),
|
||||
std::unique_ptr<const ISqlData> pThreadData,
|
||||
const char *pName)
|
||||
{
|
||||
m_aTasks[FirstElem++].reset(new CSqlExecData(pFuncPtr, std::move(pThreadData), pName));
|
||||
FirstElem %= sizeof(m_aTasks) / sizeof(m_aTasks[0]);
|
||||
m_NumElem.signal();
|
||||
}
|
||||
|
||||
void CDbConnectionPool::OnShutdown()
|
||||
{
|
||||
}
|
||||
|
||||
void CDbConnectionPool::Shutdown()
|
||||
void CDbConnectionPool::SqlWorker(void *pUser)
|
||||
{
|
||||
CDbConnectionPool *pThis = (CDbConnectionPool *)pUser;
|
||||
pThis->SqlWorker();
|
||||
}
|
||||
|
||||
void CDbConnectionPool::SqlWorker()
|
||||
{
|
||||
while(1)
|
||||
{
|
||||
m_NumElem.wait();
|
||||
auto pThreadData = std::move(m_aTasks[LastElem++]);
|
||||
LastElem %= sizeof(m_aTasks) / sizeof(m_aTasks[0]);
|
||||
bool Success = false;
|
||||
switch(pThreadData->m_Mode)
|
||||
{
|
||||
case CSqlExecData::READ_ACCESS:
|
||||
{
|
||||
for(int i = 0; i < (int)m_aapDbConnections[Mode::READ].size(); i++)
|
||||
{
|
||||
if(ExecSqlFunc(m_aapDbConnections[Mode::READ][i].get(), pThreadData.get(), false))
|
||||
{
|
||||
Success = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} break;
|
||||
case CSqlExecData::WRITE_ACCESS:
|
||||
{
|
||||
for(int i = 0; i < (int)m_aapDbConnections[Mode::WRITE].size(); i++)
|
||||
{
|
||||
if(ExecSqlFunc(m_aapDbConnections[Mode::WRITE][i].get(), pThreadData.get(), false))
|
||||
{
|
||||
Success = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!Success)
|
||||
{
|
||||
for(int i = 0; i < (int)m_aapDbConnections[Mode::WRITE_BACKUP].size(); i++)
|
||||
{
|
||||
if(ExecSqlFunc(m_aapDbConnections[Mode::WRITE_BACKUP][i].get(), pThreadData.get(), true))
|
||||
{
|
||||
Success = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} break;
|
||||
}
|
||||
if(Success)
|
||||
dbg_msg("sql", "%s done", pThreadData->m_pName);
|
||||
}
|
||||
}
|
||||
|
||||
bool CDbConnectionPool::ExecSqlFunc(IDbConnection *pConnection, CSqlExecData *pData, bool Failure)
|
||||
{
|
||||
if(pConnection->Connect() != IDbConnection::SUCCESS)
|
||||
return false;
|
||||
bool Success = false;
|
||||
try {
|
||||
switch(pData->m_Mode)
|
||||
{
|
||||
case CSqlExecData::READ_ACCESS:
|
||||
if(pData->m_Ptr.m_pReadFunc(pConnection, pData->m_pThreadData.get()))
|
||||
Success = true;
|
||||
break;
|
||||
case CSqlExecData::WRITE_ACCESS:
|
||||
if(pData->m_Ptr.m_pWriteFunc(pConnection, pData->m_pThreadData.get(), Failure))
|
||||
Success = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
#if defined(CONF_SQL)
|
||||
catch (sql::SQLException &e)
|
||||
{
|
||||
dbg_msg("sql", "MySQL Error: %s", e.what());
|
||||
}
|
||||
#endif
|
||||
catch (...)
|
||||
{
|
||||
dbg_msg("sql", "Unexpected exception caught");
|
||||
}
|
||||
pConnection->Unlock();
|
||||
pConnection->Disconnect();
|
||||
if(!Success)
|
||||
dbg_msg("sql", "%s failed", pData->m_pName);
|
||||
return Success;
|
||||
}
|
||||
|
||||
|
|
|
@ -4,12 +4,13 @@
|
|||
#include "connection.h"
|
||||
|
||||
#include <base/tl/threading.h>
|
||||
#include <atomic>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
struct ISqlData
|
||||
{
|
||||
virtual ~ISqlData();
|
||||
virtual ~ISqlData() {};
|
||||
};
|
||||
|
||||
class CDbConnectionPool
|
||||
|
@ -17,6 +18,7 @@ class CDbConnectionPool
|
|||
public:
|
||||
CDbConnectionPool();
|
||||
~CDbConnectionPool();
|
||||
CDbConnectionPool& operator=(const CDbConnectionPool&) = delete;
|
||||
|
||||
enum Mode
|
||||
{
|
||||
|
@ -30,17 +32,27 @@ public:
|
|||
|
||||
void Execute(
|
||||
bool (*pFuncPtr) (IDbConnection *, const ISqlData *),
|
||||
std::unique_ptr<ISqlData> pSqlRequestData);
|
||||
std::unique_ptr<const ISqlData> pSqlRequestData,
|
||||
const char *pName);
|
||||
// writes to WRITE_BACKUP server in case of failure
|
||||
void ExecuteWrite(
|
||||
bool (*pFuncPtr) (IDbConnection *, const ISqlData *),
|
||||
std::unique_ptr<ISqlData> pSqlRequestData);
|
||||
bool (*pFuncPtr) (IDbConnection *, const ISqlData *, bool),
|
||||
std::unique_ptr<const ISqlData> pSqlRequestData,
|
||||
const char *pName);
|
||||
|
||||
void Shutdown();
|
||||
void OnShutdown();
|
||||
|
||||
private:
|
||||
std::vector<std::unique_ptr<IDbConnection>> m_aapDbConnections[NUM_MODES];
|
||||
lock m_ConnectionLookupLock[NUM_MODES];
|
||||
|
||||
static void SqlWorker(void *pUser);
|
||||
void SqlWorker();
|
||||
bool ExecSqlFunc(IDbConnection *pConnection, struct CSqlExecData *pData, bool Failure);
|
||||
|
||||
semaphore m_NumElem;
|
||||
int FirstElem;
|
||||
int LastElem;
|
||||
std::unique_ptr<struct CSqlExecData> m_aTasks[512];
|
||||
};
|
||||
|
||||
#endif // ENGINE_SERVER_DATABASES_CONNECTION_POOL_H
|
||||
|
|
|
@ -1,6 +1,11 @@
|
|||
#include "mysql.h"
|
||||
|
||||
#if defined(CONF_SQL)
|
||||
#include <cppconn/driver.h>
|
||||
#include <engine/shared/protocol.h>
|
||||
#endif
|
||||
|
||||
lock CMysqlConnection::m_SqlDriverLock;
|
||||
|
||||
CMysqlConnection::CMysqlConnection(
|
||||
const char *pDatabase,
|
||||
|
@ -11,7 +16,10 @@ CMysqlConnection::CMysqlConnection(
|
|||
int Port,
|
||||
bool Setup) :
|
||||
IDbConnection(pPrefix),
|
||||
m_pDriver(nullptr),
|
||||
#if defined(CONF_SQL)
|
||||
m_NewQuery(false),
|
||||
m_Locked(false),
|
||||
#endif
|
||||
m_Port(Port),
|
||||
m_Setup(Setup),
|
||||
m_InUse(false)
|
||||
|
@ -20,6 +28,9 @@ CMysqlConnection::CMysqlConnection(
|
|||
str_copy(m_aUser, pUser, sizeof(m_aUser));
|
||||
str_copy(m_aPass, pPass, sizeof(m_aPass));
|
||||
str_copy(m_aIp, pIp, sizeof(m_aIp));
|
||||
#if not defined(CONF_SQL)
|
||||
dbg_msg("sql", "Adding MySQL server failed due to MySQL support not enabled during compile time");
|
||||
#endif
|
||||
}
|
||||
|
||||
CMysqlConnection::~CMysqlConnection()
|
||||
|
@ -33,9 +44,119 @@ CMysqlConnection *CMysqlConnection::Copy()
|
|||
|
||||
IDbConnection::Status CMysqlConnection::Connect()
|
||||
{
|
||||
#if defined(CONF_SQL)
|
||||
if(m_InUse.exchange(true))
|
||||
return Status::IN_USE;
|
||||
|
||||
m_NewQuery = true;
|
||||
if(m_pConnection != nullptr)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Connect to specific database
|
||||
m_pConnection->setSchema(m_aDatabase);
|
||||
return Status::SUCCESS;
|
||||
}
|
||||
catch (sql::SQLException &e)
|
||||
{
|
||||
dbg_msg("sql", "MySQL Error: %s", e.what());
|
||||
}
|
||||
catch (const std::exception& ex)
|
||||
{
|
||||
dbg_msg("sql", "MySQL Error: %s", ex.what());
|
||||
}
|
||||
catch (const std::string& ex)
|
||||
{
|
||||
dbg_msg("sql", "MySQL Error: %s", ex.c_str());
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
dbg_msg("sql", "Unknown Error cause by the MySQL/C++ Connector");
|
||||
}
|
||||
|
||||
m_InUse.store(false);
|
||||
dbg_msg("sql", "ERROR: SQL connection failed");
|
||||
return Status::ERROR;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
m_pConnection.release();
|
||||
m_pPreparedStmt.release();
|
||||
m_pResults.release();
|
||||
|
||||
sql::ConnectOptionsMap connection_properties;
|
||||
connection_properties["hostName"] = sql::SQLString(m_aIp);
|
||||
connection_properties["port"] = m_Port;
|
||||
connection_properties["userName"] = sql::SQLString(m_aUser);
|
||||
connection_properties["password"] = sql::SQLString(m_aPass);
|
||||
connection_properties["OPT_CONNECT_TIMEOUT"] = 10;
|
||||
connection_properties["OPT_READ_TIMEOUT"] = 10;
|
||||
connection_properties["OPT_WRITE_TIMEOUT"] = 20;
|
||||
connection_properties["OPT_RECONNECT"] = true;
|
||||
connection_properties["OPT_CHARSET_NAME"] = sql::SQLString("utf8mb4");
|
||||
connection_properties["OPT_SET_CHARSET_NAME"] = sql::SQLString("utf8mb4");
|
||||
|
||||
// Create connection
|
||||
{
|
||||
scope_lock GlobalLockScope(&m_SqlDriverLock);
|
||||
sql::Driver *pDriver = get_driver_instance();
|
||||
m_pConnection.reset(pDriver->connect(connection_properties));
|
||||
}
|
||||
|
||||
// Create Statement
|
||||
m_pStmt = std::unique_ptr<sql::Statement>(m_pConnection->createStatement());
|
||||
|
||||
// Apparently OPT_CHARSET_NAME and OPT_SET_CHARSET_NAME are not enough
|
||||
m_pStmt->execute("SET CHARACTER SET utf8mb4;");
|
||||
|
||||
if(m_Setup)
|
||||
{
|
||||
char aBuf[1024];
|
||||
// create database
|
||||
str_format(aBuf, sizeof(aBuf), "CREATE DATABASE IF NOT EXISTS %s CHARACTER SET utf8mb4", m_aDatabase);
|
||||
m_pStmt->execute(aBuf);
|
||||
// Connect to specific database
|
||||
m_pConnection->setSchema(m_aDatabase);
|
||||
str_format(aBuf, sizeof(aBuf), m_pCreateRace, GetPrefix(), MAX_NAME_LENGTH);
|
||||
m_pStmt->execute(aBuf);
|
||||
str_format(aBuf, sizeof(aBuf), m_pCreateTeamrace, GetPrefix(), MAX_NAME_LENGTH);
|
||||
m_pStmt->execute(aBuf);
|
||||
str_format(aBuf, sizeof(aBuf), m_pCreateMaps, GetPrefix());
|
||||
m_pStmt->execute(aBuf);
|
||||
str_format(aBuf, sizeof(aBuf), m_pCreateSaves, GetPrefix());
|
||||
m_pStmt->execute(aBuf);
|
||||
str_format(aBuf, sizeof(aBuf), m_pCreatePoints, GetPrefix(), MAX_NAME_LENGTH);
|
||||
m_pStmt->execute(aBuf);
|
||||
m_Setup = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Connect to specific database
|
||||
m_pConnection->setSchema(m_aDatabase);
|
||||
}
|
||||
dbg_msg("sql", "sql connection established");
|
||||
return Status::SUCCESS;
|
||||
}
|
||||
catch (sql::SQLException &e)
|
||||
{
|
||||
dbg_msg("sql", "MySQL Error: %s", e.what());
|
||||
}
|
||||
catch (const std::exception& ex)
|
||||
{
|
||||
dbg_msg("sql", "MySQL Error: %s", ex.what());
|
||||
}
|
||||
catch (const std::string& ex)
|
||||
{
|
||||
dbg_msg("sql", "MySQL Error: %s", ex.c_str());
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
dbg_msg("sql", "Unknown Error cause by the MySQL/C++ Connector");
|
||||
}
|
||||
|
||||
#endif
|
||||
dbg_msg("sql", "ERROR: sql connection failed");
|
||||
return Status::ERROR;
|
||||
}
|
||||
|
||||
|
@ -44,45 +165,107 @@ void CMysqlConnection::Disconnect()
|
|||
m_InUse.store(false);
|
||||
}
|
||||
|
||||
void CMysqlConnection::Lock()
|
||||
void CMysqlConnection::Lock(const char *pTable)
|
||||
{
|
||||
#if defined(CONF_SQL)
|
||||
char aBuf[512];
|
||||
str_format(aBuf, sizeof(aBuf), "lock tables %s write;", pTable);
|
||||
m_pStmt->execute(aBuf);
|
||||
m_Locked = true;
|
||||
#endif
|
||||
}
|
||||
|
||||
void CMysqlConnection::Unlock()
|
||||
{
|
||||
#if defined(CONF_SQL)
|
||||
if(m_Locked)
|
||||
{
|
||||
m_pStmt->execute("unlock tables;");
|
||||
m_Locked = false;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void CMysqlConnection::PrepareStatement(const char *pStmt)
|
||||
{
|
||||
#if defined(CONF_SQL)
|
||||
m_pPreparedStmt.reset(m_pConnection->prepareStatement(pStmt));
|
||||
m_NewQuery = true;
|
||||
#endif
|
||||
}
|
||||
|
||||
void CMysqlConnection::BindString(int Idx, const char *pString)
|
||||
{
|
||||
#if defined(CONF_SQL)
|
||||
m_pPreparedStmt->setString(Idx, pString);
|
||||
m_NewQuery = true;
|
||||
#endif
|
||||
}
|
||||
|
||||
void CMysqlConnection::BindInt(int Idx, int Value)
|
||||
{
|
||||
}
|
||||
|
||||
void CMysqlConnection::Execute()
|
||||
{
|
||||
#if defined(CONF_SQL)
|
||||
m_pPreparedStmt->setInt(Idx, Value);
|
||||
m_NewQuery = true;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool CMysqlConnection::Step()
|
||||
{
|
||||
#if defined(CONF_SQL)
|
||||
if(m_NewQuery)
|
||||
{
|
||||
m_NewQuery = false;
|
||||
m_pResults.reset(m_pPreparedStmt->executeQuery());
|
||||
}
|
||||
return m_pResults->next();
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool CMysqlConnection::IsNull(int Col) const
|
||||
{
|
||||
#if defined(CONF_SQL)
|
||||
return m_pResults->isNull(Col);
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
float CMysqlConnection::GetFloat(int Col) const
|
||||
{
|
||||
#if defined(CONF_SQL)
|
||||
return (float)m_pResults->getDouble(Col);
|
||||
#else
|
||||
return 0.0;
|
||||
#endif
|
||||
}
|
||||
|
||||
int CMysqlConnection::GetInt(int Col) const
|
||||
{
|
||||
#if defined(CONF_SQL)
|
||||
return m_pResults->getInt(Col);
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
void CMysqlConnection::GetString(int Col, char *pBuffer, int BufferSize) const
|
||||
{
|
||||
#if defined(CONF_SQL)
|
||||
auto String = m_pResults->getString(Col);
|
||||
str_copy(pBuffer, String.c_str(), BufferSize);
|
||||
#endif
|
||||
}
|
||||
|
||||
int CMysqlConnection::GetBlob(int Col, unsigned char *pBuffer, int BufferSize) const
|
||||
{
|
||||
#if defined(CONF_SQL)
|
||||
auto Blob = m_pResults->getBlob(Col);
|
||||
Blob->read((char *)pBuffer, BufferSize);
|
||||
return Blob->gcount();
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -2,16 +2,16 @@
|
|||
#define ENGINE_SERVER_DATABASES_MYSQL_H
|
||||
|
||||
#include "connection.h"
|
||||
#include <base/tl/threading.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <memory>
|
||||
|
||||
#if defined(CONF_SQL)
|
||||
#include <cppconn/exception.h>
|
||||
#include <cppconn/prepared_statement.h>
|
||||
#include <cppconn/statement.h>
|
||||
|
||||
namespace sql {
|
||||
class Driver;
|
||||
} /* namespace sql */
|
||||
#endif
|
||||
|
||||
class CMysqlConnection : public IDbConnection
|
||||
{
|
||||
|
@ -31,7 +31,7 @@ public:
|
|||
virtual Status Connect();
|
||||
virtual void Disconnect();
|
||||
|
||||
virtual void Lock();
|
||||
virtual void Lock(const char *pTable);
|
||||
virtual void Unlock();
|
||||
|
||||
virtual void PrepareStatement(const char *pStmt);
|
||||
|
@ -39,19 +39,23 @@ public:
|
|||
virtual void BindString(int Idx, const char *pString);
|
||||
virtual void BindInt(int Idx, int Value);
|
||||
|
||||
virtual void Execute();
|
||||
|
||||
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;
|
||||
virtual int GetBlob(int Col, unsigned char *pBuffer, int BufferSize) const;
|
||||
|
||||
private:
|
||||
#if defined(CONF_SQL)
|
||||
std::unique_ptr<sql::Connection> m_pConnection;
|
||||
sql::Driver *m_pDriver;
|
||||
std::unique_ptr<sql::PreparedStatement> m_pPreparedStmt;
|
||||
std::unique_ptr<sql::Statement> m_pStmt;
|
||||
std::unique_ptr<sql::ResultSet> m_pResults;
|
||||
bool m_NewQuery;
|
||||
bool m_Locked;
|
||||
#endif
|
||||
|
||||
// copy of config vars
|
||||
char m_aDatabase[64];
|
||||
|
@ -62,6 +66,7 @@ private:
|
|||
bool m_Setup;
|
||||
|
||||
std::atomic_bool m_InUse;
|
||||
static lock m_SqlDriverLock;
|
||||
};
|
||||
|
||||
#endif // ENGINE_SERVER_DATABASES_MYSQL_H
|
||||
|
|
|
@ -44,6 +44,9 @@
|
|||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
#include <engine/server/databases/mysql.h>
|
||||
#include <engine/server/databases/connection_pool.h>
|
||||
|
||||
|
||||
CSnapIDPool::CSnapIDPool()
|
||||
{
|
||||
|
@ -297,16 +300,7 @@ CServer::CServer(): m_Register(false), m_RegSixup(true)
|
|||
m_ConnLoggingSocketCreated = false;
|
||||
#endif
|
||||
|
||||
#if defined (CONF_SQL)
|
||||
for (int i = 0; i < MAX_SQLSERVERS; i++)
|
||||
{
|
||||
m_apSqlReadServers[i] = 0;
|
||||
m_apSqlWriteServers[i] = 0;
|
||||
}
|
||||
|
||||
CSqlConnector::SetReadServers(m_apSqlReadServers);
|
||||
CSqlConnector::SetWriteServers(m_apSqlWriteServers);
|
||||
#endif
|
||||
m_pConnectionPool = new CDbConnectionPool();
|
||||
|
||||
m_aErrorShutdownReason[0] = 0;
|
||||
|
||||
|
@ -2584,22 +2578,14 @@ int CServer::Run()
|
|||
m_Fifo.Shutdown();
|
||||
#endif
|
||||
|
||||
GameServer()->OnShutdown(true);
|
||||
GameServer()->OnShutdown();
|
||||
m_pMap->Unload();
|
||||
|
||||
for(int i = 0; i < 2; i++)
|
||||
free(m_apCurrentMapData[i]);
|
||||
|
||||
#if defined (CONF_SQL)
|
||||
for (int i = 0; i < MAX_SQLSERVERS; i++)
|
||||
{
|
||||
if (m_apSqlReadServers[i])
|
||||
delete m_apSqlReadServers[i];
|
||||
|
||||
if (m_apSqlWriteServers[i])
|
||||
delete m_apSqlWriteServers[i];
|
||||
}
|
||||
#endif
|
||||
DbPool()->OnShutdown();
|
||||
delete m_pConnectionPool;
|
||||
|
||||
#if defined (CONF_UPNP)
|
||||
m_UPnP.Shutdown();
|
||||
|
@ -3084,8 +3070,6 @@ void CServer::ConShowIps(IConsole::IResult *pResult, void *pUser)
|
|||
}
|
||||
}
|
||||
|
||||
#if defined (CONF_SQL)
|
||||
|
||||
void CServer::ConAddSqlServer(IConsole::IResult *pResult, void *pUserData)
|
||||
{
|
||||
CServer *pSelf = (CServer *)pUserData;
|
||||
|
@ -3109,34 +3093,31 @@ void CServer::ConAddSqlServer(IConsole::IResult *pResult, void *pUserData)
|
|||
|
||||
bool SetUpDb = pResult->NumArguments() == 8 ? pResult->GetInteger(7) : true;
|
||||
|
||||
CSqlServer** apSqlServers = ReadOnly ? pSelf->m_apSqlReadServers : pSelf->m_apSqlWriteServers;
|
||||
auto pSqlServers = std::unique_ptr<CMysqlConnection>(new CMysqlConnection(
|
||||
pResult->GetString(1), pResult->GetString(2), pResult->GetString(3),
|
||||
pResult->GetString(4), pResult->GetString(5), pResult->GetInteger(6),
|
||||
SetUpDb));
|
||||
|
||||
for (int i = 0; i < MAX_SQLSERVERS; i++)
|
||||
char aBuf[512];
|
||||
str_format(aBuf, sizeof(aBuf),
|
||||
"Added new Sql%sServer: DB: '%s' Prefix: '%s' User: '%s' IP: <{'%s'}> Port: %d",
|
||||
ReadOnly ? "Read" : "Write",
|
||||
pResult->GetString(1), pResult->GetString(2), pResult->GetString(3),
|
||||
pResult->GetString(5), pResult->GetInteger(6));
|
||||
pSelf->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "server", aBuf);
|
||||
if(SetUpDb)
|
||||
{
|
||||
if (!apSqlServers[i])
|
||||
{
|
||||
apSqlServers[i] = new CSqlServer(pResult->GetString(1), pResult->GetString(2), pResult->GetString(3), pResult->GetString(4), pResult->GetString(5), pResult->GetInteger(6), &pSelf->m_GlobalSqlLock, ReadOnly, SetUpDb);
|
||||
|
||||
char aBuf[512];
|
||||
str_format(aBuf, sizeof(aBuf),
|
||||
"Added new Sql%sServer: %d: DB: '%s' Prefix: '%s' User: '%s' IP: <{'%s'}> Port: %d",
|
||||
ReadOnly ? "Read" : "Write", i, apSqlServers[i]->GetDatabase(),
|
||||
apSqlServers[i]->GetPrefix(), apSqlServers[i]->GetUser(),
|
||||
apSqlServers[i]->GetIP(), apSqlServers[i]->GetPort());
|
||||
pSelf->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "server", aBuf);
|
||||
if(SetUpDb)
|
||||
{
|
||||
if(!apSqlServers[i]->CreateTables())
|
||||
pSelf->SetErrorShutdown("database create tables failed");
|
||||
}
|
||||
return;
|
||||
}
|
||||
/* TODO
|
||||
if(!pSqlServers->CreateTables())
|
||||
pSelf->SetErrorShutdown("database create tables failed");
|
||||
*/
|
||||
}
|
||||
pSelf->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "server", "failed to add new sqlserver: limit of sqlservers reached");
|
||||
pSelf->DbPool()->RegisterDatabase(std::move(pSqlServers), ReadOnly ? CDbConnectionPool::READ : CDbConnectionPool::WRITE);
|
||||
}
|
||||
|
||||
void CServer::ConDumpSqlServers(IConsole::IResult *pResult, void *pUserData)
|
||||
{
|
||||
/* TODO
|
||||
CServer *pSelf = (CServer *)pUserData;
|
||||
|
||||
bool ReadOnly;
|
||||
|
@ -3159,10 +3140,9 @@ void CServer::ConDumpSqlServers(IConsole::IResult *pResult, void *pUserData)
|
|||
str_format(aBuf, sizeof(aBuf), "SQL-%s %d: DB: '%s' Prefix: '%s' User: '%s' Pass: '%s' IP: <{'%s'}> Port: %d", ReadOnly ? "Read" : "Write", i, apSqlServers[i]->GetDatabase(), apSqlServers[i]->GetPrefix(), apSqlServers[i]->GetUser(), apSqlServers[i]->GetPass(), apSqlServers[i]->GetIP(), apSqlServers[i]->GetPort());
|
||||
pSelf->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "server", aBuf);
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void CServer::ConchainSpecialInfoupdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData)
|
||||
{
|
||||
pfnCallback(pResult, pCallbackUserData);
|
||||
|
@ -3363,10 +3343,8 @@ void CServer::RegisterCommands()
|
|||
|
||||
Console()->Register("reload", "", CFGFLAG_SERVER, ConMapReload, this, "Reload the map");
|
||||
|
||||
#if defined(CONF_SQL)
|
||||
Console()->Register("add_sqlserver", "s['r'|'w'] s[Database] s[Prefix] s[User] s[Password] s[IP] i[Port] ?i[SetUpDatabase ?]", CFGFLAG_SERVER|CFGFLAG_NONTEEHISTORIC, ConAddSqlServer, this, "add a sqlserver");
|
||||
Console()->Register("dump_sqlservers", "s['r'|'w']", CFGFLAG_SERVER, ConDumpSqlServers, this, "dumps all sqlservers readservers = r, writeservers = w");
|
||||
#endif
|
||||
|
||||
Console()->Register("auth_add", "s[ident] s[level] s[pw]", CFGFLAG_SERVER|CFGFLAG_NONTEEHISTORIC, ConAuthAdd, this, "Add a rcon key");
|
||||
Console()->Register("auth_add_p", "s[ident] s[level] s[hash] s[salt]", CFGFLAG_SERVER|CFGFLAG_NONTEEHISTORIC, ConAuthAddHashed, this, "Add a prehashed rcon key");
|
||||
|
|
|
@ -33,11 +33,6 @@
|
|||
#include "upnp.h"
|
||||
#endif
|
||||
|
||||
#if defined (CONF_SQL)
|
||||
#include "sql_connector.h"
|
||||
#include "sql_server.h"
|
||||
#endif
|
||||
|
||||
class CSnapIDPool
|
||||
{
|
||||
enum
|
||||
|
@ -102,24 +97,20 @@ class CServer : public IServer
|
|||
CUPnP m_UPnP;
|
||||
#endif
|
||||
|
||||
#if defined(CONF_SQL)
|
||||
lock m_GlobalSqlLock;
|
||||
|
||||
CSqlServer *m_apSqlReadServers[MAX_SQLSERVERS];
|
||||
CSqlServer *m_apSqlWriteServers[MAX_SQLSERVERS];
|
||||
#endif
|
||||
|
||||
#if defined(CONF_FAMILY_UNIX)
|
||||
UNIXSOCKETADDR m_ConnLoggingDestAddr;
|
||||
bool m_ConnLoggingSocketCreated;
|
||||
UNIXSOCKET m_ConnLoggingSocket;
|
||||
#endif
|
||||
|
||||
class CDbConnectionPool *m_pConnectionPool;
|
||||
|
||||
public:
|
||||
class IGameServer *GameServer() { return m_pGameServer; }
|
||||
class IConsole *Console() { return m_pConsole; }
|
||||
class IStorage *Storage() { return m_pStorage; }
|
||||
class IEngineAntibot *Antibot() { return m_pAntibot; }
|
||||
class CDbConnectionPool *DbPool() { return m_pConnectionPool; }
|
||||
|
||||
enum
|
||||
{
|
||||
|
@ -395,11 +386,9 @@ public:
|
|||
static void ConNameUnban(IConsole::IResult *pResult, void *pUser);
|
||||
static void ConNameBans(IConsole::IResult *pResult, void *pUser);
|
||||
|
||||
#if defined (CONF_SQL)
|
||||
// console commands for sqlmasters
|
||||
static void ConAddSqlServer(IConsole::IResult *pResult, void *pUserData);
|
||||
static void ConDumpSqlServers(IConsole::IResult *pResult, void *pUserData);
|
||||
#endif
|
||||
|
||||
static void ConchainSpecialInfoupdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
|
||||
static void ConchainMaxclientsperipUpdate(IConsole::IResult *pResult, void *pUserData, IConsole::FCommandCallback pfnCallback, void *pCallbackUserData);
|
||||
|
|
|
@ -24,6 +24,23 @@ void sqlstr::FuzzyString(char *pString, int size)
|
|||
delete [] newString;
|
||||
}
|
||||
|
||||
int sqlstr::EscapeLike(char *pDst, const char *pSrc, int DstSize)
|
||||
{
|
||||
int Pos = 0;
|
||||
int DstPos = 0;
|
||||
while(DstPos + 2 < DstSize)
|
||||
{
|
||||
if(pSrc[Pos] == '\0')
|
||||
break;
|
||||
if(pSrc[Pos] == '\\' || pSrc[Pos] == '%' || pSrc[Pos] == '_' || pSrc[Pos] == '[')
|
||||
pDst[DstPos++] = '\\';
|
||||
pDst[DstPos++] = pSrc[Pos++];
|
||||
|
||||
}
|
||||
pDst[DstPos++] = '\0';
|
||||
return DstPos;
|
||||
}
|
||||
|
||||
// anti SQL injection
|
||||
void sqlstr::ClearString(char *pString, int size)
|
||||
{
|
||||
|
|
|
@ -11,6 +11,9 @@ void FuzzyString(char *pString, int size);
|
|||
// anti SQL injection
|
||||
void ClearString(char *pString, int size = 32);
|
||||
|
||||
// written number of added bytes
|
||||
int EscapeLike(char *pDst, const char *pSrc, int DstSize);
|
||||
|
||||
void AgoTimeToString(int agoTime, char *pAgoString);
|
||||
|
||||
template<unsigned int size>
|
||||
|
|
|
@ -217,11 +217,9 @@ MACRO_CONFIG_STR(SvSqlServerName, sv_sql_servername, 5, "UNK", CFGFLAG_SERVER, "
|
|||
MACRO_CONFIG_STR(SvSqlValidServerNames, sv_sql_valid_servernames, 64, "UNK", CFGFLAG_SERVER, "Comma separated list of valid server names for saving a game to ([A-Z][A-Z][A-Z].?")
|
||||
MACRO_CONFIG_INT(SvSaveGames, sv_savegames, 1, 0, 1, CFGFLAG_SERVER, "Enables savegames (/save and /load)")
|
||||
MACRO_CONFIG_INT(SvSaveGamesDelay, sv_savegames_delay, 60, 0, 10000, CFGFLAG_SERVER, "Delay in seconds for loading a savegame")
|
||||
#if defined(CONF_SQL)
|
||||
MACRO_CONFIG_INT(SvUseSQL, sv_use_sql, 0, 0, 1, CFGFLAG_SERVER, "Enables SQL DB instead of record file")
|
||||
MACRO_CONFIG_STR(SvSqlFailureFile, sv_sql_failure_file, 64, "failed_sql.sql", CFGFLAG_SERVER, "File to store failed Sql-Inserts (ranks)")
|
||||
MACRO_CONFIG_INT(SvSqlQueriesDelay, sv_sql_queries_delay, 1, 0, 20, CFGFLAG_SERVER, "Delay in seconds between SQL queries of a single player")
|
||||
#endif
|
||||
MACRO_CONFIG_STR(SvSqliteFile, sv_sqlite_file, 64, "ddnet.sqlite", CFGFLAG_SERVER, "File to store ranks in case sv_use_sql is turned off")
|
||||
|
||||
#if defined(CONF_UPNP)
|
||||
MACRO_CONFIG_INT(SvUseUPnP, sv_use_upnp, 0, 0, 1, CFGFLAG_SERVER, "Enables UPnP support.")
|
||||
|
|
|
@ -24,10 +24,6 @@
|
|||
|
||||
#include "gamemodes/DDRace.h"
|
||||
#include "score.h"
|
||||
#include "score/file_score.h"
|
||||
#if defined(CONF_SQL)
|
||||
#include "score/sql_score.h"
|
||||
#endif
|
||||
|
||||
enum
|
||||
{
|
||||
|
@ -3116,17 +3112,11 @@ void CGameContext::OnInit(/*class IKernel *pKernel*/)
|
|||
}
|
||||
}
|
||||
|
||||
// delete old score object
|
||||
if(m_pScore)
|
||||
delete m_pScore;
|
||||
if(!m_pScore)
|
||||
{
|
||||
m_pScore = new CScore(this, ((CServer *)Server())->DbPool());
|
||||
}
|
||||
|
||||
// create score object (add sql later)
|
||||
#if defined(CONF_SQL)
|
||||
if(g_Config.m_SvUseSQL)
|
||||
m_pScore = new CSqlScore(this);
|
||||
else
|
||||
#endif
|
||||
m_pScore = new CFileScore(this);
|
||||
// setup core world
|
||||
//for(int i = 0; i < MAX_CLIENTS; i++)
|
||||
// game.players[i].core.world = &game.world.core;
|
||||
|
@ -3376,11 +3366,8 @@ void CGameContext::OnMapChange(char *pNewMapName, int MapNameSize)
|
|||
str_copy(m_aDeleteTempfile, aTemp, sizeof(m_aDeleteTempfile));
|
||||
}
|
||||
|
||||
void CGameContext::OnShutdown(bool FullShutdown)
|
||||
void CGameContext::OnShutdown()
|
||||
{
|
||||
if (FullShutdown)
|
||||
Score()->OnShutdown();
|
||||
|
||||
Antibot()->RoundEnd();
|
||||
|
||||
if(m_TeeHistorianActive)
|
||||
|
|
|
@ -18,7 +18,6 @@
|
|||
#include "player.h"
|
||||
#include "teehistorian.h"
|
||||
|
||||
#include "score.h"
|
||||
#ifdef _MSC_VER
|
||||
typedef __int32 int32_t;
|
||||
typedef unsigned __int32 uint32_t;
|
||||
|
@ -54,6 +53,7 @@ enum
|
|||
NUM_TUNEZONES = 256
|
||||
};
|
||||
|
||||
class CScore;
|
||||
class IConsole;
|
||||
class IEngine;
|
||||
class IStorage;
|
||||
|
@ -227,7 +227,7 @@ public:
|
|||
virtual void OnInit();
|
||||
virtual void OnConsoleInit();
|
||||
virtual void OnMapChange(char *pNewMapName, int MapNameSize);
|
||||
virtual void OnShutdown(bool FullShutdown = false);
|
||||
virtual void OnShutdown();
|
||||
|
||||
virtual void OnTick();
|
||||
virtual void OnPreSnap();
|
||||
|
@ -278,7 +278,7 @@ public:
|
|||
private:
|
||||
|
||||
bool m_VoteWillPass;
|
||||
class IScore *m_pScore;
|
||||
class CScore *m_pScore;
|
||||
|
||||
//DDRace Console Commands
|
||||
|
||||
|
@ -411,7 +411,7 @@ private:
|
|||
|
||||
public:
|
||||
CLayers *Layers() { return &m_Layers; }
|
||||
class IScore *Score() { return m_pScore; }
|
||||
class CScore *Score() { return m_pScore; }
|
||||
|
||||
enum
|
||||
{
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -4,10 +4,18 @@
|
|||
#include <memory>
|
||||
#include <atomic>
|
||||
|
||||
#include <game/voting.h>
|
||||
#include <engine/map.h>
|
||||
#include <engine/server/databases/connection_pool.h>
|
||||
#include <game/voting.h>
|
||||
#include <game/prng.h>
|
||||
|
||||
#include "save.h"
|
||||
|
||||
struct ISqlData;
|
||||
class IDbConnection;
|
||||
class IServer;
|
||||
class CGameContext;
|
||||
|
||||
enum
|
||||
{
|
||||
NUM_CHECKPOINTS = 25,
|
||||
|
@ -132,43 +140,193 @@ public:
|
|||
float m_aBestCpTime[NUM_CHECKPOINTS];
|
||||
};
|
||||
|
||||
class IScore
|
||||
struct CSqlInitData : ISqlData
|
||||
{
|
||||
CSqlInitData(std::shared_ptr<CScoreInitResult> pResult) :
|
||||
m_pResult(pResult)
|
||||
{}
|
||||
std::shared_ptr<CScoreInitResult> m_pResult;
|
||||
|
||||
// current map
|
||||
char m_Map[MAX_MAP_LENGTH];
|
||||
};
|
||||
|
||||
struct CSqlPlayerRequest : ISqlData
|
||||
{
|
||||
CSqlPlayerRequest(std::shared_ptr<CScorePlayerResult> pResult) :
|
||||
m_pResult(pResult)
|
||||
{}
|
||||
std::shared_ptr<CScorePlayerResult> m_pResult;
|
||||
// object being requested, either map (128 bytes) or player (16 bytes)
|
||||
char m_Name[MAX_MAP_LENGTH];
|
||||
// current map
|
||||
char m_Map[MAX_MAP_LENGTH];
|
||||
char m_RequestingPlayer[MAX_NAME_LENGTH];
|
||||
// relevant for /top5 kind of requests
|
||||
int m_Offset;
|
||||
};
|
||||
|
||||
struct CSqlRandomMapRequest : ISqlData
|
||||
{
|
||||
CSqlRandomMapRequest(std::shared_ptr<CScoreRandomMapResult> pResult) :
|
||||
m_pResult(pResult)
|
||||
{}
|
||||
std::shared_ptr<CScoreRandomMapResult> m_pResult;
|
||||
|
||||
char m_ServerType[32];
|
||||
char m_CurrentMap[MAX_MAP_LENGTH];
|
||||
char m_RequestingPlayer[MAX_NAME_LENGTH];
|
||||
int m_Stars;
|
||||
};
|
||||
|
||||
struct CSqlScoreData : ISqlData
|
||||
{
|
||||
CSqlScoreData(std::shared_ptr<CScorePlayerResult> pResult) :
|
||||
m_pResult(pResult)
|
||||
{}
|
||||
virtual ~CSqlScoreData() {};
|
||||
|
||||
std::shared_ptr<CScorePlayerResult> m_pResult;
|
||||
|
||||
char m_Map[MAX_MAP_LENGTH];
|
||||
char m_GameUuid[UUID_MAXSTRSIZE];
|
||||
char m_Name[MAX_MAP_LENGTH];
|
||||
|
||||
int m_ClientID;
|
||||
float m_Time;
|
||||
char m_aTimestamp[TIMESTAMP_STR_LENGTH];
|
||||
float m_aCpCurrent[NUM_CHECKPOINTS];
|
||||
int m_Num;
|
||||
bool m_Search;
|
||||
char m_aRequestingPlayer[MAX_NAME_LENGTH];
|
||||
};
|
||||
|
||||
struct CSqlTeamScoreData : ISqlData
|
||||
{
|
||||
char m_GameUuid[UUID_MAXSTRSIZE];
|
||||
char m_Map[MAX_MAP_LENGTH];
|
||||
float m_Time;
|
||||
char m_aTimestamp[TIMESTAMP_STR_LENGTH];
|
||||
unsigned int m_Size;
|
||||
char m_aNames[MAX_CLIENTS][MAX_NAME_LENGTH];
|
||||
};
|
||||
|
||||
struct CSqlTeamSave : ISqlData
|
||||
{
|
||||
CSqlTeamSave(std::shared_ptr<CScoreSaveResult> pResult) :
|
||||
m_pResult(pResult)
|
||||
{}
|
||||
virtual ~CSqlTeamSave() {};
|
||||
|
||||
std::shared_ptr<CScoreSaveResult> m_pResult;
|
||||
|
||||
char m_ClientName[MAX_NAME_LENGTH];
|
||||
char m_Map[MAX_MAP_LENGTH];
|
||||
char m_Code[128];
|
||||
char m_aGeneratedCode[128];
|
||||
char m_Server[5];
|
||||
};
|
||||
|
||||
struct CSqlTeamLoad : ISqlData
|
||||
{
|
||||
CSqlTeamLoad(std::shared_ptr<CScoreSaveResult> pResult) :
|
||||
m_pResult(pResult)
|
||||
{}
|
||||
virtual ~CSqlTeamLoad() {};
|
||||
|
||||
std::shared_ptr<CScoreSaveResult> m_pResult;
|
||||
|
||||
char m_Code[128];
|
||||
char m_Map[MAX_MAP_LENGTH];
|
||||
char m_RequestingPlayer[MAX_NAME_LENGTH];
|
||||
int m_ClientID;
|
||||
// struct holding all player names in the team or an empty string
|
||||
char m_aClientNames[MAX_CLIENTS][MAX_NAME_LENGTH];
|
||||
int m_aClientID[MAX_CLIENTS];
|
||||
int m_NumPlayer;
|
||||
};
|
||||
|
||||
class CScore
|
||||
{
|
||||
CPlayerData m_aPlayerData[MAX_CLIENTS];
|
||||
CDbConnectionPool *m_pPool;
|
||||
|
||||
static bool Init(IDbConnection *pSqlServer, const ISqlData *pGameData);
|
||||
|
||||
static bool RandomMapThread(IDbConnection *pSqlServer, const ISqlData *pGameData);
|
||||
static bool RandomUnfinishedMapThread(IDbConnection *pSqlServer, const ISqlData *pGameData);
|
||||
static bool MapVoteThread(IDbConnection *pSqlServer, const ISqlData *pGameData);
|
||||
|
||||
static bool LoadPlayerDataThread(IDbConnection *pSqlServer, const ISqlData *pGameData);
|
||||
static bool MapInfoThread(IDbConnection *pSqlServer, const ISqlData *pGameData);
|
||||
static bool ShowRankThread(IDbConnection *pSqlServer, const ISqlData *pGameData);
|
||||
static bool ShowTeamRankThread(IDbConnection *pSqlServer, const ISqlData *pGameData);
|
||||
static bool ShowTop5Thread(IDbConnection *pSqlServer, const ISqlData *pGameData);
|
||||
static bool ShowTeamTop5Thread(IDbConnection *pSqlServer, const ISqlData *pGameData);
|
||||
static bool ShowTimesThread(IDbConnection *pSqlServer, const ISqlData *pGameData);
|
||||
static bool ShowPointsThread(IDbConnection *pSqlServer, const ISqlData *pGameData);
|
||||
static bool ShowTopPointsThread(IDbConnection *pSqlServer, const ISqlData *pGameData);
|
||||
static bool GetSavesThread(IDbConnection *pSqlServer, const ISqlData *pGameData);
|
||||
|
||||
static bool SaveTeamThread(IDbConnection *pSqlServer, const ISqlData *pGameData, bool Failure);
|
||||
static bool LoadTeamThread(IDbConnection *pSqlServer, const ISqlData *pGameData);
|
||||
|
||||
static bool SaveScoreThread(IDbConnection *pSqlServer, const ISqlData *pGameData, bool Failure);
|
||||
static bool SaveTeamScoreThread(IDbConnection *pSqlServer, const ISqlData *pGameData, bool Failure);
|
||||
|
||||
CGameContext *GameServer() const { return m_pGameServer; }
|
||||
IServer *Server() const { return m_pServer; }
|
||||
CGameContext *m_pGameServer;
|
||||
IServer *m_pServer;
|
||||
|
||||
std::vector<std::string> m_aWordlist;
|
||||
CPrng m_Prng;
|
||||
void GeneratePassphrase(char *pBuf, int BufSize);
|
||||
|
||||
// returns new SqlResult bound to the player, if no current Thread is active for this player
|
||||
std::shared_ptr<CScorePlayerResult> NewSqlPlayerResult(int ClientID);
|
||||
// Creates for player database requests
|
||||
void ExecPlayerThread(
|
||||
bool (*pFuncPtr) (IDbConnection *, const ISqlData *),
|
||||
const char *pThreadName,
|
||||
int ClientID,
|
||||
const char *pName,
|
||||
int Offset);
|
||||
|
||||
// returns true if the player should be rate limited
|
||||
bool RateLimitPlayer(int ClientID);
|
||||
|
||||
public:
|
||||
virtual ~IScore() {}
|
||||
CScore(CGameContext *pGameServer, CDbConnectionPool *pPool);
|
||||
~CScore() {}
|
||||
|
||||
CPlayerData *PlayerData(int ID) { return &m_aPlayerData[ID]; }
|
||||
|
||||
virtual void MapInfo(int ClientID, const char *pMapName) = 0;
|
||||
virtual void MapVote(int ClientID, const char *pMapName) = 0;
|
||||
virtual void LoadPlayerData(int ClientID) = 0;
|
||||
virtual void SaveScore(int ClientID, float Time, const char *pTimestamp, float aCpTime[NUM_CHECKPOINTS], bool NotEligible) = 0;
|
||||
void MapInfo(int ClientID, const char *pMapName);
|
||||
void MapVote(int ClientID, const char *pMapName);
|
||||
void LoadPlayerData(int ClientID);
|
||||
void SaveScore(int ClientID, float Time, const char *pTimestamp, float aCpTime[NUM_CHECKPOINTS], bool NotEligible);
|
||||
|
||||
virtual void SaveTeamScore(int *pClientIDs, unsigned int Size, float Time, const char *pTimestamp) = 0;
|
||||
void SaveTeamScore(int *pClientIDs, unsigned int Size, float Time, const char *pTimestamp);
|
||||
|
||||
virtual void ShowTop5(int ClientID, int Offset=1) = 0;
|
||||
virtual void ShowRank(int ClientID, const char *pName) = 0;
|
||||
void ShowTop5(int ClientID, int Offset=1);
|
||||
void ShowRank(int ClientID, const char *pName);
|
||||
|
||||
virtual void ShowTeamTop5(int ClientID, int Offset=1) = 0;
|
||||
virtual void ShowTeamRank(int ClientID, const char *pName) = 0;
|
||||
void ShowTeamTop5(int ClientID, int Offset=1);
|
||||
void ShowTeamRank(int ClientID, const char *pName);
|
||||
|
||||
virtual void ShowTopPoints(int ClientID, int Offset=1) = 0;
|
||||
virtual void ShowPoints(int ClientID, const char *pName) = 0;
|
||||
void ShowTopPoints(int ClientID, int Offset=1);
|
||||
void ShowPoints(int ClientID, const char *pName);
|
||||
|
||||
virtual void ShowTimes(int ClientID, const char *pName, int Offset = 1) = 0;
|
||||
virtual void ShowTimes(int ClientID, int Offset = 1) = 0;
|
||||
void ShowTimes(int ClientID, const char *pName, int Offset = 1);
|
||||
void ShowTimes(int ClientID, int Offset = 1);
|
||||
|
||||
virtual void RandomMap(int ClientID, int Stars) = 0;
|
||||
virtual void RandomUnfinishedMap(int ClientID, int Stars) = 0;
|
||||
void RandomMap(int ClientID, int Stars);
|
||||
void RandomUnfinishedMap(int ClientID, int Stars);
|
||||
|
||||
virtual void SaveTeam(int ClientID, const char *pCode, const char *pServer) = 0;
|
||||
virtual void LoadTeam(const char *pCode, int ClientID) = 0;
|
||||
virtual void GetSaves(int ClientID) = 0;
|
||||
|
||||
// called when the server is shut down but not on mapchange/reload
|
||||
virtual void OnShutdown() = 0;
|
||||
void SaveTeam(int ClientID, const char *pCode, const char *pServer);
|
||||
void LoadTeam(const char *pCode, int ClientID);
|
||||
void GetSaves(int ClientID);
|
||||
};
|
||||
|
||||
#endif // GAME_SERVER_SCORE_H
|
||||
|
|
|
@ -1,372 +0,0 @@
|
|||
/* (c) Shereef Marzouk. See "licence DDRace.txt" and the readme.txt in the root of the distribution for more information. */
|
||||
/* Based on Race mod stuff and tweaked by GreYFoX@GTi and others to fit our DDRace needs. */
|
||||
/* copyright (c) 2008 rajh and gregwar. Score stuff */
|
||||
#include <base/tl/sorted_array.h>
|
||||
|
||||
#include <engine/shared/config.h>
|
||||
#include <sstream>
|
||||
#include <fstream>
|
||||
#include "../gamemodes/DDRace.h"
|
||||
#include "file_score.h"
|
||||
#include <engine/shared/console.h>
|
||||
|
||||
static LOCK gs_ScoreLock = 0;
|
||||
|
||||
CFileScore::CPlayerScore::CPlayerScore(const char *pName, float Score,
|
||||
float aCpTime[NUM_CHECKPOINTS])
|
||||
{
|
||||
str_copy(m_aName, pName, sizeof(m_aName));
|
||||
m_Score = Score;
|
||||
for (int i = 0; i < NUM_CHECKPOINTS; i++)
|
||||
m_aCpTime[i] = aCpTime[i];
|
||||
}
|
||||
|
||||
CFileScore::CFileScore(CGameContext *pGameServer) :
|
||||
m_pGameServer(pGameServer), m_pServer(pGameServer->Server())
|
||||
{
|
||||
if (gs_ScoreLock == 0)
|
||||
gs_ScoreLock = lock_create();
|
||||
|
||||
Init();
|
||||
}
|
||||
|
||||
CFileScore::~CFileScore()
|
||||
{
|
||||
lock_wait(gs_ScoreLock);
|
||||
|
||||
// clear list
|
||||
m_Top.clear();
|
||||
|
||||
lock_unlock(gs_ScoreLock);
|
||||
}
|
||||
|
||||
std::string CFileScore::SaveFile()
|
||||
{
|
||||
std::ostringstream oss;
|
||||
char aBuf[256];
|
||||
str_copy(aBuf, Server()->GetMapName(), sizeof(aBuf));
|
||||
for(int i = 0; i < 256; i++) if(aBuf[i] == '/') aBuf[i] = '-';
|
||||
if (g_Config.m_SvScoreFolder[0])
|
||||
oss << g_Config.m_SvScoreFolder << "/" << aBuf << "_record.dtb";
|
||||
else
|
||||
oss << Server()->GetMapName() << "_record.dtb";
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
void CFileScore::MapInfo(int ClientID, const char* MapName)
|
||||
{
|
||||
// TODO: implement
|
||||
}
|
||||
|
||||
void CFileScore::MapVote(int ClientID, const char* MapName)
|
||||
{
|
||||
// TODO: implement
|
||||
}
|
||||
|
||||
void CFileScore::SaveScoreThread(void *pUser)
|
||||
{
|
||||
CFileScore *pSelf = (CFileScore *) pUser;
|
||||
lock_wait(gs_ScoreLock);
|
||||
std::fstream f;
|
||||
f.open(pSelf->SaveFile().c_str(), std::ios::out);
|
||||
if(f.fail())
|
||||
{
|
||||
dbg_msg("filescore", "opening '%s' for writing failed", pSelf->SaveFile().c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
int t = 0;
|
||||
for (sorted_array<CPlayerScore>::range r = pSelf->m_Top.all();
|
||||
!r.empty(); r.pop_front())
|
||||
{
|
||||
f << r.front().m_aName << std::endl << r.front().m_Score
|
||||
<< std::endl;
|
||||
if (g_Config.m_SvCheckpointSave)
|
||||
{
|
||||
for (int c = 0; c < NUM_CHECKPOINTS; c++)
|
||||
f << r.front().m_aCpTime[c] << " ";
|
||||
f << std::endl;
|
||||
}
|
||||
t++;
|
||||
if (t % 50 == 0)
|
||||
thread_sleep(1000);
|
||||
}
|
||||
}
|
||||
f.close();
|
||||
lock_unlock(gs_ScoreLock);
|
||||
}
|
||||
|
||||
void CFileScore::Save()
|
||||
{
|
||||
thread_init_and_detach(SaveScoreThread, this, "FileScore save");
|
||||
}
|
||||
|
||||
void CFileScore::Init()
|
||||
{
|
||||
lock_wait(gs_ScoreLock);
|
||||
|
||||
// create folder if not exist
|
||||
if (g_Config.m_SvScoreFolder[0])
|
||||
fs_makedir(g_Config.m_SvScoreFolder);
|
||||
|
||||
std::fstream f;
|
||||
f.open(SaveFile().c_str(), std::ios::in);
|
||||
|
||||
if(f.fail())
|
||||
{
|
||||
dbg_msg("filescore", "opening '%s' for reading failed", SaveFile().c_str());
|
||||
}
|
||||
while (!f.eof() && !f.fail())
|
||||
{
|
||||
std::string TmpName, TmpScore, TmpCpLine;
|
||||
std::getline(f, TmpName);
|
||||
if (!f.eof() && TmpName != "")
|
||||
{
|
||||
std::getline(f, TmpScore);
|
||||
float aTmpCpTime[NUM_CHECKPOINTS] =
|
||||
{ 0 };
|
||||
if (g_Config.m_SvCheckpointSave)
|
||||
{
|
||||
std::getline(f, TmpCpLine);
|
||||
|
||||
std::istringstream iss(TmpCpLine);
|
||||
int i = 0;
|
||||
for(std::string p; std::getline(iss, p, ' '); i++)
|
||||
aTmpCpTime[i] = std::stof(p, NULL);
|
||||
}
|
||||
m_Top.add(
|
||||
*new CPlayerScore(TmpName.c_str(), atof(TmpScore.c_str()),
|
||||
aTmpCpTime));
|
||||
}
|
||||
}
|
||||
f.close();
|
||||
lock_unlock(gs_ScoreLock);
|
||||
|
||||
// save the current best score
|
||||
if (m_Top.size())
|
||||
((CGameControllerDDRace*) GameServer()->m_pController)->m_CurrentRecord =
|
||||
m_Top[0].m_Score;
|
||||
}
|
||||
|
||||
CFileScore::CPlayerScore *CFileScore::SearchName(const char *pName,
|
||||
int *pPosition, bool NoCase)
|
||||
{
|
||||
CPlayerScore *pPlayer = 0;
|
||||
int Pos = 1;
|
||||
int Found = 0;
|
||||
for (sorted_array<CPlayerScore>::range r = m_Top.all(); !r.empty();
|
||||
r.pop_front())
|
||||
{
|
||||
if (str_find_nocase(r.front().m_aName, pName))
|
||||
{
|
||||
if (pPosition)
|
||||
*pPosition = Pos;
|
||||
if (NoCase)
|
||||
{
|
||||
Found++;
|
||||
pPlayer = &r.front();
|
||||
}
|
||||
if (!str_comp(r.front().m_aName, pName))
|
||||
return &r.front();
|
||||
}
|
||||
Pos++;
|
||||
}
|
||||
if (Found > 1)
|
||||
{
|
||||
if (pPosition)
|
||||
*pPosition = -1;
|
||||
return 0;
|
||||
}
|
||||
return pPlayer;
|
||||
}
|
||||
|
||||
void CFileScore::UpdatePlayer(int ID, float Score,
|
||||
float aCpTime[NUM_CHECKPOINTS])
|
||||
{
|
||||
const char *pName = Server()->ClientName(ID);
|
||||
|
||||
lock_wait(gs_ScoreLock);
|
||||
CPlayerScore *pPlayer = SearchScore(ID, 0);
|
||||
|
||||
if (pPlayer)
|
||||
{
|
||||
for (int c = 0; c < NUM_CHECKPOINTS; c++)
|
||||
pPlayer->m_aCpTime[c] = aCpTime[c];
|
||||
|
||||
pPlayer->m_Score = Score;
|
||||
str_copy(pPlayer->m_aName, pName, sizeof(pPlayer->m_aName));
|
||||
|
||||
sort(m_Top.all());
|
||||
}
|
||||
else
|
||||
m_Top.add(*new CPlayerScore(pName, Score, aCpTime));
|
||||
|
||||
lock_unlock(gs_ScoreLock);
|
||||
Save();
|
||||
}
|
||||
|
||||
void CFileScore::LoadPlayerData(int ClientID)
|
||||
{
|
||||
CPlayerScore *pPlayer = SearchScore(ClientID, 0);
|
||||
if (pPlayer)
|
||||
{
|
||||
lock_wait(gs_ScoreLock);
|
||||
lock_unlock(gs_ScoreLock);
|
||||
Save();
|
||||
}
|
||||
|
||||
// set score
|
||||
if (pPlayer)
|
||||
{
|
||||
PlayerData(ClientID)->Set(pPlayer->m_Score, pPlayer->m_aCpTime);
|
||||
GameServer()->m_apPlayers[ClientID]->m_HasFinishScore = true;
|
||||
}
|
||||
}
|
||||
|
||||
void CFileScore::SaveTeamScore(int* ClientIDs, unsigned int Size, float Time, const char *pTimestamp)
|
||||
{
|
||||
dbg_msg("filescore", "saveteamscore not implemented for filescore");
|
||||
}
|
||||
|
||||
void CFileScore::SaveScore(int ClientID, float Time, const char *pTimestamp,
|
||||
float CpTime[NUM_CHECKPOINTS], bool NotEligible)
|
||||
{
|
||||
CConsole* pCon = (CConsole*) GameServer()->Console();
|
||||
if (!pCon->m_Cheated || g_Config.m_SvRankCheats)
|
||||
UpdatePlayer(ClientID, Time, CpTime);
|
||||
}
|
||||
|
||||
void CFileScore::ShowTop5(int ClientID, int Offset)
|
||||
{
|
||||
char aBuf[512];
|
||||
Offset = maximum(1, Offset < 0 ? m_Top.size() + Offset - 3 : Offset);
|
||||
GameServer()->SendChatTarget(ClientID, "----------- Top 5 -----------");
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
if (i + Offset > m_Top.size())
|
||||
break;
|
||||
CPlayerScore *r = &m_Top[i + Offset - 1];
|
||||
str_format(aBuf, sizeof(aBuf),
|
||||
"%d. %s Time: %d minute(s) %5.2f second(s)", i + Offset,
|
||||
r->m_aName, (int)r->m_Score / 60,
|
||||
r->m_Score - ((int)r->m_Score / 60 * 60));
|
||||
GameServer()->SendChatTarget(ClientID, aBuf);
|
||||
}
|
||||
GameServer()->SendChatTarget(ClientID, "------------------------------");
|
||||
}
|
||||
|
||||
void CFileScore::ShowRank(int ClientID, const char* pName)
|
||||
{
|
||||
CPlayerScore *pScore;
|
||||
int Pos = -2;
|
||||
char aBuf[512];
|
||||
|
||||
pScore = SearchName(pName, &Pos, 1);
|
||||
|
||||
if (pScore && Pos > -1)
|
||||
{
|
||||
float Time = pScore->m_Score;
|
||||
if (g_Config.m_SvHideScore)
|
||||
str_format(aBuf, sizeof(aBuf),
|
||||
"Your time: %d minute(s) %5.2f second(s)", (int)Time / 60,
|
||||
Time - ((int)Time / 60 * 60));
|
||||
else
|
||||
str_format(aBuf, sizeof(aBuf),
|
||||
"%d. %s Time: %d minute(s) %5.2f second(s), requested by (%s)", Pos,
|
||||
pScore->m_aName, (int)Time / 60,
|
||||
Time - ((int)Time / 60 * 60), Server()->ClientName(ClientID));
|
||||
if (g_Config.m_SvHideScore)
|
||||
GameServer()->SendChatTarget(ClientID, aBuf);
|
||||
else
|
||||
GameServer()->SendChat(-1, CGameContext::CHAT_ALL, aBuf, ClientID);
|
||||
return;
|
||||
}
|
||||
else if (Pos == -1)
|
||||
str_format(aBuf, sizeof(aBuf), "Several players were found.");
|
||||
else
|
||||
str_format(aBuf, sizeof(aBuf), "%s is not ranked", pName);
|
||||
|
||||
GameServer()->SendChatTarget(ClientID, aBuf);
|
||||
}
|
||||
|
||||
void CFileScore::ShowTeamTop5(int ClientID, int Offset)
|
||||
{
|
||||
char aBuf[512];
|
||||
str_format(aBuf, sizeof(aBuf), "Team ranks not supported in file based servers");
|
||||
GameServer()->SendChatTarget(ClientID, aBuf);
|
||||
}
|
||||
|
||||
void CFileScore::ShowTeamRank(int ClientID, const char* pName)
|
||||
{
|
||||
char aBuf[512];
|
||||
str_format(aBuf, sizeof(aBuf), "Team ranks not supported in file based servers");
|
||||
GameServer()->SendChatTarget(ClientID, aBuf);
|
||||
}
|
||||
|
||||
void CFileScore::ShowTopPoints(int ClientID, int Offset)
|
||||
{
|
||||
char aBuf[512];
|
||||
str_format(aBuf, sizeof(aBuf), "Team ranks not supported in file based servers");
|
||||
GameServer()->SendChatTarget(ClientID, aBuf);
|
||||
}
|
||||
|
||||
void CFileScore::ShowPoints(int ClientID, const char* pName)
|
||||
{
|
||||
char aBuf[512];
|
||||
str_format(aBuf, sizeof(aBuf), "Points not supported in file based servers");
|
||||
GameServer()->SendChatTarget(ClientID, aBuf);
|
||||
}
|
||||
|
||||
void CFileScore::ShowTimes(int ClientID, const char *pName, int Offset)
|
||||
{
|
||||
char aBuf[512];
|
||||
str_format(aBuf, sizeof(aBuf), "Show times not supported in file based servers");
|
||||
GameServer()->SendChatTarget(ClientID, aBuf);
|
||||
}
|
||||
|
||||
void CFileScore::ShowTimes(int ClientID, int Offset)
|
||||
{
|
||||
char aBuf[512];
|
||||
str_format(aBuf, sizeof(aBuf), "Show times not supported in file based servers");
|
||||
GameServer()->SendChatTarget(ClientID, aBuf);
|
||||
}
|
||||
|
||||
void CFileScore::RandomMap(int ClientID, int Stars)
|
||||
{
|
||||
char aBuf[512];
|
||||
str_format(aBuf, sizeof(aBuf), "Random map not supported in file based servers");
|
||||
GameServer()->SendChatTarget(ClientID, aBuf);
|
||||
}
|
||||
|
||||
void CFileScore::RandomUnfinishedMap(int ClientID, int Stars)
|
||||
{
|
||||
char aBuf[512];
|
||||
str_format(aBuf, sizeof(aBuf), "Random unfinished map not supported in file based servers");
|
||||
GameServer()->SendChatTarget(ClientID, aBuf);
|
||||
}
|
||||
|
||||
void CFileScore::SaveTeam(int ClientID, const char* Code, const char* Server)
|
||||
{
|
||||
char aBuf[512];
|
||||
str_format(aBuf, sizeof(aBuf), "Save-function not supported in file based servers");
|
||||
GameServer()->SendChatTarget(ClientID, aBuf);
|
||||
}
|
||||
|
||||
void CFileScore::LoadTeam(const char* Code, int ClientID)
|
||||
{
|
||||
char aBuf[512];
|
||||
str_format(aBuf, sizeof(aBuf), "Save-function not supported in file based servers");
|
||||
GameServer()->SendChatTarget(ClientID, aBuf);
|
||||
}
|
||||
|
||||
void CFileScore::GetSaves(int ClientID)
|
||||
{
|
||||
char aBuf[512];
|
||||
str_format(aBuf, sizeof(aBuf), "Save-function not supported in file based servers");
|
||||
GameServer()->SendChatTarget(ClientID, aBuf);
|
||||
}
|
||||
|
||||
void CFileScore::OnShutdown()
|
||||
{
|
||||
;
|
||||
}
|
|
@ -1,92 +0,0 @@
|
|||
/* (c) Shereef Marzouk. See "licence DDRace.txt" and the readme.txt in the root of the distribution for more information. */
|
||||
/* Based on Race mod stuff and tweaked by GreYFoX@GTi and others to fit our DDRace needs. */
|
||||
/* copyright (c) 2008 rajh and gregwar. Score stuff */
|
||||
#ifndef GAME_SERVER_SCORE_FILE_SCORE_H
|
||||
#define GAME_SERVER_SCORE_FILE_SCORE_H
|
||||
|
||||
#include <base/tl/sorted_array.h>
|
||||
|
||||
#include <string>
|
||||
#include "../score.h"
|
||||
|
||||
class CFileScore: public IScore
|
||||
{
|
||||
CGameContext *m_pGameServer;
|
||||
IServer *m_pServer;
|
||||
|
||||
class CPlayerScore
|
||||
{
|
||||
public:
|
||||
char m_aName[MAX_NAME_LENGTH];
|
||||
float m_Score;
|
||||
float m_aCpTime[NUM_CHECKPOINTS];
|
||||
|
||||
CPlayerScore() {}
|
||||
|
||||
CPlayerScore(const char *pName, float Score,
|
||||
float aCpTime[NUM_CHECKPOINTS]);
|
||||
|
||||
bool operator<(const CPlayerScore& other)
|
||||
{
|
||||
return (this->m_Score < other.m_Score);
|
||||
}
|
||||
};
|
||||
|
||||
sorted_array<CPlayerScore> m_Top;
|
||||
|
||||
CGameContext *GameServer()
|
||||
{
|
||||
return m_pGameServer;
|
||||
}
|
||||
IServer *Server()
|
||||
{
|
||||
return m_pServer;
|
||||
}
|
||||
|
||||
CPlayerScore *SearchScore(int ID, int *pPosition)
|
||||
{
|
||||
return SearchName(Server()->ClientName(ID), pPosition, 0);
|
||||
}
|
||||
|
||||
CPlayerScore *SearchName(const char *pName, int *pPosition, bool MatchCase);
|
||||
void UpdatePlayer(int ID, float Score, float aCpTime[NUM_CHECKPOINTS]);
|
||||
|
||||
void Init();
|
||||
void Save();
|
||||
static void SaveScoreThread(void *pUser);
|
||||
|
||||
public:
|
||||
|
||||
CFileScore(CGameContext *pGameServer);
|
||||
~CFileScore();
|
||||
|
||||
virtual void LoadPlayerData(int ClientID);
|
||||
virtual void MapInfo(int ClientID, const char* MapName);
|
||||
virtual void MapVote(int ClientID, const char* MapName);
|
||||
virtual void SaveScore(int ClientID, float Time, const char *pTimestamp,
|
||||
float CpTime[NUM_CHECKPOINTS], bool NotEligible);
|
||||
virtual void SaveTeamScore(int* ClientIDs, unsigned int Size, float Time, const char *pTimestamp);
|
||||
|
||||
virtual void ShowTop5(int ClientID, int Offset = 1);
|
||||
virtual void ShowRank(int ClientID, const char* pName);
|
||||
|
||||
virtual void ShowTeamTop5(int ClientID, int Offset = 1);
|
||||
virtual void ShowTeamRank(int ClientID, const char* pName);
|
||||
|
||||
virtual void ShowTopPoints(int ClientID, int Offset);
|
||||
virtual void ShowPoints(int ClientID, const char* pName);
|
||||
virtual void ShowTimes(int ClientID, const char *pName, int Offset = 1);
|
||||
virtual void ShowTimes(int ClientID, int Offset = 1);
|
||||
virtual void RandomMap(int ClientID, int Stars);
|
||||
virtual void RandomUnfinishedMap(int ClientID, int Stars);
|
||||
virtual void SaveTeam(int ClientID, const char* Code, const char* Server);
|
||||
virtual void LoadTeam(const char* Code, int ClientID);
|
||||
virtual void GetSaves(int ClientID);
|
||||
|
||||
virtual void OnShutdown();
|
||||
|
||||
private:
|
||||
std::string SaveFile();
|
||||
};
|
||||
|
||||
#endif // GAME_SERVER_SCORE_FILE_SCORE_H
|
File diff suppressed because it is too large
Load diff
|
@ -1,220 +0,0 @@
|
|||
/* (c) Shereef Marzouk. See "licence DDRace.txt" and the readme.txt in the root of the distribution for more information. */
|
||||
/* Based on Race mod stuff and tweaked by GreYFoX@GTi and others to fit our DDRace needs. */
|
||||
/* CSqlScore Class by Sushi Tee*/
|
||||
#ifndef GAME_SERVER_SCORE_SQL_H
|
||||
#define GAME_SERVER_SCORE_SQL_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <engine/map.h>
|
||||
#include <engine/server/sql_string_helpers.h>
|
||||
#include <game/prng.h>
|
||||
|
||||
#include "../score.h"
|
||||
|
||||
class CSqlServer;
|
||||
|
||||
// holding relevant data for one thread, and function pointer for return values
|
||||
template < typename TResult >
|
||||
struct CSqlData
|
||||
{
|
||||
CSqlData(std::shared_ptr<TResult> pSqlResult) :
|
||||
m_pResult(pSqlResult)
|
||||
{ }
|
||||
std::shared_ptr<TResult> m_pResult;
|
||||
virtual ~CSqlData() = default;
|
||||
};
|
||||
|
||||
struct CSqlInitData : CSqlData<CScoreInitResult>
|
||||
{
|
||||
using CSqlData<CScoreInitResult>::CSqlData;
|
||||
// current map
|
||||
sqlstr::CSqlString<MAX_MAP_LENGTH> m_Map;
|
||||
};
|
||||
|
||||
struct CSqlPlayerRequest : CSqlData<CScorePlayerResult>
|
||||
{
|
||||
using CSqlData<CScorePlayerResult>::CSqlData;
|
||||
// object being requested, either map (128 bytes) or player (16 bytes)
|
||||
sqlstr::CSqlString<MAX_MAP_LENGTH> m_Name;
|
||||
// current map
|
||||
sqlstr::CSqlString<MAX_MAP_LENGTH> m_Map;
|
||||
sqlstr::CSqlString<MAX_NAME_LENGTH> m_RequestingPlayer;
|
||||
// relevant for /top5 kind of requests
|
||||
int m_Offset;
|
||||
};
|
||||
|
||||
struct CSqlRandomMapRequest : CSqlData<CScoreRandomMapResult>
|
||||
{
|
||||
using CSqlData<CScoreRandomMapResult>::CSqlData;
|
||||
sqlstr::CSqlString<32> m_ServerType;
|
||||
sqlstr::CSqlString<MAX_MAP_LENGTH> m_CurrentMap;
|
||||
sqlstr::CSqlString<MAX_NAME_LENGTH> m_RequestingPlayer;
|
||||
int m_Stars;
|
||||
};
|
||||
|
||||
struct CSqlScoreData : CSqlData<CScorePlayerResult>
|
||||
{
|
||||
using CSqlData<CScorePlayerResult>::CSqlData;
|
||||
|
||||
sqlstr::CSqlString<MAX_MAP_LENGTH> m_Map;
|
||||
char m_GameUuid[UUID_MAXSTRSIZE];
|
||||
sqlstr::CSqlString<MAX_NAME_LENGTH> m_Name;
|
||||
|
||||
int m_ClientID;
|
||||
float m_Time;
|
||||
char m_aTimestamp[TIMESTAMP_STR_LENGTH];
|
||||
float m_aCpCurrent[NUM_CHECKPOINTS];
|
||||
int m_Num;
|
||||
bool m_Search;
|
||||
char m_aRequestingPlayer[MAX_NAME_LENGTH];
|
||||
};
|
||||
|
||||
struct CSqlTeamScoreData : CSqlData<void>
|
||||
{
|
||||
using CSqlData<void>::CSqlData;
|
||||
char m_GameUuid[UUID_MAXSTRSIZE];
|
||||
sqlstr::CSqlString<MAX_MAP_LENGTH> m_Map;
|
||||
float m_Time;
|
||||
char m_aTimestamp[TIMESTAMP_STR_LENGTH];
|
||||
unsigned int m_Size;
|
||||
sqlstr::CSqlString<MAX_NAME_LENGTH> m_aNames[MAX_CLIENTS];
|
||||
};
|
||||
|
||||
struct CSqlTeamSave : CSqlData<CScoreSaveResult>
|
||||
{
|
||||
using CSqlData<CScoreSaveResult>::CSqlData;
|
||||
virtual ~CSqlTeamSave() {};
|
||||
|
||||
char m_ClientName[MAX_NAME_LENGTH];
|
||||
|
||||
char m_Map[MAX_MAP_LENGTH];
|
||||
char m_Code[128];
|
||||
char m_aGeneratedCode[128];
|
||||
char m_Server[5];
|
||||
};
|
||||
|
||||
struct CSqlTeamLoad : CSqlData<CScoreSaveResult>
|
||||
{
|
||||
using CSqlData<CScoreSaveResult>::CSqlData;
|
||||
sqlstr::CSqlString<128> m_Code;
|
||||
sqlstr::CSqlString<MAX_MAP_LENGTH> m_Map;
|
||||
sqlstr::CSqlString<MAX_NAME_LENGTH> m_RequestingPlayer;
|
||||
int m_ClientID;
|
||||
// struct holding all player names in the team or an empty string
|
||||
char m_aClientNames[MAX_CLIENTS][MAX_NAME_LENGTH];
|
||||
int m_aClientID[MAX_CLIENTS];
|
||||
int m_NumPlayer;
|
||||
};
|
||||
|
||||
// controls one thread
|
||||
template < typename TResult >
|
||||
struct CSqlExecData
|
||||
{
|
||||
CSqlExecData(
|
||||
bool (*pFuncPtr) (CSqlServer*, const CSqlData<TResult> *, bool),
|
||||
CSqlData<TResult> *pSqlResult,
|
||||
bool ReadOnly = true
|
||||
);
|
||||
~CSqlExecData();
|
||||
|
||||
bool (*m_pFuncPtr) (CSqlServer*, const CSqlData<TResult> *, bool);
|
||||
CSqlData<TResult> *m_pSqlData;
|
||||
bool m_ReadOnly;
|
||||
|
||||
static void ExecSqlFunc(void *pUser);
|
||||
};
|
||||
|
||||
class IServer;
|
||||
class CGameContext;
|
||||
|
||||
class CSqlScore: public IScore
|
||||
{
|
||||
static LOCK ms_FailureFileLock;
|
||||
|
||||
static bool Init(CSqlServer* pSqlServer, const CSqlData<CScoreInitResult> *pGameData, bool HandleFailure);
|
||||
|
||||
static bool RandomMapThread(CSqlServer* pSqlServer, const CSqlData<CScoreRandomMapResult> *pGameData, bool HandleFailure = false);
|
||||
static bool RandomUnfinishedMapThread(CSqlServer* pSqlServer, const CSqlData<CScoreRandomMapResult> *pGameData, bool HandleFailure = false);
|
||||
static bool MapVoteThread(CSqlServer* pSqlServer, const CSqlData<CScorePlayerResult> *pGameData, bool HandleFailure = false);
|
||||
|
||||
static bool LoadPlayerDataThread(CSqlServer* pSqlServer, const CSqlData<CScorePlayerResult> *pGameData, bool HandleFailure = false);
|
||||
static bool MapInfoThread(CSqlServer* pSqlServer, const CSqlData<CScorePlayerResult> *pGameData, bool HandleFailure = false);
|
||||
static bool ShowRankThread(CSqlServer* pSqlServer, const CSqlData<CScorePlayerResult> *pGameData, bool HandleFailure = false);
|
||||
static bool ShowTeamRankThread(CSqlServer* pSqlServer, const CSqlData<CScorePlayerResult> *pGameData, bool HandleFailure = false);
|
||||
static bool ShowTop5Thread(CSqlServer* pSqlServer, const CSqlData<CScorePlayerResult> *pGameData, bool HandleFailure = false);
|
||||
static bool ShowTeamTop5Thread(CSqlServer* pSqlServer, const CSqlData<CScorePlayerResult> *pGameData, bool HandleFailure = false);
|
||||
static bool ShowTimesThread(CSqlServer* pSqlServer, const CSqlData<CScorePlayerResult> *pGameData, bool HandleFailure = false);
|
||||
static bool ShowPointsThread(CSqlServer* pSqlServer, const CSqlData<CScorePlayerResult> *pGameData, bool HandleFailure = false);
|
||||
static bool ShowTopPointsThread(CSqlServer* pSqlServer, const CSqlData<CScorePlayerResult> *pGameData, bool HandleFailure = false);
|
||||
static bool GetSavesThread(CSqlServer* pSqlServer, const CSqlData<CScorePlayerResult> *pGameData, bool HandleFailure = false);
|
||||
|
||||
static bool SaveTeamThread(CSqlServer* pSqlServer, const CSqlData<CScoreSaveResult> *pGameData, bool HandleFailure = false);
|
||||
static bool LoadTeamThread(CSqlServer* pSqlServer, const CSqlData<CScoreSaveResult> *pGameData, bool HandleFailure = false);
|
||||
|
||||
static bool SaveScoreThread(CSqlServer* pSqlServer, const CSqlData<CScorePlayerResult> *pGameData, bool HandleFailure = false);
|
||||
static bool SaveTeamScoreThread(CSqlServer* pSqlServer, const CSqlData<void> *pGameData, bool HandleFailure = false);
|
||||
|
||||
CGameContext *GameServer() { return m_pGameServer; }
|
||||
IServer *Server() { return m_pServer; }
|
||||
|
||||
CGameContext *m_pGameServer;
|
||||
IServer *m_pServer;
|
||||
|
||||
std::vector<std::string> m_aWordlist;
|
||||
CPrng m_Prng;
|
||||
void GeneratePassphrase(char *pBuf, int BufSize);
|
||||
|
||||
// returns new SqlResult bound to the player, if no current Thread is active for this player
|
||||
std::shared_ptr<CScorePlayerResult> NewSqlPlayerResult(int ClientID);
|
||||
// Creates for player database requests
|
||||
void ExecPlayerThread(
|
||||
bool (*pFuncPtr) (CSqlServer*, const CSqlData<CScorePlayerResult> *, bool),
|
||||
const char* pThreadName,
|
||||
int ClientID,
|
||||
const char* pName,
|
||||
int Offset
|
||||
);
|
||||
|
||||
// returns true if the player should be rate limited
|
||||
bool RateLimitPlayer(int ClientID);
|
||||
|
||||
public:
|
||||
// keeps track of score-threads
|
||||
static std::atomic_int ms_InstanceCount;
|
||||
|
||||
CSqlScore(CGameContext *pGameServer);
|
||||
~CSqlScore() {}
|
||||
|
||||
// Requested by game context, shouldn't fail in case the player started another thread
|
||||
virtual void RandomMap(int ClientID, int Stars);
|
||||
virtual void RandomUnfinishedMap(int ClientID, int Stars);
|
||||
virtual void MapVote(int ClientID, const char* MapName);
|
||||
|
||||
virtual void LoadPlayerData(int ClientID);
|
||||
// Requested by players (fails if another request by this player is active)
|
||||
virtual void MapInfo(int ClientID, const char* MapName);
|
||||
virtual void ShowRank(int ClientID, const char* pName);
|
||||
virtual void ShowTeamRank(int ClientID, const char* pName);
|
||||
virtual void ShowPoints(int ClientID, const char* pName);
|
||||
virtual void ShowTimes(int ClientID, const char* pName, int Offset = 1);
|
||||
virtual void ShowTimes(int ClientID, int Offset = 1);
|
||||
virtual void ShowTop5(int ClientID, int Offset = 1);
|
||||
virtual void ShowTeamTop5(int ClientID, int Offset = 1);
|
||||
virtual void ShowTopPoints(int ClientID, int Offset = 1);
|
||||
virtual void GetSaves(int ClientID);
|
||||
|
||||
// requested by teams
|
||||
virtual void SaveTeam(int ClientID, const char* Code, const char* Server);
|
||||
virtual void LoadTeam(const char* Code, int ClientID);
|
||||
|
||||
// Game relevant not allowed to fail due to an ongoing SQL request.
|
||||
virtual void SaveScore(int ClientID, float Time, const char *pTimestamp,
|
||||
float CpTime[NUM_CHECKPOINTS], bool NotEligible);
|
||||
virtual void SaveTeamScore(int* aClientIDs, unsigned int Size, float Time, const char *pTimestamp);
|
||||
|
||||
virtual void OnShutdown();
|
||||
};
|
||||
|
||||
#endif // GAME_SERVER_SCORE_SQL_H
|
Loading…
Reference in a new issue