From e114a6387102cc58ac98dd3728f49d1763b2cced Mon Sep 17 00:00:00 2001 From: ST-Chara Date: Fri, 4 Oct 2024 13:58:01 +0800 Subject: [PATCH 1/3] Fix crash --- src/engine/shared/http.cpp | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/src/engine/shared/http.cpp b/src/engine/shared/http.cpp index c4c54813..8fe2d63f 100644 --- a/src/engine/shared/http.cpp +++ b/src/engine/shared/http.cpp @@ -216,7 +216,16 @@ int CHttpRequest::RunImpl(CURL *pUser) } curl_easy_setopt(pHandle, CURLOPT_SHARE, gs_pShare); - curl_easy_setopt(pHandle, CURLOPT_PROTOCOLS_STR, Protocols); + // ‘CURLOPT_PROTOCOLS’ is deprecated: since 7.85.0. Use CURLOPT_PROTOCOLS_STR + // Wait until all platforms have 7.85.0 +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" +#endif + curl_easy_setopt(pHandle, CURLOPT_PROTOCOLS, Protocols); +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif curl_easy_setopt(pHandle, CURLOPT_FOLLOWLOCATION, 1L); curl_easy_setopt(pHandle, CURLOPT_MAXREDIRS, 4L); curl_easy_setopt(pHandle, CURLOPT_FAILONERROR, 1L); @@ -229,9 +238,18 @@ int CHttpRequest::RunImpl(CURL *pUser) curl_easy_setopt(pHandle, CURLOPT_WRITEFUNCTION, WriteCallback); curl_easy_setopt(pHandle, CURLOPT_NOPROGRESS, 0L); curl_easy_setopt(pHandle, CURLOPT_PROGRESSDATA, this); - curl_easy_setopt(pHandle, CURLOPT_XFERINFOFUNCTION, ProgressCallback); + // ‘CURLOPT_PROGRESSFUNCTION’ is deprecated: since 7.32.0. Use CURLOPT_XFERINFOFUNCTION + // See problems with curl_off_t type in header file in https://github.com/ddnet/ddnet/pull/6185/ +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" +#endif + curl_easy_setopt(pHandle, CURLOPT_PROGRESSFUNCTION, ProgressCallback); +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif curl_easy_setopt(pHandle, CURLOPT_IPRESOLVE, m_IpResolve == IPRESOLVE::V4 ? CURL_IPRESOLVE_V4 : m_IpResolve == IPRESOLVE::V6 ? CURL_IPRESOLVE_V6 - : CURL_IPRESOLVE_WHATEVER); + : CURL_IPRESOLVE_WHATEVER); if (g_Config.m_Bindaddr[0] != '\0') { curl_easy_setopt(pHandle, CURLOPT_INTERFACE, g_Config.m_Bindaddr); From efd463ea87381bcf5c6d6f49c90de04c469ca44f Mon Sep 17 00:00:00 2001 From: CometOnOrbit Date: Sat, 25 Jan 2025 00:48:54 +0800 Subject: [PATCH 2/3] MySQL Connection Pool --- CMakeLists.txt | 5 + src/base/system.cpp | 186 ++++++++++++------ src/base/system.h | 34 +++- .../server/database/connection_pool.cpp | 145 ++++++++++++++ src/engine/server/database/connection_pool.h | 90 +++------ src/engine/server/database/sql_connection.cpp | 101 ++++++++++ src/engine/server/database/sql_connection.h | 46 +++++ .../server/database/sql_string_helpers.cpp | 137 +++++++++++++ .../server/database/sql_string_helpers.h | 49 +++++ 9 files changed, 672 insertions(+), 121 deletions(-) create mode 100644 src/engine/server/database/connection_pool.cpp create mode 100644 src/engine/server/database/sql_connection.cpp create mode 100644 src/engine/server/database/sql_connection.h create mode 100644 src/engine/server/database/sql_string_helpers.cpp create mode 100644 src/engine/server/database/sql_string_helpers.h diff --git a/CMakeLists.txt b/CMakeLists.txt index bfa6ba5c..2ed20659 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -512,7 +512,12 @@ list(APPEND TARGETS_OWN engine-shared game-shared) # Sources set_glob(ENGINE_SERVER GLOB src/engine/server + database/connection_pool.cpp database/connection_pool.h + database/sql_connection.cpp + database/sql_connection.h + database/sql_string_helpers.cpp + database/sql_string_helpers.h register.cpp register.h server.cpp diff --git a/src/base/system.cpp b/src/base/system.cpp index 76af59bf..3a999ca8 100644 --- a/src/base/system.cpp +++ b/src/base/system.cpp @@ -70,12 +70,15 @@ extern "C" static DBG_LOGGER loggers[16]; static int num_loggers = 0; + static int id_logger_file = 0; static NETSTATS network_stats = {0}; static MEMSTATS memory_stats = {0}; - void dbg_logger(DBG_LOGGER logger) + void dbg_logger(DBG_LOGGER logger, bool file) { + if (file) + id_logger_file = num_loggers; loggers[num_loggers++] = logger; } @@ -131,7 +134,27 @@ extern "C" va_end(args); for (i = 0; i < num_loggers; i++) - loggers[i](str); + if (!id_logger_file) + loggers[i](str); + else if (id_logger_file != i) + loggers[i](str); + + if (id_logger_file) + { + str_format(str, sizeof(str), "%d-%d-%d %d:%d:%d | [%s]: ", 1900 + pTime->tm_year, pTime->tm_mon, pTime->tm_mday, pTime->tm_hour, pTime->tm_min, pTime->tm_sec, sys); + len = strlen(str); + msg = (char *)str + len; + + va_start(args, fmt); +#if defined(CONF_FAMILY_WINDOWS) + _vsnprintf(msg, sizeof(str) - len, fmt, args); +#else + vsnprintf(msg, sizeof(str) - len, fmt, args); +#endif + va_end(args); + + loggers[id_logger_file](str); + } } static IOHANDLE logfile = 0; @@ -148,7 +171,7 @@ extern "C" { logfile = io_open(filename, IOFLAG_WRITE); if (logfile) - dbg_logger(logger_file); + dbg_logger(logger_file, true); else dbg_msg("dbg/logger", "failed to open '%s' for logging", filename); } @@ -916,6 +939,8 @@ typedef CRITICAL_SECTION LOCKINTERNAL; sock->ipv6sock = -1; sock->type &= ~NETTYPE_IPV6; } + + free(sock); return 0; } @@ -997,78 +1022,97 @@ typedef CRITICAL_SECTION LOCKINTERNAL; { NETSOCKET sock = (NETSOCKET_INTERNAL *)malloc(sizeof(*sock)); *sock = invalid_socket; - NETADDR tmpbindaddr = bindaddr; - int broadcast = 1; - int socket = -1; if (bindaddr.type & NETTYPE_IPV4) { struct sockaddr_in addr; - - /* bind, we should check for error */ + NETADDR tmpbindaddr = bindaddr; tmpbindaddr.type = NETTYPE_IPV4; netaddr_to_sockaddr_in(&tmpbindaddr, &addr); - socket = priv_net_create_socket(AF_INET, SOCK_DGRAM, (struct sockaddr *)&addr, sizeof(addr), 0); + int socket = priv_net_create_socket(AF_INET, SOCK_DGRAM, (struct sockaddr *)&addr, sizeof(addr), 0); if (socket >= 0) { sock->type |= NETTYPE_IPV4; sock->ipv4sock = socket; /* set broadcast */ + int broadcast = 1; if (setsockopt(socket, SOL_SOCKET, SO_BROADCAST, (const char *)&broadcast, sizeof(broadcast)) != 0) - dbg_msg("socket", "Setting BROADCAST on ipv4 failed: %d", errno); + { + dbg_msg("socket", "Setting BROADCAST on ipv4 failed: %d", net_errno()); + } { /* set DSCP/TOS */ int iptos = 0x10 /* IPTOS_LOWDELAY */; - // int iptos = 46; /* High Priority */ if (setsockopt(socket, IPPROTO_IP, IP_TOS, (char *)&iptos, sizeof(iptos)) != 0) - dbg_msg("socket", "Setting TOS on ipv4 failed: %d", errno); + { + dbg_msg("socket", "Setting TOS on ipv4 failed: %d", net_errno()); + } } } } +#if defined(CONF_WEBSOCKETS) + if (bindaddr.type & NETTYPE_WEBSOCKET_IPV4) + { + char addr_str[NETADDR_MAXSTRSIZE]; + NETADDR tmpbindaddr = bindaddr; + tmpbindaddr.type = NETTYPE_WEBSOCKET_IPV4; + net_addr_str(&tmpbindaddr, addr_str, sizeof(addr_str), 0); + int socket = websocket_create(addr_str, tmpbindaddr.port); + if (socket >= 0) + { + sock->type |= NETTYPE_WEBSOCKET_IPV4; + sock->web_ipv4sock = socket; + } + } +#endif + if (bindaddr.type & NETTYPE_IPV6) { struct sockaddr_in6 addr; - - /* bind, we should check for error */ + NETADDR tmpbindaddr = bindaddr; tmpbindaddr.type = NETTYPE_IPV6; netaddr_to_sockaddr_in6(&tmpbindaddr, &addr); - socket = priv_net_create_socket(AF_INET6, SOCK_DGRAM, (struct sockaddr *)&addr, sizeof(addr), 0); + int socket = priv_net_create_socket(AF_INET6, SOCK_DGRAM, (struct sockaddr *)&addr, sizeof(addr), 0); if (socket >= 0) { sock->type |= NETTYPE_IPV6; sock->ipv6sock = socket; /* set broadcast */ + int broadcast = 1; if (setsockopt(socket, SOL_SOCKET, SO_BROADCAST, (const char *)&broadcast, sizeof(broadcast)) != 0) - dbg_msg("socket", "Setting BROADCAST on ipv6 failed: %d", errno); + { + dbg_msg("socket", "Setting BROADCAST on ipv6 failed: %d", net_errno()); + } + // TODO: setting IP_TOS on ipv6 with setsockopt is not supported on Windows, see https://github.com/ddnet/ddnet/issues/7605 +#if !defined(CONF_FAMILY_WINDOWS) { /* set DSCP/TOS */ int iptos = 0x10 /* IPTOS_LOWDELAY */; - // int iptos = 46; /* High Priority */ if (setsockopt(socket, IPPROTO_IP, IP_TOS, (char *)&iptos, sizeof(iptos)) != 0) - dbg_msg("socket", "Setting TOS on ipv6 failed: %d", errno); + { + dbg_msg("socket", "Setting TOS on ipv6 failed: %d", net_errno()); + } } +#endif } } - if (socket < 0) + if (sock->type == NETTYPE_INVALID) { free(sock); sock = nullptr; } else { - /* set non-blocking */ net_set_non_blocking(sock); - net_buffer_init(&sock->buffer); } - /* return */ return sock; } @@ -1234,20 +1278,6 @@ typedef CRITICAL_SECTION LOCKINTERNAL; } #endif -#if defined(CONF_WEBSOCKETS) - if (bytes <= 0 && sock->web_ipv4sock >= 0) - { - char *buf; - int size; - net_buffer_simple(&sock->buffer, &buf, &size); - socklen_t fromlen = sizeof(struct sockaddr); - struct sockaddr_in *sockaddrbuf_in = (struct sockaddr_in *)&sockaddrbuf; - bytes = websocket_recv(sock->web_ipv4sock, (unsigned char *)buf, size, sockaddrbuf_in, fromlen); - *data = (unsigned char *)buf; - sockaddrbuf_in->sin_family = AF_WEBSOCKET_INET; - } -#endif - if (bytes > 0) { sockaddr_to_netaddr((struct sockaddr *)&sockaddrbuf, addr); @@ -1269,41 +1299,41 @@ typedef CRITICAL_SECTION LOCKINTERNAL; { NETSOCKET sock = (NETSOCKET_INTERNAL *)malloc(sizeof(*sock)); *sock = invalid_socket; - NETADDR tmpbindaddr = bindaddr; if (bindaddr.type & NETTYPE_IPV4) { struct sockaddr_in addr; - int socket = -1; - - /* bind, we should check for error */ + NETADDR tmpbindaddr = bindaddr; tmpbindaddr.type = NETTYPE_IPV4; netaddr_to_sockaddr_in(&tmpbindaddr, &addr); - socket = priv_net_create_socket(AF_INET, SOCK_STREAM, (struct sockaddr *)&addr, sizeof(addr), 0); - if (socket >= 0) + int socket4 = priv_net_create_socket(AF_INET, SOCK_STREAM, (struct sockaddr *)&addr, sizeof(addr), 0); + if (socket4 >= 0) { sock->type |= NETTYPE_IPV4; - sock->ipv4sock = socket; + sock->ipv4sock = socket4; } } if (bindaddr.type & NETTYPE_IPV6) { struct sockaddr_in6 addr; - int socket = -1; - - /* bind, we should check for error */ + NETADDR tmpbindaddr = bindaddr; tmpbindaddr.type = NETTYPE_IPV6; netaddr_to_sockaddr_in6(&tmpbindaddr, &addr); - socket = priv_net_create_socket(AF_INET6, SOCK_STREAM, (struct sockaddr *)&addr, sizeof(addr), 0); - if (socket >= 0) + int socket6 = priv_net_create_socket(AF_INET6, SOCK_STREAM, (struct sockaddr *)&addr, sizeof(addr), 0); + if (socket6 >= 0) { sock->type |= NETTYPE_IPV6; - sock->ipv6sock = socket; + sock->ipv6sock = socket6; } } - /* return */ + if (sock->type == NETTYPE_INVALID) + { + free(sock); + sock = nullptr; + } + return sock; } @@ -1733,7 +1763,7 @@ typedef CRITICAL_SECTION LOCKINTERNAL; tv.tv_usec = time % 1000000; sockid = 0; - FD_ZERO(&readfds); // NOLINT(clang-analyzer-security.insecureAPI.bzero) + FD_ZERO(&readfds); if (sock->ipv4sock >= 0) { FD_SET(sock->ipv4sock, &readfds); @@ -1754,12 +1784,24 @@ typedef CRITICAL_SECTION LOCKINTERNAL; if (sock->ipv4sock >= 0 && FD_ISSET(sock->ipv4sock, &readfds)) return 1; + if (sock->ipv6sock >= 0 && FD_ISSET(sock->ipv6sock, &readfds)) return 1; return 0; } + std::chrono::nanoseconds time_get_nanoseconds() + { + return std::chrono::nanoseconds(time_get_impl()); + } + + int net_socket_read_wait_nanosecond(NETSOCKET sock, std::chrono::nanoseconds nanoseconds) + { + using namespace std::chrono_literals; + return ::net_socket_read_wait(sock, (nanoseconds / std::chrono::nanoseconds(1us).count()).count()); + } + int time_timestamp() { return time(0); @@ -2049,17 +2091,33 @@ typedef CRITICAL_SECTION LOCKINTERNAL; return 0; } - void str_timestamp(char *buffer, int buffer_size) +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wformat-nonliteral" +#endif + void str_timestamp_ex(time_t time_data, char *buffer, int buffer_size, const char *format) { - time_t time_data; struct tm *time_info; - - time(&time_data); time_info = localtime(&time_data); - strftime(buffer, buffer_size, "%Y-%m-%d_%H-%M-%S", time_info); + strftime(buffer, buffer_size, format, time_info); buffer[buffer_size - 1] = 0; /* assure null termination */ } + void str_timestamp_format(char *buffer, int buffer_size, const char *format) + { + time_t time_data; + time(&time_data); + str_timestamp_ex(time_data, buffer, buffer_size, format); + } + + void str_timestamp(char *buffer, int buffer_size) + { + str_timestamp_format(buffer, buffer_size, FORMAT_NOSPACE); + } +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif + int mem_comp(const void *a, const void *b, int size) { return memcmp(a, b, size); @@ -2174,7 +2232,7 @@ typedef CRITICAL_SECTION LOCKINTERNAL; dst[dst_size - 1] = 0; /* assure null termination */ } - static int str_utf8_isstart(char c) + int str_utf8_isstart(char c) { if ((c & 0xC0) == 0x80) /* 10xxxxxx */ return 0; @@ -2417,6 +2475,18 @@ typedef CRITICAL_SECTION LOCKINTERNAL; return tok + len; } + int str_count(const char *str, const char *count) + { + int num = 0; + str = str_find(str, count); + while (str++) + { + str = str_find(str, count); + num++; + } + return num; + } + int bytes_be_to_int(const unsigned char *bytes) { int Result; @@ -2524,4 +2594,4 @@ typedef CRITICAL_SECTION LOCKINTERNAL; #if defined(__cplusplus) } -#endif +#endif \ No newline at end of file diff --git a/src/base/system.h b/src/base/system.h index f83aa11b..ffb530d3 100644 --- a/src/base/system.h +++ b/src/base/system.h @@ -24,6 +24,12 @@ #define MAYBE_UNUSED #endif +#ifdef __GNUC__ +#define GNUC_ATTRIBUTE(x) __attribute__(x) +#else +#define GNUC_ATTRIBUTE(x) +#endif + #include #include "types.h" @@ -206,7 +212,6 @@ extern "C" IOSEEK_END = 2, }; - /* Function: io_open Opens a file. @@ -867,6 +872,8 @@ typedef long long int64; // TeeUniverses void str_append_num(char *dst, const char *src, int dst_size, int num); + int str_utf8_isstart(char c); + /* Function: str_copy Copies a string to another. @@ -1154,6 +1161,13 @@ typedef long long int64; - Guarantees that buffer string will contain zero-termination. */ void str_timestamp(char *buffer, int buffer_size); + void str_timestamp_format(char *buffer, int buffer_size, const char *format); + void str_timestamp_ex(time_t time, char *buffer, int buffer_size, const char *format) + GNUC_ATTRIBUTE((format(strftime, 4, 0))); + +#define FORMAT_TIME "%H:%M:%S" +#define FORMAT_SPACE "%Y-%m-%d %H:%M:%S" +#define FORMAT_NOSPACE "%Y-%m-%d_%H-%M-%S" /* Group: Filesystem */ @@ -1329,12 +1343,23 @@ typedef long long int64; int net_socket_read_wait(NETSOCKET sock, int time); + /** + * Fetches a sample from a high resolution timer and converts it in nanoseconds. + * + * @ingroup Time + * + * @return Current value of the timer in nanoseconds. + */ + std::chrono::nanoseconds time_get_nanoseconds(); + + int net_socket_read_wait_nanosecond(NETSOCKET sock, std::chrono::nanoseconds nanoseconds); + void mem_debug_dump(IOHANDLE file); void swap_endian(void *data, unsigned elem_size, unsigned num); typedef void (*DBG_LOGGER)(const char *line); - void dbg_logger(DBG_LOGGER logger); + void dbg_logger(DBG_LOGGER logger, bool file = false); void dbg_logger_stdout(); void dbg_logger_debugger(); @@ -1488,9 +1513,10 @@ typedef long long int64; Remarks: - The token is always null-terminated. */ - const char *str_next_token(const char *str, const char *delim, char *buffer, int buffer_size); + int str_count(const char *str, const char *count); + /* Function: str_in_list Checks if needle is in list delimited by delim @@ -1577,4 +1603,4 @@ typedef long long int64; } #endif -#endif +#endif \ No newline at end of file diff --git a/src/engine/server/database/connection_pool.cpp b/src/engine/server/database/connection_pool.cpp new file mode 100644 index 00000000..a182de4c --- /dev/null +++ b/src/engine/server/database/connection_pool.cpp @@ -0,0 +1,145 @@ +/* Copyright(C) 2025 - 2025 Comet */ +#ifdef CONF_SQL +#include +#include + +#include "connection_pool.h" + +CConnectionPool *CConnectionPool::GetConnPool() +{ + static CConnectionPool sPool; + return &sPool; +} + +CConnectionPool::~CConnectionPool() +{ + Destroy(); +} + +bool CConnectionPool::Create(std::string User, std::string Password, std::string DbName, std::string Hostname, unsigned short Port, int PoolSize, int Timeout) +{ + m_User = User; + m_Password = Password; + m_DbName = DbName; + m_Hostname = Hostname; + m_Port = Port; + m_nPoolSize = PoolSize; + m_nTimeOut = Timeout; + + { + std::lock_guard locker(m_mtx); + + for (int i = 0; i < m_nPoolSize; i++) + AddIdelQueue(); + } + + return size_t(m_nPoolSize) == m_qIdle.size(); +} + +CSqlConnection *CConnectionPool::GetOneConn() +{ + CSqlConnection *pConn = nullptr; + + { + std::lock_guard locker(m_mtx); + + if (!m_qIdle.empty()) + { + pConn = m_qIdle.front(); + m_qIdle.pop_front(); + + if (pConn && pConn->m_pConnection->isValid()) + m_qBusy.push_back(pConn); + else + { + // Remove useless. + delete pConn; + pConn = nullptr; + + // Create new connection + pConn = new CSqlConnection; + if (pConn && pConn->Connect(m_pDriver, m_User, m_Password, m_DbName, m_Hostname, m_Port)) + m_qBusy.push_back(pConn); + else + { + // Failed, create new one + delete pConn; + pConn = nullptr; + } + } + } + else + { + // if no free, new one + pConn = new CSqlConnection; + if (pConn && pConn->Connect(m_pDriver, m_User, m_Password, m_DbName, m_Hostname, m_Port)) + m_qBusy.push_back(pConn); + else + { + delete pConn; + pConn = nullptr; + } + } + } + + return pConn; +} + +void CConnectionPool::ReleaseOneConn(CSqlConnection *pConn) +{ + if (!pConn) + return; + + std::lock_guard locker(m_mtx); + + auto it = std::find(m_qBusy.begin(), m_qBusy.end(), pConn); + if (it != m_qBusy.end()) + { + m_qBusy.erase(it); + m_qIdle.push_back(pConn); + } + + while (m_qIdle.size() > size_t(m_nPoolSize)) + { + CSqlConnection *pConn = m_qIdle.front(); + m_qIdle.pop_front(); + delete pConn; + } +} + +CConnectionPool::CConnectionPool() +{ + m_pDriver = get_driver_instance(); +} + +void CConnectionPool::Destroy() +{ + std::lock_guard locker(m_mtx); + + // idle to die + while (!m_qIdle.empty()) + { + CSqlConnection *pConn = m_qIdle.front(); + m_qIdle.pop_front(); + delete pConn; + } + + // busy to die + while (!m_qBusy.empty()) + { + CSqlConnection *pConn = m_qBusy.front(); + m_qBusy.pop_front(); + delete pConn; + } +} + +void CConnectionPool::AddIdelQueue() +{ + CSqlConnection *pConn = new CSqlConnection; + if (pConn && pConn->Connect(m_pDriver, m_User, m_Password, m_DbName, m_Hostname, m_Port)) + m_qIdle.push_back(pConn); + else + delete pConn; +} + +#endif \ No newline at end of file diff --git a/src/engine/server/database/connection_pool.h b/src/engine/server/database/connection_pool.h index 6a6b1da9..1fbf2b15 100644 --- a/src/engine/server/database/connection_pool.h +++ b/src/engine/server/database/connection_pool.h @@ -1,72 +1,44 @@ -#if defined(CONF_SQL) -// I don't think this is smart -// So please rewrite it -// I will not rewrite it because I don't know how to do it(im poor u know) -// Someone else please help me. - +/* Copyright(C) 2025 - 2025 Comet */ +#ifdef CONF_SQL #ifndef ENGINE_SERVER_DATABASE_CONNECTION_POOL_H #define ENGINE_SERVER_DATABASE_CONNECTION_POOL_H - -#include -#include -#include -#include -#include -#include -#include -#include +#include "sql_connection.h" #include -#include -#include +#include -class CMySQLConnectionPool +class CConnectionPool { public: - CMySQLConnectionPool(const std::string &Server, - const std::string &Username, - const std::string &Password, - const std::string &Database, - int maxConn = 10) - { - for (int i = 0; i < maxConn; ++i) - m_Connections.push(CreateConnection(Server, Username, Password, Database)); - } + static CConnectionPool *GetConnPool(); + CConnectionPool(const CConnectionPool &obj) = delete; + CConnectionPool &operator=(const CConnectionPool &obj) = delete; + ~CConnectionPool(); - std::shared_ptr GetConnection() - { - std::unique_lock lock(m_SqlMutex); - while (m_Connections.empty()) - { - m_SqlCondition.wait(lock); - } - std::shared_ptr pConnect = m_Connections.front(); - m_Connections.pop(); - return pConnect; - } - - void ReleaseConnection(std::shared_ptr pConnection) - { - std::lock_guard lock(m_SqlMutex); - m_Connections.push(pConnection); - m_SqlCondition.notify_one(); - } + bool Create(std::string User, std::string Password, std::string DbName, std::string Hostname, unsigned short port, int PoolSize, int Timeout); + CSqlConnection *GetOneConn(); + void ReleaseOneConn(CSqlConnection *pConn); + void Destroy(); private: - std::queue> m_Connections; - std::mutex m_SqlMutex; - std::condition_variable m_SqlCondition; + CConnectionPool(); + void AddIdelQueue(); + + sql::Driver *m_pDriver; + + std::string m_User; + std::string m_Password; + std::string m_DbName; + std::string m_Hostname; + + unsigned short m_Port; + int m_nPoolSize = 0; + int m_nTimeOut = 0; + + std::mutex m_mtx; - std::shared_ptr CreateConnection(const std::string &Server, - const std::string &Username, - const std::string &Password, - const std::string &Database) - { - sql::Driver *pDriver = get_driver_instance(); - std::shared_ptr pConnect(pDriver->connect(Server, Username, Password)); - pConnect->setSchema(Database); - return pConnect; - } + std::deque m_qIdle; + std::deque m_qBusy; }; -#endif // ! +#endif #endif \ No newline at end of file diff --git a/src/engine/server/database/sql_connection.cpp b/src/engine/server/database/sql_connection.cpp new file mode 100644 index 00000000..44ef86f7 --- /dev/null +++ b/src/engine/server/database/sql_connection.cpp @@ -0,0 +1,101 @@ +/* Copyright(C) 2025 - 2025 Comet */ +#ifdef CONF_SQL +#include +#include "sql_connection.h" + +CSqlConnection::CSqlConnection() +{ + m_pConnection = nullptr; + m_pStatement = nullptr; + m_pResult = nullptr; +} + +CSqlConnection::~CSqlConnection() +{ + Release(); +} + +bool CSqlConnection::Connect(sql::Driver *pDriver, std::string User, std::string Pw, std::string Database, std::string Hostname, unsigned short Port) +{ + try + { + char aBuf[64]; + str_format(aBuf, sizeof(aBuf), "tcp://%s:%d", Hostname.c_str(), Port); + m_pConnection = pDriver->connect(aBuf, User.c_str(), Pw.c_str()); + m_pStatement = m_pConnection->createStatement(); + m_pConnection->setSchema(Database.c_str()); + } + catch (sql::SQLException &e) + { + dbg_msg("SQLError", "Error (%d) : %s", e.getErrorCode(), e.what()); + return false; + } + return true; +} + +bool CSqlConnection::Options(std::string Option, std::string Value) +{ + if (!m_pConnection) + { + dbg_msg("MySQL", "Error: m_pConnection is nullptr"); + return false; + } + m_pConnection->setClientOption(Option, Value); + return true; +} + +bool CSqlConnection::Query(std::string Sql) +{ + if (!m_pStatement) + return false; + + // 在执行新查询前释放旧的结果集 + FreeResult(); + + m_pResult = m_pStatement->executeQuery(Sql); + return true; +} + +bool CSqlConnection::Next() +{ + if (!m_pResult) + return false; + + return m_pResult->next(); +} + +bool CSqlConnection::Execute(std::string Sql) +{ + return m_pStatement->execute(Sql); +} + +void CSqlConnection::Release() +{ + // Free results + FreeResult(); + + // Close connect and delete pointer + if (m_pConnection) + { + m_pConnection->close(); + delete m_pConnection; + m_pConnection = nullptr; + } + + // Clean + if (m_pStatement) + { + delete m_pStatement; + m_pStatement = nullptr; + } +} + +void CSqlConnection::FreeResult() +{ + if (m_pResult) + { + delete m_pResult; + m_pResult = nullptr; + } +} +#endif \ No newline at end of file diff --git a/src/engine/server/database/sql_connection.h b/src/engine/server/database/sql_connection.h new file mode 100644 index 00000000..62cf4a09 --- /dev/null +++ b/src/engine/server/database/sql_connection.h @@ -0,0 +1,46 @@ +/* Copyright(C) 2025 - 2025 Comet */ +#ifdef CONF_SQL +#ifndef GAME_SERVER_GAMECORE_DATABASE_SQL_CONNECTION_H +#define GAME_SERVER_GAMECORE_DATABASE_SQL_CONNECTION_H + +#include + +#include +#include +#include +#include + +class CSqlConnection +{ +public: + sql::ResultSet *m_pResult; + sql::Connection *m_pConnection; + sql::Statement *m_pStatement; + +public: + CSqlConnection(); + ~CSqlConnection(); + + // Connect + bool Connect(sql::Driver *pDriver, std::string User, std::string Password, std::string DbName, std::string Hostname, unsigned short Port); + + // Query + bool Query(std::string Sql); + + // Next + bool Next(); + + // Execute + bool Execute(std::string Sql); + +private: + void Release(); + void FreeResult(); + bool Options(std::string Option, std::string Value); + + // anti SQL injection + void ClearString(char *pString, int size); +}; + +#endif +#endif \ No newline at end of file diff --git a/src/engine/server/database/sql_string_helpers.cpp b/src/engine/server/database/sql_string_helpers.cpp new file mode 100644 index 00000000..bf00a1bf --- /dev/null +++ b/src/engine/server/database/sql_string_helpers.cpp @@ -0,0 +1,137 @@ +#ifdef CONF_SQL +#include +#include +#include +#include + +#include "sql_string_helpers.h" + +void sqlstr::FuzzyString(char *pString) +{ + char newString[32 * 4 - 1]; + int pos = 0; + + for (int i = 0; i < 64; i++) + { + if (!pString[i]) + break; + + newString[pos++] = pString[i]; + if (pString[i] != '\\' && str_utf8_isstart(pString[i + 1])) + newString[pos++] = '%'; + } + + newString[pos] = '\0'; + strcpy(pString, newString); +} + +// anti SQL injection +void sqlstr::ClearString(char *pString, int size) +{ + char *newString = new char[size * 2 - 1]; + int pos = 0; + + for (int i = 0; i < size; i++) + { + if (pString[i] == '\\') + { + newString[pos++] = '\\'; + newString[pos++] = '\\'; + } + else if (pString[i] == '\'') + { + newString[pos++] = '\\'; + newString[pos++] = '\''; + } + else if (pString[i] == '"') + { + newString[pos++] = '\\'; + newString[pos++] = '"'; + } + else + { + newString[pos++] = pString[i]; + } + } + + newString[pos] = '\0'; + + strcpy(pString, newString); + delete[] newString; +} + +void sqlstr::agoTimeToString(int agoTime, char agoString[]) +{ + char aBuf[20]; + int times[7] = + { + 60 * 60 * 24 * 365, + 60 * 60 * 24 * 30, + 60 * 60 * 24 * 7, + 60 * 60 * 24, + 60 * 60, + 60, + 1}; + char names[7][6] = + { + "year", + "month", + "week", + "day", + "hour", + "min", + "sec"}; + + int seconds = 0; + char name[6]; + int count = 0; + int i = 0; + + // finding biggest match + for (i = 0; i < 7; i++) + { + seconds = times[i]; + strcpy(name, names[i]); + + count = floor((float)agoTime / (float)seconds); + if (count != 0) + break; + } + + if (count == 1) + str_format(aBuf, sizeof(aBuf), "%d %s", 1, name); + else + str_format(aBuf, sizeof(aBuf), "%d %ss", count, name); + + strcat(agoString, aBuf); + + if (i + 1 < 7) + { + // getting second piece now + int seconds2 = times[i + 1]; + char name2[6]; + strcpy(name2, names[i + 1]); + + // add second piece if it's greater than 0 + int count2 = floor((float)(agoTime - (seconds * count)) / (float)seconds2); + + if (count2 != 0) + { + if (count2 == 1) + str_format(aBuf, sizeof(aBuf), " and %d %s", 1, name2); + else + str_format(aBuf, sizeof(aBuf), " and %d %ss", count2, name2); + strcat(agoString, aBuf); + } + } +} + +void sqlstr::getTimeStamp(char *dest, unsigned int size) +{ + std::time_t rawtime; + std::time(&rawtime); + + str_timestamp_ex(rawtime, dest, size, "%Y-%m-%d %H:%M:%S"); +} + +#endif \ No newline at end of file diff --git a/src/engine/server/database/sql_string_helpers.h b/src/engine/server/database/sql_string_helpers.h new file mode 100644 index 00000000..43152af9 --- /dev/null +++ b/src/engine/server/database/sql_string_helpers.h @@ -0,0 +1,49 @@ +#ifdef CONF_SQL +#ifndef ENGINE_SERVER_SQL_STRING_HELPERS_H +#define ENGINE_SERVER_SQL_STRING_HELPERS_H + +namespace sqlstr +{ + + void FuzzyString(char *pString); + + // anti SQL injection + void ClearString(char *pString, int size = 32); + + void agoTimeToString(int agoTime, char agoString[]); + + void getTimeStamp(char *dest, unsigned int size); + +} + +template +class CSqlString +{ +public: + CSqlString() {} + + CSqlString(const char *pStr) + { + str_copy(m_aString, pStr, size); + str_copy(m_aClearString, pStr, size); + sqlstr::ClearString(m_aClearString, sizeof(m_aClearString)); + } + + const char *Str() const { return m_aString; } + const char *ClrStr() const { return m_aClearString; } + + CSqlString &operator=(const char *pStr) + { + str_copy(m_aString, pStr, size); + str_copy(m_aClearString, pStr, size); + sqlstr::ClearString(m_aClearString, sizeof(m_aClearString)); + return *this; + } + +private: + char m_aString[size]; + char m_aClearString[size * 2 - 1]; +}; + +#endif +#endif \ No newline at end of file From bd50b3a5125f15dc4f0e6429aa466f85fc2439b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=9C=A8=E8=BD=A8=E5=BD=97=E6=98=9F?= Date: Sun, 26 Jan 2025 17:06:44 +0800 Subject: [PATCH 3/3] Create build.yml --- .github/workflows/build.yml | 50 +++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 .github/workflows/build.yml diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 00000000..9233f937 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,50 @@ +name: Build + +on: [push, pull_request] + +jobs: + build-bam: + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest] + include: + - name: "Ubuntu-Latest x86_64" + os: ubuntu-latest + package-full-file: tdf-linux_x86_64-release.tar.xz + + steps: + - uses: actions/checkout@v2 + with: + submodules: true + + - name: Checkout bam + uses: actions/checkout@v2 + with: + repository: matricks/bam + ref: 8cd08744c37666830d439ab54016c9d228c63b68 + path: ./bam + + - name: Prepare Linux + if: contains(matrix.os, 'ubuntu') + run: | + sudo apt-get update -y + sudo apt install libmaxminddb-dev libmariadb-dev-compat libmariadb-dev libssl-dev libmysqlcppconn-dev libicu-dev build-essential cmake python3 -y + + - name: Build in debug mode + run: bam debug + + - name: Build in release mode + run: bam release + + - name: Package Release + if: contains(matrix.os, 'ubuntu') + run: | + tar -cf ${{ matrix.package-full-file }} Alchemist-Server data maps server_items server_lang autoexec.cfg.example storage.cfg README.md TeeDef.sql + + - name: Upload Artifacts + uses: actions/upload-artifact@v2-preview + with: + name: ${{ matrix.name }} + path: ${{ matrix.package-full-file }}