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:
Zwelf 2020-07-04 19:53:27 +02:00
parent 295ce95d64
commit 2eb3d23ef4
20 changed files with 2181 additions and 2720 deletions

View file

@ -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

View file

@ -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;

View file

@ -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

View file

@ -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;
}

View file

@ -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

View file

@ -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
}

View file

@ -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

View file

@ -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");

View file

@ -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);

View file

@ -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)
{

View file

@ -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>

View file

@ -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.")

View file

@ -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)

View file

@ -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

View file

@ -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

View file

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

View file

@ -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

View file

@ -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