Compare commits
4 Commits
test/disco
...
8bb5340909
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8bb5340909 | ||
|
|
6061e43c20 | ||
|
|
54a4134bf7 | ||
|
|
bf72bd2669 |
84
.github/workflows/main.yml
vendored
84
.github/workflows/main.yml
vendored
@@ -4,32 +4,68 @@ on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
bsd:
|
||||
linux:
|
||||
runs-on: ubuntu-latest
|
||||
name: Main build job
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- name: linux-release
|
||||
build_type: Release
|
||||
cmake_args: -G Ninja -DCMAKE_BUILD_TYPE=Release
|
||||
- name: linux-asan
|
||||
build_type: RelWithDebInfo
|
||||
cmake_args: -G Ninja -DCMAKE_BUILD_TYPE=RelWithDebInfo -DENABLE_ASAN=ON
|
||||
name: ${{ matrix.name }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: FreeBSD job
|
||||
id: test
|
||||
uses: vmactions/freebsd-vm@v1
|
||||
with:
|
||||
usesh: true
|
||||
sync: sshfs
|
||||
prepare: |
|
||||
pkg install -y git cmake gmake
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Install build dependencies
|
||||
run: |
|
||||
mkdir build
|
||||
cd build
|
||||
cmake ..
|
||||
gmake all -j6
|
||||
- name: Collect outputs
|
||||
run: |
|
||||
mkdir _output
|
||||
cp build/bin/* _output
|
||||
- name: Upload
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: output_bsd
|
||||
path: _output
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y ninja-build
|
||||
|
||||
- name: Configure
|
||||
run: cmake -S . -B build ${{ matrix.cmake_args }}
|
||||
|
||||
- name: Build
|
||||
run: cmake --build build --parallel
|
||||
|
||||
- name: Upload compile commands
|
||||
if: matrix.name == 'linux-release'
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: compile-commands
|
||||
path: build/compile_commands.json
|
||||
|
||||
- name: Upload binaries
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ${{ matrix.name }}-binaries
|
||||
path: build/bin
|
||||
|
||||
freebsd:
|
||||
runs-on: ubuntu-latest
|
||||
name: freebsd-release
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: FreeBSD build
|
||||
uses: vmactions/freebsd-vm@v1
|
||||
with:
|
||||
usesh: true
|
||||
sync: sshfs
|
||||
prepare: |
|
||||
pkg install -y git cmake gmake ninja
|
||||
run: |
|
||||
cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release
|
||||
cmake --build build --parallel
|
||||
|
||||
- name: Upload binaries
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: freebsd-release-binaries
|
||||
path: build/bin
|
||||
|
||||
@@ -4,6 +4,7 @@ project(m2dev-server-src)
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
set(CMAKE_C_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||
|
||||
# ASan support
|
||||
option(ENABLE_ASAN "Enable AddressSanitizer" OFF)
|
||||
|
||||
@@ -7,12 +7,242 @@
|
||||
#include "Config.h"
|
||||
#include "QID.h"
|
||||
#include "Cache.h"
|
||||
#include "libsql/Statement.h"
|
||||
|
||||
extern std::string g_stLocale;
|
||||
extern bool CreatePlayerTableFromRes(MYSQL_RES * res, TPlayerTable * pkTab);
|
||||
extern int g_test_server;
|
||||
extern int g_log;
|
||||
|
||||
namespace
|
||||
{
|
||||
bool PreparePlayerStmt(CStmt& stmt, const std::string& query)
|
||||
{
|
||||
CAsyncSQL* sql = CDBManager::instance().GetDirectSQL(SQL_PLAYER);
|
||||
|
||||
if (!sql)
|
||||
{
|
||||
sys_err("player SQL handle is not initialized");
|
||||
return false;
|
||||
}
|
||||
|
||||
return stmt.Prepare(sql, query.c_str());
|
||||
}
|
||||
|
||||
bool LoadPlayerIndexTable(uint32_t accountId, TAccountTable* accountTable)
|
||||
{
|
||||
CStmt stmt;
|
||||
const std::string query = std::string("SELECT pid1, pid2, pid3, pid4, empire FROM player_index") + GetTablePostfix() + " WHERE id=?";
|
||||
uint32_t playerIds[PLAYER_PER_ACCOUNT] = {};
|
||||
uint8_t empire = 0;
|
||||
|
||||
if (!PreparePlayerStmt(stmt, query))
|
||||
return false;
|
||||
|
||||
if (!stmt.BindParam(MYSQL_TYPE_LONG, &accountId))
|
||||
return false;
|
||||
|
||||
for (int i = 0; i < PLAYER_PER_ACCOUNT; ++i)
|
||||
{
|
||||
if (!stmt.BindResult(MYSQL_TYPE_LONG, &playerIds[i]))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!stmt.BindResult(MYSQL_TYPE_TINY, &empire))
|
||||
return false;
|
||||
|
||||
if (!stmt.Execute() || !stmt.Fetch())
|
||||
return false;
|
||||
|
||||
for (int i = 0; i < PLAYER_PER_ACCOUNT; ++i)
|
||||
accountTable->players[i].dwID = playerIds[i];
|
||||
|
||||
accountTable->bEmpire = empire;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CreatePlayerIndexRow(uint32_t accountId)
|
||||
{
|
||||
CStmt stmt;
|
||||
const std::string query = std::string("INSERT INTO player_index") + GetTablePostfix() + " (id) VALUES(?)";
|
||||
|
||||
if (!PreparePlayerStmt(stmt, query))
|
||||
return false;
|
||||
|
||||
if (!stmt.BindParam(MYSQL_TYPE_LONG, &accountId))
|
||||
return false;
|
||||
|
||||
return stmt.Execute();
|
||||
}
|
||||
|
||||
void ApplyPlayerSummary(TAccountTable* accountTable, uint32_t playerId, const char* name, uint32_t job, uint32_t level, uint32_t progressValue,
|
||||
int32_t st, int32_t ht, int32_t dx, int32_t iq, uint32_t mainPart, uint32_t hairPart, int32_t x, int32_t y, uint32_t skillGroup, uint32_t changeName)
|
||||
{
|
||||
for (int i = 0; i < PLAYER_PER_ACCOUNT; ++i)
|
||||
{
|
||||
if (accountTable->players[i].dwID != playerId)
|
||||
continue;
|
||||
|
||||
CPlayerTableCache* cache = CClientManager::instance().GetPlayerCache(playerId);
|
||||
TPlayerTable* playerTable = cache ? cache->Get(false) : NULL;
|
||||
|
||||
if (playerTable)
|
||||
{
|
||||
strlcpy(accountTable->players[i].szName, playerTable->name, sizeof(accountTable->players[i].szName));
|
||||
accountTable->players[i].byJob = playerTable->job;
|
||||
accountTable->players[i].byLevel = playerTable->level;
|
||||
accountTable->players[i].dwPlayMinutes = playerTable->playtime;
|
||||
accountTable->players[i].byST = playerTable->st;
|
||||
accountTable->players[i].byHT = playerTable->ht;
|
||||
accountTable->players[i].byDX = playerTable->dx;
|
||||
accountTable->players[i].byIQ = playerTable->iq;
|
||||
accountTable->players[i].wMainPart = playerTable->parts[PART_MAIN];
|
||||
accountTable->players[i].wHairPart = playerTable->parts[PART_HAIR];
|
||||
accountTable->players[i].x = playerTable->x;
|
||||
accountTable->players[i].y = playerTable->y;
|
||||
accountTable->players[i].skill_group = playerTable->skill_group;
|
||||
accountTable->players[i].bChangeName = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
strlcpy(accountTable->players[i].szName, name, sizeof(accountTable->players[i].szName));
|
||||
accountTable->players[i].byJob = static_cast<uint8_t>(job);
|
||||
accountTable->players[i].byLevel = static_cast<uint8_t>(level);
|
||||
accountTable->players[i].dwPlayMinutes = progressValue;
|
||||
accountTable->players[i].byST = static_cast<uint8_t>(st);
|
||||
accountTable->players[i].byHT = static_cast<uint8_t>(ht);
|
||||
accountTable->players[i].byDX = static_cast<uint8_t>(dx);
|
||||
accountTable->players[i].byIQ = static_cast<uint8_t>(iq);
|
||||
accountTable->players[i].wMainPart = static_cast<uint16_t>(mainPart);
|
||||
accountTable->players[i].wHairPart = static_cast<uint16_t>(hairPart);
|
||||
accountTable->players[i].x = x;
|
||||
accountTable->players[i].y = y;
|
||||
accountTable->players[i].skill_group = static_cast<uint8_t>(skillGroup);
|
||||
accountTable->players[i].bChangeName = static_cast<uint8_t>(changeName);
|
||||
}
|
||||
|
||||
sys_log(0, "%s %lu %lu hair %u",
|
||||
accountTable->players[i].szName,
|
||||
accountTable->players[i].x,
|
||||
accountTable->players[i].y,
|
||||
accountTable->players[i].wHairPart);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
bool LoadAccountPlayerSummaries(uint32_t accountId, TAccountTable* accountTable)
|
||||
{
|
||||
CStmt stmt;
|
||||
std::string query;
|
||||
uint32_t playerId = 0;
|
||||
char name[CHARACTER_NAME_MAX_LEN + 1] = {};
|
||||
uint32_t job = 0;
|
||||
uint32_t level = 0;
|
||||
uint32_t progressValue = 0;
|
||||
int32_t st = 0;
|
||||
int32_t ht = 0;
|
||||
int32_t dx = 0;
|
||||
int32_t iq = 0;
|
||||
uint32_t mainPart = 0;
|
||||
uint32_t hairPart = 0;
|
||||
int32_t x = 0;
|
||||
int32_t y = 0;
|
||||
uint32_t skillGroup = 0;
|
||||
uint32_t changeName = 0;
|
||||
|
||||
if (g_stLocale == "gb2312")
|
||||
{
|
||||
query = std::string("SELECT id, name, job, level, alignment, st, ht, dx, iq, part_main, part_hair, x, y, skill_group, change_name FROM player")
|
||||
+ GetTablePostfix() + " WHERE account_id=?";
|
||||
}
|
||||
else
|
||||
{
|
||||
query = std::string("SELECT id, name, job, level, playtime, st, ht, dx, iq, part_main, part_hair, x, y, skill_group, change_name FROM player")
|
||||
+ GetTablePostfix() + " WHERE account_id=?";
|
||||
}
|
||||
|
||||
if (!PreparePlayerStmt(stmt, query))
|
||||
return false;
|
||||
|
||||
if (!stmt.BindParam(MYSQL_TYPE_LONG, &accountId))
|
||||
return false;
|
||||
|
||||
if (!stmt.BindResult(MYSQL_TYPE_LONG, &playerId)
|
||||
|| !stmt.BindResult(MYSQL_TYPE_STRING, name, sizeof(name))
|
||||
|| !stmt.BindResult(MYSQL_TYPE_LONG, &job)
|
||||
|| !stmt.BindResult(MYSQL_TYPE_LONG, &level)
|
||||
|| !stmt.BindResult(MYSQL_TYPE_LONG, &progressValue)
|
||||
|| !stmt.BindResult(MYSQL_TYPE_LONG, &st)
|
||||
|| !stmt.BindResult(MYSQL_TYPE_LONG, &ht)
|
||||
|| !stmt.BindResult(MYSQL_TYPE_LONG, &dx)
|
||||
|| !stmt.BindResult(MYSQL_TYPE_LONG, &iq)
|
||||
|| !stmt.BindResult(MYSQL_TYPE_LONG, &mainPart)
|
||||
|| !stmt.BindResult(MYSQL_TYPE_LONG, &hairPart)
|
||||
|| !stmt.BindResult(MYSQL_TYPE_LONG, &x)
|
||||
|| !stmt.BindResult(MYSQL_TYPE_LONG, &y)
|
||||
|| !stmt.BindResult(MYSQL_TYPE_LONG, &skillGroup)
|
||||
|| !stmt.BindResult(MYSQL_TYPE_LONG, &changeName))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!stmt.Execute())
|
||||
return false;
|
||||
|
||||
while (stmt.Fetch())
|
||||
{
|
||||
size_t nameLen = stmt.GetResultLength(1);
|
||||
if (nameLen >= sizeof(name))
|
||||
nameLen = sizeof(name) - 1;
|
||||
name[nameLen] = '\0';
|
||||
|
||||
ApplyPlayerSummary(accountTable, playerId, name, job, level, progressValue, st, ht, dx, iq, mainPart, hairPart, x, y, skillGroup, changeName);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CountPlayerNames(const char* playerName, uint32_t playerId, uint32_t* count)
|
||||
{
|
||||
CStmt stmt;
|
||||
std::string query;
|
||||
|
||||
if (g_stLocale == "sjis")
|
||||
query = std::string("SELECT COUNT(*) FROM player") + GetTablePostfix() + " WHERE name=? collate sjis_japanese_ci AND id <> ?";
|
||||
else
|
||||
query = std::string("SELECT COUNT(*) FROM player") + GetTablePostfix() + " WHERE name=? AND id <> ?";
|
||||
|
||||
if (!PreparePlayerStmt(stmt, query))
|
||||
return false;
|
||||
|
||||
if (!stmt.BindParam(MYSQL_TYPE_STRING, const_cast<char*>(playerName), CHARACTER_NAME_MAX_LEN + 1)
|
||||
|| !stmt.BindParam(MYSQL_TYPE_LONG, &playerId)
|
||||
|| !stmt.BindResult(MYSQL_TYPE_LONG, count))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return stmt.Execute() && stmt.Fetch();
|
||||
}
|
||||
|
||||
bool UpdatePlayerName(uint32_t playerId, const char* playerName)
|
||||
{
|
||||
CStmt stmt;
|
||||
const std::string query = std::string("UPDATE player") + GetTablePostfix() + " SET name=?, change_name=0 WHERE id=?";
|
||||
|
||||
if (!PreparePlayerStmt(stmt, query))
|
||||
return false;
|
||||
|
||||
if (!stmt.BindParam(MYSQL_TYPE_STRING, const_cast<char*>(playerName), CHARACTER_NAME_MAX_LEN + 1)
|
||||
|| !stmt.BindParam(MYSQL_TYPE_LONG, &playerId))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return stmt.Execute();
|
||||
}
|
||||
}
|
||||
|
||||
bool CClientManager::InsertLogonAccount(const char * c_pszLogin, DWORD dwHandle, const char * c_pszIP)
|
||||
{
|
||||
char szLogin[LOGIN_MAX_LEN + 1];
|
||||
@@ -110,23 +340,51 @@ void CClientManager::QUERY_LOGIN_BY_KEY(CPeer * pkPeer, DWORD dwHandle, TPacketG
|
||||
return;
|
||||
}
|
||||
|
||||
TAccountTable * pkTab = new TAccountTable;
|
||||
memset(pkTab, 0, sizeof(TAccountTable));
|
||||
TAccountTable accountTable = {};
|
||||
accountTable.id = r.id;
|
||||
trim_and_lower(r.login, accountTable.login, sizeof(accountTable.login));
|
||||
strlcpy(accountTable.passwd, r.passwd, sizeof(accountTable.passwd));
|
||||
strlcpy(accountTable.social_id, r.social_id, sizeof(accountTable.social_id));
|
||||
strlcpy(accountTable.status, "OK", sizeof(accountTable.status));
|
||||
|
||||
pkTab->id = r.id;
|
||||
trim_and_lower(r.login, pkTab->login, sizeof(pkTab->login));
|
||||
strlcpy(pkTab->passwd, r.passwd, sizeof(pkTab->passwd));
|
||||
strlcpy(pkTab->social_id, r.social_id, sizeof(pkTab->social_id));
|
||||
strlcpy(pkTab->status, "OK", sizeof(pkTab->status));
|
||||
sys_log(0, "LOGIN_BY_KEY success %s %lu %s", r.login, p->dwLoginKey, p->szIP);
|
||||
|
||||
ClientHandleInfo * info = new ClientHandleInfo(dwHandle);
|
||||
info->pAccountTable = pkTab;
|
||||
strlcpy(info->ip, p->szIP, sizeof(info->ip));
|
||||
if (!LoadPlayerIndexTable(accountTable.id, &accountTable))
|
||||
{
|
||||
sys_log(0, "LOGIN_BY_KEY missing player_index for account %u", accountTable.id);
|
||||
|
||||
sys_log(0, "LOGIN_BY_KEY success %s %lu %s", r.login, p->dwLoginKey, info->ip);
|
||||
char szQuery[QUERY_MAX_LEN];
|
||||
snprintf(szQuery, sizeof(szQuery), "SELECT pid1, pid2, pid3, pid4, empire FROM player_index%s WHERE id=%u", GetTablePostfix(), r.id);
|
||||
CDBManager::instance().ReturnQuery(szQuery, QID_LOGIN_BY_KEY, pkPeer->GetHandle(), info);
|
||||
CreatePlayerIndexRow(accountTable.id);
|
||||
|
||||
if (!LoadPlayerIndexTable(accountTable.id, &accountTable))
|
||||
{
|
||||
pkPeer->EncodeReturn(DG::LOGIN_NOT_EXIST, dwHandle);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!LoadAccountPlayerSummaries(accountTable.id, &accountTable))
|
||||
{
|
||||
pkPeer->EncodeReturn(DG::LOGIN_NOT_EXIST, dwHandle);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!InsertLogonAccount(accountTable.login, pkPeer->GetHandle(), p->szIP))
|
||||
{
|
||||
sys_log(0, "RESULT_LOGIN: already logon %s", accountTable.login);
|
||||
|
||||
TPacketDGLoginAlready packet;
|
||||
strlcpy(packet.szLogin, accountTable.login, sizeof(packet.szLogin));
|
||||
|
||||
pkPeer->EncodeHeader(DG::LOGIN_ALREADY, dwHandle, sizeof(TPacketDGLoginAlready));
|
||||
pkPeer->Encode(&packet, sizeof(packet));
|
||||
return;
|
||||
}
|
||||
|
||||
if (CLoginData* loginData = GetLoginDataByLogin(accountTable.login))
|
||||
memcpy(&loginData->GetAccountRef(), &accountTable, sizeof(TAccountTable));
|
||||
|
||||
pkPeer->EncodeHeader(DG::LOGIN_SUCCESS, dwHandle, sizeof(TAccountTable));
|
||||
pkPeer->Encode(&accountTable, sizeof(TAccountTable));
|
||||
}
|
||||
|
||||
void CClientManager::RESULT_LOGIN_BY_KEY(CPeer * peer, SQLMsg * msg)
|
||||
@@ -464,44 +722,25 @@ void CClientManager::QUERY_LOGOUT(CPeer * peer, DWORD dwHandle,const char * data
|
||||
|
||||
void CClientManager::QUERY_CHANGE_NAME(CPeer * peer, DWORD dwHandle, TPacketGDChangeName * p)
|
||||
{
|
||||
char queryStr[QUERY_MAX_LEN];
|
||||
uint32_t duplicateCount = 0;
|
||||
|
||||
if (g_stLocale == "sjis")
|
||||
snprintf(queryStr, sizeof(queryStr),
|
||||
"SELECT COUNT(*) as count FROM player%s WHERE name='%s' collate sjis_japanese_ci AND id <> %u",
|
||||
GetTablePostfix(), p->name, p->pid);
|
||||
else
|
||||
snprintf(queryStr, sizeof(queryStr),
|
||||
"SELECT COUNT(*) as count FROM player%s WHERE name='%s' AND id <> %u", GetTablePostfix(), p->name, p->pid);
|
||||
|
||||
auto pMsg = CDBManager::instance().DirectQuery(queryStr, SQL_PLAYER);
|
||||
|
||||
if (pMsg->Get()->uiNumRows)
|
||||
{
|
||||
if (!pMsg->Get()->pSQLResult)
|
||||
{
|
||||
peer->EncodeHeader(DG::PLAYER_CREATE_FAILED, dwHandle, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
MYSQL_ROW row = mysql_fetch_row(pMsg->Get()->pSQLResult);
|
||||
|
||||
if (*row[0] != '0')
|
||||
{
|
||||
peer->EncodeHeader(DG::PLAYER_CREATE_ALREADY, dwHandle, 0);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
if (!CountPlayerNames(p->name, p->pid, &duplicateCount))
|
||||
{
|
||||
peer->EncodeHeader(DG::PLAYER_CREATE_FAILED, dwHandle, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
snprintf(queryStr, sizeof(queryStr),
|
||||
"UPDATE player%s SET name='%s',change_name=0 WHERE id=%u", GetTablePostfix(), p->name, p->pid);
|
||||
if (duplicateCount != 0)
|
||||
{
|
||||
peer->EncodeHeader(DG::PLAYER_CREATE_ALREADY, dwHandle, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
auto pMsg0 = CDBManager::instance().DirectQuery(queryStr, SQL_PLAYER);
|
||||
if (!UpdatePlayerName(p->pid, p->name))
|
||||
{
|
||||
peer->EncodeHeader(DG::PLAYER_CREATE_FAILED, dwHandle, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
TPacketDGChangeName pdg;
|
||||
peer->EncodeHeader(DG::CHANGE_NAME, dwHandle, sizeof(TPacketDGChangeName));
|
||||
@@ -509,4 +748,3 @@ void CClientManager::QUERY_CHANGE_NAME(CPeer * peer, DWORD dwHandle, TPacketGDCh
|
||||
strlcpy(pdg.name, p->name, sizeof(pdg.name));
|
||||
peer->Encode(&pdg, sizeof(TPacketDGChangeName));
|
||||
}
|
||||
|
||||
|
||||
@@ -128,6 +128,12 @@ std::unique_ptr<SQLMsg> CDBManager::DirectQuery(const char* c_pszQuery, int iSlo
|
||||
return msg;
|
||||
}
|
||||
|
||||
CAsyncSQL* CDBManager::GetDirectSQL(int iSlot)
|
||||
{
|
||||
assert(iSlot < SQL_MAX_NUM);
|
||||
return m_directSQL[iSlot].get();
|
||||
}
|
||||
|
||||
extern CPacketInfo g_query_info;
|
||||
extern int g_query_count[2];
|
||||
|
||||
@@ -182,4 +188,3 @@ void CDBManager::QueryLocaleSet()
|
||||
m_asyncSQL[n]->QueryLocaleSet();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -43,6 +43,7 @@ class CDBManager : public singleton<CDBManager>
|
||||
void ReturnQuery(const char * c_pszQuery, int iType, IDENT dwIdent, void * pvData, int iSlot = SQL_PLAYER);
|
||||
void AsyncQuery(const char * c_pszQuery, int iSlot = SQL_PLAYER);
|
||||
std::unique_ptr<SQLMsg> DirectQuery(const char* c_pszQuery, int iSlot = SQL_PLAYER);
|
||||
CAsyncSQL* GetDirectSQL(int iSlot = SQL_PLAYER);
|
||||
|
||||
SQLMsg * PopResult();
|
||||
SQLMsg * PopResult(eSQL_SLOT slot );
|
||||
|
||||
@@ -30,6 +30,12 @@ void CStmt::Destroy()
|
||||
free(m_puiParamLen);
|
||||
m_puiParamLen = NULL;
|
||||
}
|
||||
|
||||
m_vec_param.clear();
|
||||
m_vec_result.clear();
|
||||
m_vec_result_len.clear();
|
||||
m_uiParamCount = 0;
|
||||
m_uiResultCount = 0;
|
||||
}
|
||||
|
||||
void CStmt::Error(const char * c_pszMsg)
|
||||
@@ -41,6 +47,8 @@ bool CStmt::Prepare(CAsyncSQL * sql, const char * c_pszQuery)
|
||||
{
|
||||
m_pkStmt = mysql_stmt_init(sql->GetSQLHandle());
|
||||
m_stQuery = c_pszQuery;
|
||||
m_uiParamCount = 0;
|
||||
m_uiResultCount = 0;
|
||||
|
||||
if (mysql_stmt_prepare(m_pkStmt, m_stQuery.c_str(), m_stQuery.length()))
|
||||
{
|
||||
@@ -48,11 +56,7 @@ bool CStmt::Prepare(CAsyncSQL * sql, const char * c_pszQuery)
|
||||
return false;
|
||||
}
|
||||
|
||||
int iParamCount = 0;
|
||||
|
||||
for (unsigned int i = 0; i < m_stQuery.length(); ++i)
|
||||
if (c_pszQuery[i] == '?')
|
||||
++iParamCount;
|
||||
const auto iParamCount = mysql_stmt_param_count(m_pkStmt);
|
||||
|
||||
if (iParamCount)
|
||||
{
|
||||
@@ -62,13 +66,12 @@ bool CStmt::Prepare(CAsyncSQL * sql, const char * c_pszQuery)
|
||||
m_puiParamLen = (long unsigned int *) calloc(iParamCount, sizeof(long unsigned int));
|
||||
}
|
||||
|
||||
m_vec_result.resize(48);
|
||||
memset(&m_vec_result[0], 0, sizeof(MYSQL_BIND) * 48);
|
||||
|
||||
if (mysql_stmt_bind_result(m_pkStmt, &m_vec_result[0]))
|
||||
const auto iFieldCount = mysql_stmt_field_count(m_pkStmt);
|
||||
if (iFieldCount)
|
||||
{
|
||||
Error("mysql_stmt_bind_result");
|
||||
return 0;
|
||||
m_vec_result.resize(iFieldCount);
|
||||
memset(&m_vec_result[0], 0, sizeof(MYSQL_BIND) * iFieldCount);
|
||||
m_vec_result_len.resize(iFieldCount, 0);
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -114,6 +117,7 @@ bool CStmt::BindResult(enum_field_types type, void * p, int iMaxLen)
|
||||
bind->buffer_type = type;
|
||||
bind->buffer = (void *) p;
|
||||
bind->buffer_length = iMaxLen;
|
||||
bind->length = &m_vec_result_len[m_uiResultCount - 1];
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -130,9 +134,21 @@ int CStmt::Execute()
|
||||
MYSQL_BIND * bind = &m_vec_param[i];
|
||||
|
||||
if (bind->buffer_type == MYSQL_TYPE_STRING)
|
||||
{
|
||||
*(m_puiParamLen + i) = strlen((const char *) bind->buffer);
|
||||
sys_log(0, "param %d len %d buf %s", i, *m_puiParamLen, (const char *) bind->buffer);
|
||||
}
|
||||
|
||||
if (!m_vec_result.empty())
|
||||
{
|
||||
if (m_uiResultCount != m_vec_result.size())
|
||||
{
|
||||
sys_log(0, "Result binding not enough %d, expected %d query: %s", m_uiResultCount, m_vec_result.size(), m_stQuery.c_str());
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (mysql_stmt_bind_result(m_pkStmt, &m_vec_result[0]))
|
||||
{
|
||||
Error("mysql_stmt_bind_result");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -157,3 +173,10 @@ bool CStmt::Fetch()
|
||||
return !mysql_stmt_fetch(m_pkStmt);
|
||||
}
|
||||
|
||||
unsigned long CStmt::GetResultLength(unsigned int index) const
|
||||
{
|
||||
if (index >= m_vec_result_len.size())
|
||||
return 0;
|
||||
|
||||
return m_vec_result_len[index];
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ class CStmt
|
||||
bool BindResult(enum_field_types type, void * p, int iMaxLen=0);
|
||||
int Execute();
|
||||
bool Fetch();
|
||||
unsigned long GetResultLength(unsigned int index) const;
|
||||
|
||||
void Error(const char * c_pszMsg);
|
||||
|
||||
@@ -35,6 +36,7 @@ class CStmt
|
||||
long unsigned int * m_puiParamLen;
|
||||
|
||||
std::vector<MYSQL_BIND> m_vec_result;
|
||||
std::vector<unsigned long> m_vec_result_len;
|
||||
unsigned int m_uiResultCount;
|
||||
};
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#include "stdafx.h"
|
||||
|
||||
#ifndef __USE_SELECT__
|
||||
#if defined(OS_FREEBSD)
|
||||
|
||||
LPFDWATCH fdwatch_new(int nfiles)
|
||||
{
|
||||
@@ -230,7 +230,307 @@ void * fdwatch_get_client_data(LPFDWATCH fdw, unsigned int event_idx)
|
||||
|
||||
return (fdw->fd_data[fd]);
|
||||
}
|
||||
#else // ifndef __USE_SELECT__
|
||||
|
||||
#elif defined(__linux__)
|
||||
|
||||
#ifndef EPOLLRDHUP
|
||||
#define EPOLLRDHUP 0
|
||||
#endif
|
||||
|
||||
static int fdwatch_interest_mask(int rw)
|
||||
{
|
||||
return rw & (FDW_READ | FDW_WRITE);
|
||||
}
|
||||
|
||||
static uint32_t fdwatch_build_epoll_events(int rw)
|
||||
{
|
||||
uint32_t events = 0;
|
||||
|
||||
if (rw & FDW_READ)
|
||||
events |= EPOLLIN | EPOLLRDHUP;
|
||||
|
||||
if (rw & FDW_WRITE)
|
||||
events |= EPOLLOUT;
|
||||
|
||||
return events;
|
||||
}
|
||||
|
||||
static int fdwatch_epoll_ctl(LPFDWATCH fdw, int op, socket_t fd)
|
||||
{
|
||||
struct epoll_event event;
|
||||
|
||||
memset(&event, 0, sizeof(event));
|
||||
event.events = fdwatch_build_epoll_events(fdw->fd_rw[fd]);
|
||||
event.data.fd = fd;
|
||||
|
||||
if (op == EPOLL_CTL_DEL)
|
||||
return epoll_ctl(fdw->epfd, op, fd, NULL);
|
||||
|
||||
return epoll_ctl(fdw->epfd, op, fd, &event);
|
||||
}
|
||||
|
||||
static bool fdwatch_update_registration(LPFDWATCH fdw, socket_t fd, int old_rw)
|
||||
{
|
||||
const int old_interest = fdwatch_interest_mask(old_rw);
|
||||
const int new_interest = fdwatch_interest_mask(fdw->fd_rw[fd]);
|
||||
|
||||
if (!old_interest && !new_interest)
|
||||
return true;
|
||||
|
||||
if (!new_interest)
|
||||
{
|
||||
if (fdwatch_epoll_ctl(fdw, EPOLL_CTL_DEL, fd) == 0 || errno == ENOENT)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!old_interest)
|
||||
{
|
||||
if (fdwatch_epoll_ctl(fdw, EPOLL_CTL_ADD, fd) == 0)
|
||||
return true;
|
||||
|
||||
if (errno == EEXIST && fdwatch_epoll_ctl(fdw, EPOLL_CTL_MOD, fd) == 0)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (fdwatch_epoll_ctl(fdw, EPOLL_CTL_MOD, fd) == 0)
|
||||
return true;
|
||||
|
||||
if (errno == ENOENT && fdwatch_epoll_ctl(fdw, EPOLL_CTL_ADD, fd) == 0)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
LPFDWATCH fdwatch_new(int nfiles)
|
||||
{
|
||||
LPFDWATCH fdw;
|
||||
int epfd;
|
||||
|
||||
epfd = epoll_create1(EPOLL_CLOEXEC);
|
||||
|
||||
if (epfd == -1)
|
||||
{
|
||||
sys_err("%s", strerror(errno));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
CREATE(fdw, FDWATCH, 1);
|
||||
|
||||
fdw->epfd = epfd;
|
||||
fdw->nfiles = nfiles;
|
||||
|
||||
CREATE(fdw->ep_events, EPOLL_EVENT, nfiles);
|
||||
CREATE(fdw->ready_events, EPOLL_EVENT, nfiles * 2);
|
||||
CREATE(fdw->fd_rw, int, nfiles);
|
||||
CREATE(fdw->fd_data, void*, nfiles);
|
||||
|
||||
return (fdw);
|
||||
}
|
||||
|
||||
void fdwatch_clear_fd(LPFDWATCH fdw, socket_t fd)
|
||||
{
|
||||
if (fd < 0 || fd >= fdw->nfiles)
|
||||
return;
|
||||
|
||||
fdw->fd_data[fd] = NULL;
|
||||
fdw->fd_rw[fd] = 0;
|
||||
}
|
||||
|
||||
void fdwatch_delete(LPFDWATCH fdw)
|
||||
{
|
||||
close(fdw->epfd);
|
||||
free(fdw->fd_data);
|
||||
free(fdw->fd_rw);
|
||||
free(fdw->ep_events);
|
||||
free(fdw->ready_events);
|
||||
free(fdw);
|
||||
}
|
||||
|
||||
int fdwatch_check_fd(LPFDWATCH fdw, socket_t fd)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void fdwatch_add_fd(LPFDWATCH fdw, socket_t fd, void* client_data, int rw, int oneshot)
|
||||
{
|
||||
int old_rw;
|
||||
void* old_data;
|
||||
|
||||
if (fd < 0 || fd >= fdw->nfiles)
|
||||
{
|
||||
sys_err("fd overflow %d", fd);
|
||||
return;
|
||||
}
|
||||
|
||||
old_rw = fdw->fd_rw[fd];
|
||||
|
||||
if (old_rw & rw)
|
||||
return;
|
||||
|
||||
old_data = fdw->fd_data[fd];
|
||||
|
||||
fdw->fd_rw[fd] |= rw;
|
||||
|
||||
if (rw & FDW_WRITE)
|
||||
{
|
||||
if (oneshot)
|
||||
fdw->fd_rw[fd] |= FDW_WRITE_ONESHOT;
|
||||
else
|
||||
fdw->fd_rw[fd] &= ~FDW_WRITE_ONESHOT;
|
||||
}
|
||||
|
||||
fdw->fd_data[fd] = client_data;
|
||||
|
||||
if (!fdwatch_update_registration(fdw, fd, old_rw))
|
||||
{
|
||||
sys_err("epoll_ctl failed for fd %d [%d] %s", fd, errno, strerror(errno));
|
||||
fdw->fd_rw[fd] = old_rw;
|
||||
fdw->fd_data[fd] = old_data;
|
||||
}
|
||||
}
|
||||
|
||||
void fdwatch_del_fd(LPFDWATCH fdw, socket_t fd)
|
||||
{
|
||||
int old_rw;
|
||||
|
||||
if (fd < 0 || fd >= fdw->nfiles)
|
||||
return;
|
||||
|
||||
old_rw = fdw->fd_rw[fd];
|
||||
|
||||
if (!fdwatch_interest_mask(old_rw) && !fdw->fd_data[fd])
|
||||
return;
|
||||
|
||||
fdwatch_clear_fd(fdw, fd);
|
||||
|
||||
if (!fdwatch_update_registration(fdw, fd, old_rw))
|
||||
sys_err("epoll_ctl delete failed for fd %d [%d] %s", fd, errno, strerror(errno));
|
||||
}
|
||||
|
||||
int fdwatch(LPFDWATCH fdw, struct timeval *timeout)
|
||||
{
|
||||
int ready;
|
||||
int timeout_ms;
|
||||
int event_idx;
|
||||
|
||||
timeout_ms = 0;
|
||||
|
||||
if (timeout)
|
||||
timeout_ms = static_cast<int>(timeout->tv_sec * 1000 + timeout->tv_usec / 1000);
|
||||
|
||||
ready = epoll_wait(fdw->epfd, fdw->ep_events, fdw->nfiles, timeout_ms);
|
||||
|
||||
if (ready == -1)
|
||||
return -1;
|
||||
|
||||
event_idx = 0;
|
||||
|
||||
for (int i = 0; i < ready; ++i)
|
||||
{
|
||||
const int fd = fdw->ep_events[i].data.fd;
|
||||
const uint32_t events = fdw->ep_events[i].events;
|
||||
|
||||
if (fd < 0 || fd >= fdw->nfiles)
|
||||
{
|
||||
sys_err("epoll ident overflow %d nfiles: %d", fd, fdw->nfiles);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (events & (EPOLLERR | EPOLLHUP | EPOLLRDHUP))
|
||||
{
|
||||
fdw->ready_events[event_idx] = fdw->ep_events[i];
|
||||
++event_idx;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((events & EPOLLIN) && event_idx < fdw->nfiles * 2)
|
||||
{
|
||||
fdw->ready_events[event_idx].data.fd = fd;
|
||||
fdw->ready_events[event_idx].events = EPOLLIN;
|
||||
++event_idx;
|
||||
}
|
||||
|
||||
if ((events & EPOLLOUT) && event_idx < fdw->nfiles * 2)
|
||||
{
|
||||
fdw->ready_events[event_idx].data.fd = fd;
|
||||
fdw->ready_events[event_idx].events = EPOLLOUT;
|
||||
++event_idx;
|
||||
}
|
||||
}
|
||||
|
||||
return event_idx;
|
||||
}
|
||||
|
||||
void* fdwatch_get_client_data(LPFDWATCH fdw, unsigned int event_idx)
|
||||
{
|
||||
const int fd = fdw->ready_events[event_idx].data.fd;
|
||||
|
||||
assert(event_idx < fdw->nfiles * 2);
|
||||
|
||||
if (fd < 0 || fd >= fdw->nfiles)
|
||||
return NULL;
|
||||
|
||||
return fdw->fd_data[fd];
|
||||
}
|
||||
|
||||
int fdwatch_get_ident(LPFDWATCH fdw, unsigned int event_idx)
|
||||
{
|
||||
assert(event_idx < fdw->nfiles * 2);
|
||||
return fdw->ready_events[event_idx].data.fd;
|
||||
}
|
||||
|
||||
void fdwatch_clear_event(LPFDWATCH fdw, socket_t fd, unsigned int event_idx)
|
||||
{
|
||||
assert(event_idx < fdw->nfiles * 2);
|
||||
|
||||
if (fdw->ready_events[event_idx].data.fd != fd)
|
||||
return;
|
||||
|
||||
fdw->ready_events[event_idx].data.fd = -1;
|
||||
fdw->ready_events[event_idx].events = 0;
|
||||
}
|
||||
|
||||
int fdwatch_check_event(LPFDWATCH fdw, socket_t fd, unsigned int event_idx)
|
||||
{
|
||||
assert(event_idx < fdw->nfiles * 2);
|
||||
|
||||
if (fdw->ready_events[event_idx].data.fd != fd)
|
||||
return 0;
|
||||
|
||||
if (fdw->ready_events[event_idx].events & (EPOLLERR | EPOLLHUP | EPOLLRDHUP))
|
||||
return FDW_EOF;
|
||||
|
||||
if ((fdw->ready_events[event_idx].events & EPOLLIN) && (fdw->fd_rw[fd] & FDW_READ))
|
||||
return FDW_READ;
|
||||
|
||||
if ((fdw->ready_events[event_idx].events & EPOLLOUT) && (fdw->fd_rw[fd] & FDW_WRITE))
|
||||
{
|
||||
if (fdw->fd_rw[fd] & FDW_WRITE_ONESHOT)
|
||||
{
|
||||
const int old_rw = fdw->fd_rw[fd];
|
||||
|
||||
fdw->fd_rw[fd] &= ~FDW_WRITE;
|
||||
|
||||
if (!fdwatch_update_registration(fdw, fd, old_rw))
|
||||
sys_err("epoll_ctl write reset failed for fd %d [%d] %s", fd, errno, strerror(errno));
|
||||
}
|
||||
|
||||
return FDW_WRITE;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fdwatch_get_buffer_size(LPFDWATCH fdw, socket_t fd)
|
||||
{
|
||||
return INT_MAX;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#ifdef OS_WINDOWS
|
||||
static int win32_init_refcount = 0;
|
||||
|
||||
@@ -1,73 +1,76 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef __USE_SELECT__
|
||||
typedef struct fdwatch FDWATCH;
|
||||
typedef struct fdwatch * LPFDWATCH;
|
||||
|
||||
typedef struct fdwatch FDWATCH;
|
||||
typedef struct fdwatch * LPFDWATCH;
|
||||
enum EFdwatch
|
||||
{
|
||||
FDW_NONE = 0,
|
||||
FDW_READ = 1,
|
||||
FDW_WRITE = 2,
|
||||
FDW_WRITE_ONESHOT = 4,
|
||||
FDW_EOF = 8,
|
||||
};
|
||||
|
||||
enum EFdwatch
|
||||
{
|
||||
FDW_NONE = 0,
|
||||
FDW_READ = 1,
|
||||
FDW_WRITE = 2,
|
||||
FDW_WRITE_ONESHOT = 4,
|
||||
FDW_EOF = 8,
|
||||
};
|
||||
#if defined(OS_FREEBSD)
|
||||
|
||||
typedef struct kevent KEVENT;
|
||||
typedef struct kevent * LPKEVENT;
|
||||
typedef int KQUEUE;
|
||||
typedef struct kevent KEVENT;
|
||||
typedef struct kevent * LPKEVENT;
|
||||
typedef int KQUEUE;
|
||||
|
||||
struct fdwatch
|
||||
{
|
||||
KQUEUE kq;
|
||||
struct fdwatch
|
||||
{
|
||||
KQUEUE kq;
|
||||
|
||||
int nfiles;
|
||||
int nfiles;
|
||||
|
||||
LPKEVENT kqevents;
|
||||
int nkqevents;
|
||||
LPKEVENT kqevents;
|
||||
int nkqevents;
|
||||
|
||||
LPKEVENT kqrevents;
|
||||
int * fd_event_idx;
|
||||
LPKEVENT kqrevents;
|
||||
int * fd_event_idx;
|
||||
|
||||
void ** fd_data;
|
||||
int * fd_rw;
|
||||
};
|
||||
void ** fd_data;
|
||||
int * fd_rw;
|
||||
};
|
||||
|
||||
#elif defined(__linux__)
|
||||
|
||||
typedef struct epoll_event EPOLL_EVENT;
|
||||
typedef struct epoll_event * LPEPOLL_EVENT;
|
||||
|
||||
struct fdwatch
|
||||
{
|
||||
int epfd;
|
||||
int nfiles;
|
||||
LPEPOLL_EVENT ep_events;
|
||||
LPEPOLL_EVENT ready_events;
|
||||
void ** fd_data;
|
||||
int * fd_rw;
|
||||
};
|
||||
|
||||
#else
|
||||
|
||||
typedef struct fdwatch FDWATCH;
|
||||
typedef struct fdwatch * LPFDWATCH;
|
||||
struct fdwatch
|
||||
{
|
||||
fd_set rfd_set;
|
||||
fd_set wfd_set;
|
||||
|
||||
enum EFdwatch
|
||||
{
|
||||
FDW_NONE = 0,
|
||||
FDW_READ = 1,
|
||||
FDW_WRITE = 2,
|
||||
FDW_WRITE_ONESHOT = 4,
|
||||
FDW_EOF = 8,
|
||||
};
|
||||
socket_t* select_fds;
|
||||
int* select_rfdidx;
|
||||
|
||||
struct fdwatch
|
||||
{
|
||||
fd_set rfd_set;
|
||||
fd_set wfd_set;
|
||||
int nselect_fds;
|
||||
|
||||
socket_t* select_fds;
|
||||
int* select_rfdidx;
|
||||
fd_set working_rfd_set;
|
||||
fd_set working_wfd_set;
|
||||
|
||||
int nselect_fds;
|
||||
int nfiles;
|
||||
|
||||
fd_set working_rfd_set;
|
||||
fd_set working_wfd_set;
|
||||
void** fd_data;
|
||||
int* fd_rw;
|
||||
};
|
||||
|
||||
int nfiles;
|
||||
|
||||
void** fd_data;
|
||||
int* fd_rw;
|
||||
};
|
||||
|
||||
#endif // WIN32
|
||||
#endif
|
||||
|
||||
|
||||
LPFDWATCH fdwatch_new(int nfiles);
|
||||
@@ -82,4 +85,3 @@ void * fdwatch_get_client_data(LPFDWATCH fdw, unsigned int event_idx);
|
||||
void fdwatch_del_fd(LPFDWATCH fdw, socket_t fd);
|
||||
int fdwatch_get_buffer_size(LPFDWATCH fdw, socket_t fd);
|
||||
int fdwatch_get_ident(LPFDWATCH fdw, unsigned int event_idx);
|
||||
|
||||
|
||||
@@ -66,7 +66,7 @@ inline double rint(double x)
|
||||
|
||||
#else
|
||||
|
||||
#ifndef OS_FREEBSD
|
||||
#if !defined(OS_FREEBSD) && !defined(__linux__)
|
||||
#define __USE_SELECT__
|
||||
#ifdef __CYGWIN__
|
||||
#define _POSIX_SOURCE 1
|
||||
@@ -102,6 +102,8 @@ inline double rint(double x)
|
||||
|
||||
#ifdef OS_FREEBSD
|
||||
#include <sys/event.h>
|
||||
#elif defined(__linux__)
|
||||
#include <sys/epoll.h>
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user