Compare commits
13 Commits
main
...
74827a1907
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
74827a1907 | ||
|
|
8a5f6e70ee | ||
|
|
0f69efeba4 | ||
|
|
a0ad693e13 | ||
|
|
cc8fa1e78a | ||
|
|
dc76f5da87 | ||
|
|
2bbd624c9a | ||
|
|
44720d0691 | ||
|
|
ccc1a8899d | ||
|
|
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)
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include "Config.h"
|
||||
#include "DBManager.h"
|
||||
#include "QID.h"
|
||||
#include "libsql/Statement.h"
|
||||
#include "GuildManager.h"
|
||||
#include "PrivManager.h"
|
||||
#include "MoneyLog.h"
|
||||
@@ -37,6 +38,186 @@ CPacketInfo g_item_info;
|
||||
int g_item_count = 0;
|
||||
int g_query_count[2];
|
||||
|
||||
namespace
|
||||
{
|
||||
bool PrepareClientPlayerStmt(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 LoadSafeboxPassword(uint32_t accountId, char* password, size_t passwordSize, bool* foundRow)
|
||||
{
|
||||
CStmt stmt;
|
||||
const std::string query = std::string("SELECT password FROM safebox") + GetTablePostfix() + " WHERE account_id=?";
|
||||
|
||||
if (!PrepareClientPlayerStmt(stmt, query))
|
||||
return false;
|
||||
|
||||
password[0] = '\0';
|
||||
*foundRow = false;
|
||||
|
||||
if (!stmt.BindParam(MYSQL_TYPE_LONG, &accountId)
|
||||
|| !stmt.BindResult(MYSQL_TYPE_STRING, password, static_cast<int>(passwordSize)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!stmt.Execute())
|
||||
return false;
|
||||
|
||||
if (stmt.iRows == 0)
|
||||
return true;
|
||||
|
||||
if (!stmt.Fetch())
|
||||
return false;
|
||||
|
||||
size_t passwordLen = stmt.GetResultLength(0);
|
||||
if (passwordLen >= passwordSize)
|
||||
passwordLen = passwordSize - 1;
|
||||
|
||||
password[passwordLen] = '\0';
|
||||
*foundRow = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LoadSafeboxTable(uint32_t accountId, const char* providedPassword, bool isMall, TSafeboxTable* safebox, bool* wrongPassword)
|
||||
{
|
||||
CStmt stmt;
|
||||
const std::string query = std::string("SELECT account_id, size, password FROM safebox") + GetTablePostfix() + " WHERE account_id=?";
|
||||
uint32_t loadedAccountId = 0;
|
||||
uint32_t loadedSize = 0;
|
||||
char storedPassword[SAFEBOX_PASSWORD_MAX_LEN + 1] = {};
|
||||
|
||||
if (!PrepareClientPlayerStmt(stmt, query))
|
||||
return false;
|
||||
|
||||
*wrongPassword = false;
|
||||
memset(safebox, 0, sizeof(TSafeboxTable));
|
||||
|
||||
if (!stmt.BindParam(MYSQL_TYPE_LONG, &accountId)
|
||||
|| !stmt.BindResult(MYSQL_TYPE_LONG, &loadedAccountId)
|
||||
|| !stmt.BindResult(MYSQL_TYPE_LONG, &loadedSize)
|
||||
|| !stmt.BindResult(MYSQL_TYPE_STRING, storedPassword, sizeof(storedPassword)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!stmt.Execute())
|
||||
return false;
|
||||
|
||||
if (stmt.iRows == 0)
|
||||
{
|
||||
safebox->dwID = accountId;
|
||||
*wrongPassword = strcmp("000000", providedPassword) != 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!stmt.Fetch())
|
||||
return false;
|
||||
|
||||
size_t passwordLen = stmt.GetResultLength(2);
|
||||
if (passwordLen >= sizeof(storedPassword))
|
||||
passwordLen = sizeof(storedPassword) - 1;
|
||||
|
||||
storedPassword[passwordLen] = '\0';
|
||||
|
||||
if (((passwordLen == 0) && strcmp("000000", providedPassword))
|
||||
|| ((passwordLen != 0) && strcmp(storedPassword, providedPassword)))
|
||||
{
|
||||
*wrongPassword = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
safebox->dwID = loadedAccountId == 0 ? accountId : loadedAccountId;
|
||||
safebox->bSize = static_cast<BYTE>(loadedSize);
|
||||
|
||||
if (isMall)
|
||||
{
|
||||
safebox->bSize = 1;
|
||||
sys_log(0, "MALL id[%u] size[%u]", safebox->dwID, safebox->bSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
sys_log(0, "SAFEBOX id[%u] size[%u]", safebox->dwID, safebox->bSize);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void QueueSafeboxItemsLoad(CPeer* peer, CClientManager::ClientHandleInfo* info)
|
||||
{
|
||||
char query[512];
|
||||
snprintf(query, sizeof(query),
|
||||
"SELECT id, window+0, pos, count, vnum, socket0, socket1, socket2, "
|
||||
"attrtype0, attrvalue0, "
|
||||
"attrtype1, attrvalue1, "
|
||||
"attrtype2, attrvalue2, "
|
||||
"attrtype3, attrvalue3, "
|
||||
"attrtype4, attrvalue4, "
|
||||
"attrtype5, attrvalue5, "
|
||||
"attrtype6, attrvalue6 "
|
||||
"FROM item%s WHERE owner_id=%d AND window='%s'",
|
||||
GetTablePostfix(), info->account_id, info->ip[0] == 0 ? "SAFEBOX" : "MALL");
|
||||
|
||||
CDBManager::instance().ReturnQuery(query, QID_SAFEBOX_LOAD, peer->GetHandle(), info);
|
||||
}
|
||||
|
||||
bool UpdateSafeboxPassword(uint32_t accountId, const char* password)
|
||||
{
|
||||
CStmt stmt;
|
||||
const std::string query = std::string("UPDATE safebox") + GetTablePostfix() + " SET password=? WHERE account_id=?";
|
||||
|
||||
if (!PrepareClientPlayerStmt(stmt, query))
|
||||
return false;
|
||||
|
||||
if (!stmt.BindParam(MYSQL_TYPE_STRING, const_cast<char*>(password), SAFEBOX_PASSWORD_MAX_LEN + 1)
|
||||
|| !stmt.BindParam(MYSQL_TYPE_LONG, &accountId))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return stmt.Execute();
|
||||
}
|
||||
|
||||
bool FindPlayerIdByName(const char* playerName, uint32_t* playerId)
|
||||
{
|
||||
CStmt stmt;
|
||||
std::string query;
|
||||
|
||||
if (g_stLocale == "sjis")
|
||||
query = std::string("SELECT id FROM player") + GetTablePostfix() + " WHERE name=? collate sjis_japanese_ci";
|
||||
else
|
||||
query = std::string("SELECT id FROM player") + GetTablePostfix() + " WHERE name=?";
|
||||
|
||||
if (!PrepareClientPlayerStmt(stmt, query))
|
||||
return false;
|
||||
|
||||
*playerId = 0;
|
||||
|
||||
if (!stmt.BindParam(MYSQL_TYPE_STRING, const_cast<char*>(playerName), CHARACTER_NAME_MAX_LEN + 1)
|
||||
|| !stmt.BindResult(MYSQL_TYPE_LONG, playerId))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!stmt.Execute())
|
||||
return false;
|
||||
|
||||
if (stmt.iRows == 0)
|
||||
return true;
|
||||
|
||||
return stmt.Fetch();
|
||||
}
|
||||
}
|
||||
|
||||
CClientManager::CClientManager() :
|
||||
m_pkAuthPeer(NULL),
|
||||
m_iPlayerIDStart(0),
|
||||
@@ -451,15 +632,29 @@ void CClientManager::QUERY_SAFEBOX_LOAD(CPeer * pkPeer, DWORD dwHandle, TSafebox
|
||||
pi->ip[0] = bMall ? 1 : 0;
|
||||
strlcpy(pi->login, packet->szLogin, sizeof(pi->login));
|
||||
|
||||
char szQuery[QUERY_MAX_LEN];
|
||||
snprintf(szQuery, sizeof(szQuery),
|
||||
"SELECT account_id, size, password FROM safebox%s WHERE account_id=%u",
|
||||
GetTablePostfix(), packet->dwID);
|
||||
|
||||
if (g_log)
|
||||
sys_log(0, "GD::SAFEBOX_LOAD (handle: %d account.id %u is_mall %d)", dwHandle, packet->dwID, bMall ? 1 : 0);
|
||||
|
||||
CDBManager::instance().ReturnQuery(szQuery, QID_SAFEBOX_LOAD, pkPeer->GetHandle(), pi);
|
||||
TSafeboxTable* safebox = new TSafeboxTable;
|
||||
bool wrongPassword = false;
|
||||
|
||||
if (!LoadSafeboxTable(packet->dwID, pi->safebox_password, bMall, safebox, &wrongPassword))
|
||||
{
|
||||
delete safebox;
|
||||
delete pi;
|
||||
return;
|
||||
}
|
||||
|
||||
if (wrongPassword)
|
||||
{
|
||||
delete safebox;
|
||||
delete pi;
|
||||
pkPeer->EncodeHeader(DG::SAFEBOX_WRONG_PASSWORD, dwHandle, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
pi->pSafebox = safebox;
|
||||
QueueSafeboxItemsLoad(pkPeer, pi);
|
||||
}
|
||||
|
||||
void CClientManager::RESULT_SAFEBOX_LOAD(CPeer * pkPeer, SQLMsg * msg)
|
||||
@@ -467,177 +662,89 @@ void CClientManager::RESULT_SAFEBOX_LOAD(CPeer * pkPeer, SQLMsg * msg)
|
||||
CQueryInfo * qi = (CQueryInfo *) msg->pvUserData;
|
||||
ClientHandleInfo * pi = (ClientHandleInfo *) qi->pvData;
|
||||
DWORD dwHandle = pi->dwHandle;
|
||||
|
||||
// 여기에서 사용하는 account_index는 쿼리 순서를 말한다.
|
||||
// 첫번째 패스워드 알아내기 위해 하는 쿼리가 0
|
||||
// 두번째 실제 데이터를 얻어놓는 쿼리가 1
|
||||
|
||||
if (pi->account_index == 0)
|
||||
if (!pi->pSafebox)
|
||||
{
|
||||
char szSafeboxPassword[SAFEBOX_PASSWORD_MAX_LEN + 1];
|
||||
strlcpy(szSafeboxPassword, pi->safebox_password, sizeof(szSafeboxPassword));
|
||||
|
||||
TSafeboxTable * pSafebox = new TSafeboxTable;
|
||||
memset(pSafebox, 0, sizeof(TSafeboxTable));
|
||||
|
||||
SQLResult * res = msg->Get();
|
||||
|
||||
if (res->uiNumRows == 0)
|
||||
{
|
||||
if (strcmp("000000", szSafeboxPassword))
|
||||
{
|
||||
pkPeer->EncodeHeader(DG::SAFEBOX_WRONG_PASSWORD, dwHandle, 0);
|
||||
delete pSafebox;
|
||||
delete pi;
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
MYSQL_ROW row = mysql_fetch_row(res->pSQLResult);
|
||||
|
||||
// 비밀번호가 틀리면..
|
||||
if (((!row[2] || !*row[2]) && strcmp("000000", szSafeboxPassword)) ||
|
||||
((row[2] && *row[2]) && strcmp(row[2], szSafeboxPassword)))
|
||||
{
|
||||
pkPeer->EncodeHeader(DG::SAFEBOX_WRONG_PASSWORD, dwHandle, 0);
|
||||
delete pSafebox;
|
||||
delete pi;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!row[0])
|
||||
pSafebox->dwID = 0;
|
||||
else
|
||||
str_to_number(pSafebox->dwID, row[0]);
|
||||
|
||||
if (!row[1])
|
||||
pSafebox->bSize = 0;
|
||||
else
|
||||
str_to_number(pSafebox->bSize, row[1]);
|
||||
/*
|
||||
if (!row[3])
|
||||
pSafebox->dwGold = 0;
|
||||
else
|
||||
pSafebox->dwGold = atoi(row[3]);
|
||||
*/
|
||||
if (pi->ip[0] == 1)
|
||||
{
|
||||
pSafebox->bSize = 1;
|
||||
sys_log(0, "MALL id[%d] size[%d]", pSafebox->dwID, pSafebox->bSize);
|
||||
}
|
||||
else
|
||||
sys_log(0, "SAFEBOX id[%d] size[%d]", pSafebox->dwID, pSafebox->bSize);
|
||||
}
|
||||
|
||||
if (0 == pSafebox->dwID)
|
||||
pSafebox->dwID = pi->account_id;
|
||||
|
||||
pi->pSafebox = pSafebox;
|
||||
|
||||
char szQuery[512];
|
||||
snprintf(szQuery, sizeof(szQuery),
|
||||
"SELECT id, window+0, pos, count, vnum, socket0, socket1, socket2, "
|
||||
"attrtype0, attrvalue0, "
|
||||
"attrtype1, attrvalue1, "
|
||||
"attrtype2, attrvalue2, "
|
||||
"attrtype3, attrvalue3, "
|
||||
"attrtype4, attrvalue4, "
|
||||
"attrtype5, attrvalue5, "
|
||||
"attrtype6, attrvalue6 "
|
||||
"FROM item%s WHERE owner_id=%d AND window='%s'",
|
||||
GetTablePostfix(), pi->account_id, pi->ip[0] == 0 ? "SAFEBOX" : "MALL");
|
||||
|
||||
pi->account_index = 1;
|
||||
|
||||
CDBManager::instance().ReturnQuery(szQuery, QID_SAFEBOX_LOAD, pkPeer->GetHandle(), pi);
|
||||
sys_err("null safebox pointer!");
|
||||
delete pi;
|
||||
return;
|
||||
}
|
||||
else
|
||||
|
||||
|
||||
// 쿼리에 에러가 있었으므로 응답할 경우 창고가 비어있는 것 처럼
|
||||
// 보이기 때문에 창고가 아얘 안열리는게 나음
|
||||
if (!msg->Get()->pSQLResult)
|
||||
{
|
||||
sys_err("null safebox result");
|
||||
delete pi;
|
||||
return;
|
||||
}
|
||||
|
||||
static std::vector<TPlayerItem> s_items;
|
||||
CreateItemTableFromRes(msg->Get()->pSQLResult, &s_items, pi->account_id);
|
||||
|
||||
std::set<TItemAward *> * pSet = ItemAwardManager::instance().GetByLogin(pi->login);
|
||||
|
||||
if (pSet && !m_vec_itemTable.empty())
|
||||
{
|
||||
|
||||
if (!pi->pSafebox)
|
||||
CGrid grid(5, MAX(1, pi->pSafebox->bSize) * 9);
|
||||
bool bEscape = false;
|
||||
|
||||
for (DWORD i = 0; i < s_items.size(); ++i)
|
||||
{
|
||||
sys_err("null safebox pointer!");
|
||||
delete pi;
|
||||
return;
|
||||
}
|
||||
TPlayerItem & r = s_items[i];
|
||||
|
||||
itertype(m_map_itemTableByVnum) it = m_map_itemTableByVnum.find(r.vnum);
|
||||
|
||||
// 쿼리에 에러가 있었으므로 응답할 경우 창고가 비어있는 것 처럼
|
||||
// 보이기 때문에 창고가 아얘 안열리는게 나음
|
||||
if (!msg->Get()->pSQLResult)
|
||||
{
|
||||
sys_err("null safebox result");
|
||||
delete pi;
|
||||
return;
|
||||
}
|
||||
|
||||
static std::vector<TPlayerItem> s_items;
|
||||
CreateItemTableFromRes(msg->Get()->pSQLResult, &s_items, pi->account_id);
|
||||
|
||||
std::set<TItemAward *> * pSet = ItemAwardManager::instance().GetByLogin(pi->login);
|
||||
|
||||
if (pSet && !m_vec_itemTable.empty())
|
||||
{
|
||||
|
||||
CGrid grid(5, MAX(1, pi->pSafebox->bSize) * 9);
|
||||
bool bEscape = false;
|
||||
|
||||
for (DWORD i = 0; i < s_items.size(); ++i)
|
||||
if (it == m_map_itemTableByVnum.end())
|
||||
{
|
||||
TPlayerItem & r = s_items[i];
|
||||
bEscape = true;
|
||||
sys_err("invalid item vnum %u in safebox: login %s", r.vnum, pi->login);
|
||||
break;
|
||||
}
|
||||
|
||||
itertype(m_map_itemTableByVnum) it = m_map_itemTableByVnum.find(r.vnum);
|
||||
grid.Put(r.pos, 1, it->second->bSize);
|
||||
}
|
||||
|
||||
if (!bEscape)
|
||||
{
|
||||
std::vector<std::pair<DWORD, DWORD> > vec_dwFinishedAwardID;
|
||||
|
||||
__typeof(pSet->begin()) it = pSet->begin();
|
||||
|
||||
char szQuery[512];
|
||||
|
||||
while (it != pSet->end())
|
||||
{
|
||||
TItemAward * pItemAward = *(it++);
|
||||
const DWORD& dwItemVnum = pItemAward->dwVnum;
|
||||
|
||||
if (pItemAward->bTaken)
|
||||
continue;
|
||||
|
||||
if (pi->ip[0] == 0 && pItemAward->bMall)
|
||||
continue;
|
||||
|
||||
if (pi->ip[0] == 1 && !pItemAward->bMall)
|
||||
continue;
|
||||
|
||||
itertype(m_map_itemTableByVnum) it = m_map_itemTableByVnum.find(pItemAward->dwVnum);
|
||||
|
||||
if (it == m_map_itemTableByVnum.end())
|
||||
{
|
||||
bEscape = true;
|
||||
sys_err("invalid item vnum %u in safebox: login %s", r.vnum, pi->login);
|
||||
break;
|
||||
sys_err("invalid item vnum %u in item_award: login %s", pItemAward->dwVnum, pi->login);
|
||||
continue;
|
||||
}
|
||||
|
||||
grid.Put(r.pos, 1, it->second->bSize);
|
||||
}
|
||||
TItemTable * pItemTable = it->second;
|
||||
|
||||
if (!bEscape)
|
||||
{
|
||||
std::vector<std::pair<DWORD, DWORD> > vec_dwFinishedAwardID;
|
||||
int iPos;
|
||||
|
||||
__typeof(pSet->begin()) it = pSet->begin();
|
||||
if ((iPos = grid.FindBlank(1, it->second->bSize)) == -1)
|
||||
break;
|
||||
|
||||
char szQuery[512];
|
||||
|
||||
while (it != pSet->end())
|
||||
{
|
||||
TItemAward * pItemAward = *(it++);
|
||||
const DWORD& dwItemVnum = pItemAward->dwVnum;
|
||||
|
||||
if (pItemAward->bTaken)
|
||||
continue;
|
||||
|
||||
if (pi->ip[0] == 0 && pItemAward->bMall)
|
||||
continue;
|
||||
|
||||
if (pi->ip[0] == 1 && !pItemAward->bMall)
|
||||
continue;
|
||||
|
||||
itertype(m_map_itemTableByVnum) it = m_map_itemTableByVnum.find(pItemAward->dwVnum);
|
||||
|
||||
if (it == m_map_itemTableByVnum.end())
|
||||
{
|
||||
sys_err("invalid item vnum %u in item_award: login %s", pItemAward->dwVnum, pi->login);
|
||||
continue;
|
||||
}
|
||||
|
||||
TItemTable * pItemTable = it->second;
|
||||
|
||||
int iPos;
|
||||
|
||||
if ((iPos = grid.FindBlank(1, it->second->bSize)) == -1)
|
||||
break;
|
||||
|
||||
TPlayerItem item;
|
||||
memset(&item, 0, sizeof(TPlayerItem));
|
||||
TPlayerItem item;
|
||||
memset(&item, 0, sizeof(TPlayerItem));
|
||||
|
||||
// DWORD dwSocket2 = 0;
|
||||
DWORD dwSocket2 = pItemAward->dwSocket2; //Fix
|
||||
@@ -766,22 +873,21 @@ void CClientManager::RESULT_SAFEBOX_LOAD(CPeer * pkPeer, SQLMsg * msg)
|
||||
grid.Put(iPos, 1, it->second->bSize);
|
||||
}
|
||||
|
||||
for (DWORD i = 0; i < vec_dwFinishedAwardID.size(); ++i)
|
||||
ItemAwardManager::instance().Taken(vec_dwFinishedAwardID[i].first, vec_dwFinishedAwardID[i].second);
|
||||
}
|
||||
for (DWORD i = 0; i < vec_dwFinishedAwardID.size(); ++i)
|
||||
ItemAwardManager::instance().Taken(vec_dwFinishedAwardID[i].first, vec_dwFinishedAwardID[i].second);
|
||||
}
|
||||
|
||||
pi->pSafebox->wItemCount = s_items.size();
|
||||
|
||||
pkPeer->EncodeHeader(pi->ip[0] == 0 ? DG::SAFEBOX_LOAD : DG::MALL_LOAD, dwHandle, sizeof(TSafeboxTable) + sizeof(TPlayerItem) * s_items.size());
|
||||
|
||||
pkPeer->Encode(pi->pSafebox, sizeof(TSafeboxTable));
|
||||
|
||||
if (!s_items.empty())
|
||||
pkPeer->Encode(&s_items[0], sizeof(TPlayerItem) * s_items.size());
|
||||
|
||||
delete pi;
|
||||
}
|
||||
|
||||
pi->pSafebox->wItemCount = s_items.size();
|
||||
|
||||
pkPeer->EncodeHeader(pi->ip[0] == 0 ? DG::SAFEBOX_LOAD : DG::MALL_LOAD, dwHandle, sizeof(TSafeboxTable) + sizeof(TPlayerItem) * s_items.size());
|
||||
|
||||
pkPeer->Encode(pi->pSafebox, sizeof(TSafeboxTable));
|
||||
|
||||
if (!s_items.empty())
|
||||
pkPeer->Encode(&s_items[0], sizeof(TPlayerItem) * s_items.size());
|
||||
|
||||
delete pi;
|
||||
}
|
||||
|
||||
void CClientManager::QUERY_SAFEBOX_CHANGE_SIZE(CPeer * pkPeer, DWORD dwHandle, TSafeboxChangeSizePacket * p)
|
||||
@@ -817,15 +923,23 @@ void CClientManager::RESULT_SAFEBOX_CHANGE_SIZE(CPeer * pkPeer, SQLMsg * msg)
|
||||
|
||||
void CClientManager::QUERY_SAFEBOX_CHANGE_PASSWORD(CPeer * pkPeer, DWORD dwHandle, TSafeboxChangePasswordPacket * p)
|
||||
{
|
||||
ClientHandleInfo * pi = new ClientHandleInfo(dwHandle);
|
||||
strlcpy(pi->safebox_password, p->szNewPassword, sizeof(pi->safebox_password));
|
||||
strlcpy(pi->login, p->szOldPassword, sizeof(pi->login));
|
||||
pi->account_id = p->dwID;
|
||||
BYTE result = 0;
|
||||
char storedPassword[SAFEBOX_PASSWORD_MAX_LEN + 1];
|
||||
bool foundRow = false;
|
||||
|
||||
char szQuery[QUERY_MAX_LEN];
|
||||
snprintf(szQuery, sizeof(szQuery), "SELECT password FROM safebox%s WHERE account_id=%u", GetTablePostfix(), p->dwID);
|
||||
if (LoadSafeboxPassword(p->dwID, storedPassword, sizeof(storedPassword), &foundRow))
|
||||
{
|
||||
const bool oldPasswordMatches =
|
||||
(foundRow
|
||||
&& ((storedPassword[0] && !strcasecmp(storedPassword, p->szOldPassword))
|
||||
|| (!storedPassword[0] && !strcmp("000000", p->szOldPassword))));
|
||||
|
||||
CDBManager::instance().ReturnQuery(szQuery, QID_SAFEBOX_CHANGE_PASSWORD, pkPeer->GetHandle(), pi);
|
||||
if (oldPasswordMatches && UpdateSafeboxPassword(p->dwID, p->szNewPassword))
|
||||
result = 1;
|
||||
}
|
||||
|
||||
pkPeer->EncodeHeader(DG::SAFEBOX_CHANGE_PASSWORD_ANSWER, dwHandle, sizeof(BYTE));
|
||||
pkPeer->EncodeBYTE(result);
|
||||
}
|
||||
|
||||
void CClientManager::RESULT_SAFEBOX_CHANGE_PASSWORD(CPeer * pkPeer, SQLMsg * msg)
|
||||
@@ -833,29 +947,20 @@ void CClientManager::RESULT_SAFEBOX_CHANGE_PASSWORD(CPeer * pkPeer, SQLMsg * msg
|
||||
CQueryInfo * qi = (CQueryInfo *) msg->pvUserData;
|
||||
ClientHandleInfo * p = (ClientHandleInfo *) qi->pvData;
|
||||
DWORD dwHandle = p->dwHandle;
|
||||
BYTE result = 0;
|
||||
|
||||
if (msg->Get()->uiNumRows > 0)
|
||||
{
|
||||
MYSQL_ROW row = mysql_fetch_row(msg->Get()->pSQLResult);
|
||||
|
||||
if ((row[0] && *row[0] && !strcasecmp(row[0], p->login)) || ((!row[0] || !*row[0]) && !strcmp("000000", p->login)))
|
||||
{
|
||||
char szQuery[QUERY_MAX_LEN];
|
||||
char escape_pwd[64];
|
||||
CDBManager::instance().EscapeString(escape_pwd, p->safebox_password, strlen(p->safebox_password));
|
||||
|
||||
snprintf(szQuery, sizeof(szQuery), "UPDATE safebox%s SET password='%s' WHERE account_id=%u", GetTablePostfix(), escape_pwd, p->account_id);
|
||||
|
||||
CDBManager::instance().ReturnQuery(szQuery, QID_SAFEBOX_CHANGE_PASSWORD_SECOND, pkPeer->GetHandle(), p);
|
||||
return;
|
||||
}
|
||||
result = UpdateSafeboxPassword(p->account_id, p->safebox_password) ? 1 : 0;
|
||||
}
|
||||
|
||||
delete p;
|
||||
|
||||
// Wrong old password
|
||||
pkPeer->EncodeHeader(DG::SAFEBOX_CHANGE_PASSWORD_ANSWER, dwHandle, sizeof(BYTE));
|
||||
pkPeer->EncodeBYTE(0);
|
||||
pkPeer->EncodeBYTE(result);
|
||||
}
|
||||
|
||||
void CClientManager::RESULT_SAFEBOX_CHANGE_PASSWORD_SECOND(CPeer * pkPeer, SQLMsg * msg)
|
||||
@@ -1841,21 +1946,10 @@ void CClientManager::UpdateLand(DWORD * pdw)
|
||||
// BLOCK_CHAT
|
||||
void CClientManager::BlockChat(TPacketBlockChat* p)
|
||||
{
|
||||
char szQuery[256];
|
||||
DWORD pid = 0;
|
||||
|
||||
if (g_stLocale == "sjis")
|
||||
snprintf(szQuery, sizeof(szQuery), "SELECT id FROM player%s WHERE name = '%s' collate sjis_japanese_ci", GetTablePostfix(), p->szName);
|
||||
else
|
||||
snprintf(szQuery, sizeof(szQuery), "SELECT id FROM player%s WHERE name = '%s'", GetTablePostfix(), p->szName);
|
||||
|
||||
auto pmsg = CDBManager::instance().DirectQuery(szQuery);
|
||||
SQLResult * pRes = pmsg->Get();
|
||||
|
||||
if (pRes->uiNumRows)
|
||||
if (FindPlayerIdByName(p->szName, &pid) && pid != 0)
|
||||
{
|
||||
MYSQL_ROW row = mysql_fetch_row(pRes->pSQLResult);
|
||||
DWORD pid = strtoul(row[0], NULL, 10);
|
||||
|
||||
TPacketGDAddAffect pa;
|
||||
pa.dwPID = pid;
|
||||
pa.elem.dwType = 223;
|
||||
|
||||
@@ -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 );
|
||||
|
||||
@@ -1,11 +1,181 @@
|
||||
|
||||
#include "stdafx.h"
|
||||
|
||||
#include <array>
|
||||
#include <string>
|
||||
#include "config.h"
|
||||
#include "char.h"
|
||||
#include "char_manager.h"
|
||||
#include "db.h"
|
||||
#include "guild_manager.h"
|
||||
#include "marriage.h"
|
||||
#include "libsql/Statement.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
struct PlayerIndexEmpireData
|
||||
{
|
||||
uint32_t accountId = 0;
|
||||
std::array<uint32_t, 4> playerIds = {};
|
||||
};
|
||||
|
||||
bool PrepareGameStmt(CStmt& stmt, const std::string& query)
|
||||
{
|
||||
CAsyncSQL* sql = DBManager::instance().GetDirectSQL();
|
||||
|
||||
if (!sql)
|
||||
{
|
||||
sys_err("game direct SQL handle is not initialized");
|
||||
return false;
|
||||
}
|
||||
|
||||
return stmt.Prepare(sql, query.c_str());
|
||||
}
|
||||
|
||||
bool LoadPlayerIndexEmpireData(uint32_t playerId, uint8_t empire, PlayerIndexEmpireData& data)
|
||||
{
|
||||
CStmt stmt;
|
||||
const std::string query = std::string("SELECT id, pid1, pid2, pid3, pid4 FROM player_index") + get_table_postfix()
|
||||
+ " WHERE (pid1=? OR pid2=? OR pid3=? OR pid4=?) AND empire=?";
|
||||
|
||||
if (!PrepareGameStmt(stmt, query))
|
||||
return false;
|
||||
|
||||
if (!stmt.BindParam(MYSQL_TYPE_LONG, &playerId)
|
||||
|| !stmt.BindParam(MYSQL_TYPE_LONG, &playerId)
|
||||
|| !stmt.BindParam(MYSQL_TYPE_LONG, &playerId)
|
||||
|| !stmt.BindParam(MYSQL_TYPE_LONG, &playerId)
|
||||
|| !stmt.BindParam(MYSQL_TYPE_TINY, &empire))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!stmt.BindResult(MYSQL_TYPE_LONG, &data.accountId)
|
||||
|| !stmt.BindResult(MYSQL_TYPE_LONG, &data.playerIds[0])
|
||||
|| !stmt.BindResult(MYSQL_TYPE_LONG, &data.playerIds[1])
|
||||
|| !stmt.BindResult(MYSQL_TYPE_LONG, &data.playerIds[2])
|
||||
|| !stmt.BindResult(MYSQL_TYPE_LONG, &data.playerIds[3]))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!stmt.Execute() || stmt.iRows == 0 || !stmt.Fetch())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LoadGuildIdByPlayerId(uint32_t playerId, uint32_t& guildId)
|
||||
{
|
||||
CStmt stmt;
|
||||
const std::string query = std::string("SELECT guild_id FROM guild_member") + get_table_postfix() + " WHERE pid=?";
|
||||
|
||||
guildId = 0;
|
||||
|
||||
if (!PrepareGameStmt(stmt, query))
|
||||
return false;
|
||||
|
||||
if (!stmt.BindParam(MYSQL_TYPE_LONG, &playerId)
|
||||
|| !stmt.BindResult(MYSQL_TYPE_LONG, &guildId))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!stmt.Execute())
|
||||
return false;
|
||||
|
||||
if (stmt.iRows == 0)
|
||||
return true;
|
||||
|
||||
return stmt.Fetch();
|
||||
}
|
||||
|
||||
bool UpdatePlayerIndexEmpire(uint32_t playerId, uint8_t currentEmpire, uint8_t newEmpire)
|
||||
{
|
||||
CStmt stmt;
|
||||
const std::string query = std::string("UPDATE player_index") + get_table_postfix()
|
||||
+ " SET empire=? WHERE (pid1=? OR pid2=? OR pid3=? OR pid4=?) AND empire=?";
|
||||
|
||||
if (!PrepareGameStmt(stmt, query))
|
||||
return false;
|
||||
|
||||
if (!stmt.BindParam(MYSQL_TYPE_TINY, &newEmpire)
|
||||
|| !stmt.BindParam(MYSQL_TYPE_LONG, &playerId)
|
||||
|| !stmt.BindParam(MYSQL_TYPE_LONG, &playerId)
|
||||
|| !stmt.BindParam(MYSQL_TYPE_LONG, &playerId)
|
||||
|| !stmt.BindParam(MYSQL_TYPE_LONG, &playerId)
|
||||
|| !stmt.BindParam(MYSQL_TYPE_TINY, ¤tEmpire))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return stmt.Execute();
|
||||
}
|
||||
|
||||
bool LoadChangeEmpireCountForAccount(uint32_t accountId, uint32_t& count, bool& found)
|
||||
{
|
||||
CStmt stmt;
|
||||
const std::string query = "SELECT change_count FROM change_empire WHERE account_id=?";
|
||||
|
||||
count = 0;
|
||||
found = false;
|
||||
|
||||
if (!PrepareGameStmt(stmt, query))
|
||||
return false;
|
||||
|
||||
if (!stmt.BindParam(MYSQL_TYPE_LONG, &accountId)
|
||||
|| !stmt.BindResult(MYSQL_TYPE_LONG, &count))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!stmt.Execute())
|
||||
return false;
|
||||
|
||||
if (stmt.iRows == 0)
|
||||
return true;
|
||||
|
||||
if (!stmt.Fetch())
|
||||
return false;
|
||||
|
||||
found = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool InsertChangeEmpireCount(uint32_t accountId, uint32_t count)
|
||||
{
|
||||
CStmt stmt;
|
||||
const std::string query = "INSERT INTO change_empire VALUES(?, ?, NOW())";
|
||||
|
||||
if (!PrepareGameStmt(stmt, query))
|
||||
return false;
|
||||
|
||||
if (!stmt.BindParam(MYSQL_TYPE_LONG, &accountId)
|
||||
|| !stmt.BindParam(MYSQL_TYPE_LONG, &count))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return stmt.Execute();
|
||||
}
|
||||
|
||||
bool UpdateChangeEmpireCount(uint32_t accountId, uint32_t count)
|
||||
{
|
||||
CStmt stmt;
|
||||
const std::string query = "UPDATE change_empire SET change_count=? WHERE account_id=?";
|
||||
|
||||
if (!PrepareGameStmt(stmt, query))
|
||||
return false;
|
||||
|
||||
if (!stmt.BindParam(MYSQL_TYPE_LONG, &count)
|
||||
|| !stmt.BindParam(MYSQL_TYPE_LONG, &accountId))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return stmt.Execute();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Return Value
|
||||
@@ -21,31 +191,12 @@ int CHARACTER::ChangeEmpire(BYTE empire)
|
||||
if (GetEmpire() == empire)
|
||||
return 1;
|
||||
|
||||
char szQuery[1024+1];
|
||||
DWORD dwAID;
|
||||
DWORD dwPID[4];
|
||||
memset(dwPID, 0, sizeof(dwPID));
|
||||
PlayerIndexEmpireData indexData;
|
||||
|
||||
{
|
||||
// 1. 내 계정의 모든 pid를 얻어 온다
|
||||
snprintf(szQuery, sizeof(szQuery),
|
||||
"SELECT id, pid1, pid2, pid3, pid4 FROM player_index%s WHERE pid1=%u OR pid2=%u OR pid3=%u OR pid4=%u AND empire=%u",
|
||||
get_table_postfix(), GetPlayerID(), GetPlayerID(), GetPlayerID(), GetPlayerID(), GetEmpire());
|
||||
|
||||
auto msg = DBManager::instance().DirectQuery(szQuery);
|
||||
|
||||
if (msg->Get()->uiNumRows == 0)
|
||||
{
|
||||
if (!LoadPlayerIndexEmpireData(GetPlayerID(), GetEmpire(), indexData))
|
||||
return 0;
|
||||
}
|
||||
|
||||
MYSQL_ROW row = mysql_fetch_row(msg->Get()->pSQLResult);
|
||||
|
||||
str_to_number(dwAID, row[0]);
|
||||
str_to_number(dwPID[0], row[1]);
|
||||
str_to_number(dwPID[1], row[2]);
|
||||
str_to_number(dwPID[2], row[3]);
|
||||
str_to_number(dwPID[3], row[4]);
|
||||
}
|
||||
|
||||
const int loop = 4;
|
||||
@@ -53,36 +204,21 @@ int CHARACTER::ChangeEmpire(BYTE empire)
|
||||
{
|
||||
// 2. 각 캐릭터의 길드 정보를 얻어온다.
|
||||
// 한 캐릭터라도 길드에 가입 되어 있다면, 제국 이동을 할 수 없다.
|
||||
DWORD dwGuildID[4];
|
||||
CGuild * pGuild[4];
|
||||
|
||||
for (int i = 0; i < loop; ++i)
|
||||
{
|
||||
snprintf(szQuery, sizeof(szQuery), "SELECT guild_id FROM guild_member%s WHERE pid=%u", get_table_postfix(), dwPID[i]);
|
||||
uint32_t guildId = 0;
|
||||
CGuild* guild = NULL;
|
||||
|
||||
auto pMsg = DBManager::instance().DirectQuery(szQuery);
|
||||
if (indexData.playerIds[i] == 0)
|
||||
continue;
|
||||
|
||||
if (pMsg != NULL)
|
||||
{
|
||||
if (pMsg->Get()->uiNumRows > 0)
|
||||
{
|
||||
MYSQL_ROW row = mysql_fetch_row(pMsg->Get()->pSQLResult);
|
||||
if (!LoadGuildIdByPlayerId(indexData.playerIds[i], guildId))
|
||||
return 0;
|
||||
|
||||
str_to_number(dwGuildID[i], row[0]);
|
||||
guild = CGuildManager::instance().FindGuild(guildId);
|
||||
|
||||
pGuild[i] = CGuildManager::instance().FindGuild(dwGuildID[i]);
|
||||
|
||||
if (pGuild[i] != NULL)
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
dwGuildID[i] = 0;
|
||||
pGuild[i] = NULL;
|
||||
}
|
||||
}
|
||||
if (guild != NULL)
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,19 +227,17 @@ int CHARACTER::ChangeEmpire(BYTE empire)
|
||||
// 한 캐릭터라도 결혼 상태라면 제국 이동을 할 수 없다.
|
||||
for (int i = 0; i < loop; ++i)
|
||||
{
|
||||
if (marriage::CManager::instance().IsEngagedOrMarried(dwPID[i]) == true)
|
||||
if (indexData.playerIds[i] == 0)
|
||||
continue;
|
||||
|
||||
if (marriage::CManager::instance().IsEngagedOrMarried(indexData.playerIds[i]) == true)
|
||||
return 3;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
// 4. db의 제국 정보를 업데이트 한다.
|
||||
snprintf(szQuery, sizeof(szQuery), "UPDATE player_index%s SET empire=%u WHERE pid1=%u OR pid2=%u OR pid3=%u OR pid4=%u AND empire=%u",
|
||||
get_table_postfix(), empire, GetPlayerID(), GetPlayerID(), GetPlayerID(), GetPlayerID(), GetEmpire());
|
||||
|
||||
auto msg = DBManager::instance().DirectQuery(szQuery);
|
||||
|
||||
if (msg->Get()->uiAffectedRows > 0)
|
||||
if (UpdatePlayerIndexEmpire(GetPlayerID(), GetEmpire(), empire))
|
||||
{
|
||||
// 5. 제국 변경 이력을 추가한다.
|
||||
SetChangeEmpireCount();
|
||||
@@ -117,82 +251,45 @@ int CHARACTER::ChangeEmpire(BYTE empire)
|
||||
|
||||
int CHARACTER::GetChangeEmpireCount() const
|
||||
{
|
||||
char szQuery[1024+1];
|
||||
DWORD dwAID = GetAID();
|
||||
uint32_t count = 0;
|
||||
bool found = false;
|
||||
const uint32_t accountId = GetAID();
|
||||
|
||||
if (dwAID == 0)
|
||||
if (accountId == 0)
|
||||
return 0;
|
||||
|
||||
snprintf(szQuery, sizeof(szQuery), "SELECT change_count FROM change_empire WHERE account_id = %u", dwAID);
|
||||
if (!LoadChangeEmpireCountForAccount(accountId, count, found) || !found)
|
||||
return 0;
|
||||
|
||||
auto pMsg = DBManager::instance().DirectQuery(szQuery);
|
||||
|
||||
if (pMsg != NULL)
|
||||
{
|
||||
if (pMsg->Get()->uiNumRows == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
MYSQL_ROW row = mysql_fetch_row(pMsg->Get()->pSQLResult);
|
||||
|
||||
DWORD count = 0;
|
||||
str_to_number(count, row[0]);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return count;
|
||||
}
|
||||
|
||||
void CHARACTER::SetChangeEmpireCount()
|
||||
{
|
||||
char szQuery[1024+1];
|
||||
const uint32_t accountId = GetAID();
|
||||
uint32_t count = 0;
|
||||
bool found = false;
|
||||
|
||||
DWORD dwAID = GetAID();
|
||||
if (accountId == 0)
|
||||
return;
|
||||
|
||||
if (dwAID == 0) return;
|
||||
if (!LoadChangeEmpireCountForAccount(accountId, count, found))
|
||||
return;
|
||||
|
||||
int count = GetChangeEmpireCount();
|
||||
++count;
|
||||
|
||||
if (count == 0)
|
||||
{
|
||||
count++;
|
||||
snprintf(szQuery, sizeof(szQuery), "INSERT INTO change_empire VALUES(%u, %d, NOW())", dwAID, count);
|
||||
}
|
||||
if (!found)
|
||||
InsertChangeEmpireCount(accountId, count);
|
||||
else
|
||||
{
|
||||
count++;
|
||||
snprintf(szQuery, sizeof(szQuery), "UPDATE change_empire SET change_count=%d WHERE account_id=%u", count, dwAID);
|
||||
}
|
||||
|
||||
DBManager::instance().DirectQuery(szQuery);
|
||||
UpdateChangeEmpireCount(accountId, count);
|
||||
}
|
||||
|
||||
DWORD CHARACTER::GetAID() const
|
||||
{
|
||||
char szQuery[1024+1];
|
||||
DWORD dwAID = 0;
|
||||
PlayerIndexEmpireData indexData;
|
||||
|
||||
snprintf(szQuery, sizeof(szQuery), "SELECT id FROM player_index%s WHERE pid1=%u OR pid2=%u OR pid3=%u OR pid4=%u AND empire=%u",
|
||||
get_table_postfix(), GetPlayerID(), GetPlayerID(), GetPlayerID(), GetPlayerID(), GetEmpire());
|
||||
|
||||
auto pMsg = DBManager::instance().DirectQuery(szQuery);
|
||||
|
||||
if (pMsg)
|
||||
{
|
||||
if (pMsg->Get()->uiNumRows == 0)
|
||||
return 0;
|
||||
|
||||
MYSQL_ROW row = mysql_fetch_row(pMsg->Get()->pSQLResult);
|
||||
|
||||
str_to_number(dwAID, row[0]);
|
||||
|
||||
return dwAID;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!LoadPlayerIndexEmpireData(GetPlayerID(), GetEmpire(), indexData))
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return indexData.accountId;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
#include "stdafx.h"
|
||||
#include <array>
|
||||
#include <cerrno>
|
||||
#include <cstdlib>
|
||||
#include <limits>
|
||||
#include <sstream>
|
||||
#ifndef OS_WINDOWS
|
||||
#include <ifaddrs.h>
|
||||
@@ -20,6 +24,161 @@
|
||||
|
||||
using std::string;
|
||||
|
||||
namespace
|
||||
{
|
||||
enum class ESqlConfigIndex : size_t
|
||||
{
|
||||
Account = 0,
|
||||
Player = 1,
|
||||
Common = 2,
|
||||
Count = 3
|
||||
};
|
||||
|
||||
struct SqlConnectionConfig
|
||||
{
|
||||
std::string host;
|
||||
std::string user;
|
||||
std::string password;
|
||||
std::string database;
|
||||
int port = 0;
|
||||
|
||||
bool IsConfigured() const
|
||||
{
|
||||
return !host.empty() && !user.empty() && !password.empty() && !database.empty();
|
||||
}
|
||||
};
|
||||
|
||||
bool ReadEnvString(const char* envName, std::string& output)
|
||||
{
|
||||
const char* envValue = std::getenv(envName);
|
||||
if (!envValue || !*envValue)
|
||||
return false;
|
||||
|
||||
output = envValue;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ReadEnvPort(const char* envName, int& output)
|
||||
{
|
||||
const char* envValue = std::getenv(envName);
|
||||
if (!envValue || !*envValue)
|
||||
return false;
|
||||
|
||||
errno = 0;
|
||||
char* end = nullptr;
|
||||
unsigned long parsed = std::strtoul(envValue, &end, 10);
|
||||
if (errno != 0 || end == envValue || *end != '\0' || parsed > std::numeric_limits<uint16_t>::max())
|
||||
{
|
||||
fprintf(stderr, "Invalid %s value: %s\n", envName, envValue);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
output = static_cast<int>(parsed);
|
||||
return true;
|
||||
}
|
||||
|
||||
void LogEnvOverride(const char* envName)
|
||||
{
|
||||
fprintf(stdout, "CONFIG: using %s override\n", envName);
|
||||
}
|
||||
|
||||
void ApplySqlEnvOverrides(const char* prefix, SqlConnectionConfig& config)
|
||||
{
|
||||
char envName[64];
|
||||
|
||||
snprintf(envName, sizeof(envName), "METIN2_%s_HOST", prefix);
|
||||
if (ReadEnvString(envName, config.host))
|
||||
LogEnvOverride(envName);
|
||||
|
||||
snprintf(envName, sizeof(envName), "METIN2_%s_USER", prefix);
|
||||
if (ReadEnvString(envName, config.user))
|
||||
LogEnvOverride(envName);
|
||||
|
||||
snprintf(envName, sizeof(envName), "METIN2_%s_PASSWORD", prefix);
|
||||
if (ReadEnvString(envName, config.password))
|
||||
LogEnvOverride(envName);
|
||||
|
||||
snprintf(envName, sizeof(envName), "METIN2_%s_DATABASE", prefix);
|
||||
if (ReadEnvString(envName, config.database))
|
||||
LogEnvOverride(envName);
|
||||
|
||||
snprintf(envName, sizeof(envName), "METIN2_%s_PORT", prefix);
|
||||
if (ReadEnvPort(envName, config.port))
|
||||
LogEnvOverride(envName);
|
||||
}
|
||||
|
||||
void ParseSqlConfigOrExit(const char* tokenName, const char* value, SqlConnectionConfig& config)
|
||||
{
|
||||
char host[64];
|
||||
char user[64];
|
||||
char password[64];
|
||||
char database[64];
|
||||
int port = 0;
|
||||
|
||||
*host = '\0';
|
||||
*user = '\0';
|
||||
*password = '\0';
|
||||
*database = '\0';
|
||||
|
||||
const char* line = two_arguments(value, host, sizeof(host), user, sizeof(user));
|
||||
line = two_arguments(line, password, sizeof(password), database, sizeof(database));
|
||||
|
||||
if (line[0])
|
||||
{
|
||||
char portBuffer[32];
|
||||
one_argument(line, portBuffer, sizeof(portBuffer));
|
||||
str_to_number(port, portBuffer);
|
||||
}
|
||||
|
||||
if (!*host || !*user || !*password || !*database)
|
||||
{
|
||||
fprintf(stderr, "%s syntax: %s <host user password db [port]>\n", tokenName, tokenName);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
config.host = host;
|
||||
config.user = user;
|
||||
config.password = password;
|
||||
config.database = database;
|
||||
config.port = port;
|
||||
}
|
||||
|
||||
void ValidateSqlConfigOrExit(const char* label, const SqlConnectionConfig& config)
|
||||
{
|
||||
if (config.IsConfigured())
|
||||
return;
|
||||
|
||||
fprintf(stderr, "%s must be configured as <host user password db [port]>\n", label);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
void ApplyAdminPagePasswordEnvOverride()
|
||||
{
|
||||
if (ReadEnvString("METIN2_ADMINPAGE_PASSWORD", g_stAdminPagePassword))
|
||||
LogEnvOverride("METIN2_ADMINPAGE_PASSWORD");
|
||||
}
|
||||
|
||||
void ValidateAdminPageConfigOrExit()
|
||||
{
|
||||
if (!IsAdminPageEnabled())
|
||||
{
|
||||
if (!g_stAdminPageIP.empty())
|
||||
{
|
||||
fprintf(stderr, "ADMIN_PAGE_PASSWORD must be configured when adminpage_ip is set\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
fprintf(stdout, "ADMIN_PAGE: disabled\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (g_stAdminPageIP.empty())
|
||||
fprintf(stdout, "ADMIN_PAGE: enabled without IP restriction\n");
|
||||
else
|
||||
fprintf(stdout, "ADMIN_PAGE: enabled for %zu IP entries\n", g_stAdminPageIP.size());
|
||||
}
|
||||
}
|
||||
|
||||
BYTE g_bChannel = 0;
|
||||
WORD mother_port = 50080;
|
||||
int passes_per_sec = 25;
|
||||
@@ -75,7 +234,7 @@ string g_stDefaultQuestObjectDir = "./quest/object";
|
||||
std::set<string> g_setQuestObjectDir;
|
||||
|
||||
std::vector<std::string> g_stAdminPageIP;
|
||||
std::string g_stAdminPagePassword = "SHOWMETHEMONEY";
|
||||
std::string g_stAdminPagePassword;
|
||||
|
||||
string g_stBlockDate = "30000705";
|
||||
|
||||
@@ -195,7 +354,12 @@ static void FN_log_adminpage()
|
||||
++iter;
|
||||
}
|
||||
|
||||
sys_log(1, "ADMIN_PAGE_PASSWORD = %s", g_stAdminPagePassword.c_str());
|
||||
sys_log(1, "ADMIN_PAGE_PASSWORD = %s", IsAdminPageEnabled() ? "[configured]" : "[disabled]");
|
||||
}
|
||||
|
||||
bool IsAdminPageEnabled()
|
||||
{
|
||||
return !g_stAdminPagePassword.empty();
|
||||
}
|
||||
|
||||
|
||||
@@ -340,35 +504,14 @@ void config_init(const string& st_localeServiceName)
|
||||
exit(1);
|
||||
}
|
||||
|
||||
char db_host[3][64], db_user[3][64], db_pwd[3][64], db_db[3][64];
|
||||
// ... 아... db_port는 이미 있는데... 네이밍 어찌해야함...
|
||||
int mysql_db_port[3];
|
||||
|
||||
for (int n = 0; n < 3; ++n)
|
||||
{
|
||||
*db_host[n] = '\0';
|
||||
*db_user[n] = '\0';
|
||||
*db_pwd[n]= '\0';
|
||||
*db_db[n]= '\0';
|
||||
mysql_db_port[n] = 0;
|
||||
}
|
||||
|
||||
char log_host[64], log_user[64], log_pwd[64], log_db[64];
|
||||
int log_port = 0;
|
||||
|
||||
*log_host = '\0';
|
||||
*log_user = '\0';
|
||||
*log_pwd = '\0';
|
||||
*log_db = '\0';
|
||||
std::array<SqlConnectionConfig, static_cast<size_t>(ESqlConfigIndex::Count)> dbConfig;
|
||||
SqlConnectionConfig logConfig;
|
||||
|
||||
|
||||
// DB에서 로케일정보를 세팅하기위해서는 다른 세팅값보다 선행되어서
|
||||
// DB정보만 읽어와 로케일 세팅을 한후 다른 세팅을 적용시켜야한다.
|
||||
// 이유는 로케일관련된 초기화 루틴이 곳곳에 존재하기 때문.
|
||||
|
||||
bool isCommonSQL = false;
|
||||
bool isPlayerSQL = false;
|
||||
|
||||
FILE* fp_common;
|
||||
if (!(fp_common = fopen("conf/game.txt", "r")))
|
||||
{
|
||||
@@ -381,96 +524,25 @@ void config_init(const string& st_localeServiceName)
|
||||
|
||||
TOKEN("account_sql")
|
||||
{
|
||||
const char* line = two_arguments(value_string, db_host[0], sizeof(db_host[0]), db_user[0], sizeof(db_user[0]));
|
||||
line = two_arguments(line, db_pwd[0], sizeof(db_pwd[0]), db_db[0], sizeof(db_db[0]));
|
||||
|
||||
if (line[0])
|
||||
{
|
||||
char buf[256];
|
||||
one_argument(line, buf, sizeof(buf));
|
||||
str_to_number(mysql_db_port[0], buf);
|
||||
}
|
||||
|
||||
if (!*db_host[0] || !*db_user[0] || !*db_pwd[0] || !*db_db[0])
|
||||
{
|
||||
fprintf(stderr, "PLAYER_SQL syntax: logsql <host user password db>\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
char buf[1024];
|
||||
snprintf(buf, sizeof(buf), "PLAYER_SQL: %s %s %s %s %d", db_host[0], db_user[0], db_pwd[0], db_db[0], mysql_db_port[0]);
|
||||
isPlayerSQL = true;
|
||||
ParseSqlConfigOrExit("account_sql", value_string, dbConfig[static_cast<size_t>(ESqlConfigIndex::Account)]);
|
||||
continue;
|
||||
}
|
||||
|
||||
TOKEN("player_sql")
|
||||
{
|
||||
const char* line = two_arguments(value_string, db_host[1], sizeof(db_host[1]), db_user[1], sizeof(db_user[1]));
|
||||
line = two_arguments(line, db_pwd[1], sizeof(db_pwd[1]), db_db[1], sizeof(db_db[1]));
|
||||
|
||||
if (line[0])
|
||||
{
|
||||
char buf[256];
|
||||
one_argument(line, buf, sizeof(buf));
|
||||
str_to_number(mysql_db_port[1], buf);
|
||||
}
|
||||
|
||||
if (!*db_host[1] || !*db_user[1] || !*db_pwd[1] || !*db_db[1])
|
||||
{
|
||||
fprintf(stderr, "PLAYER_SQL syntax: logsql <host user password db>\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
char buf[1024];
|
||||
snprintf(buf, sizeof(buf), "PLAYER_SQL: %s %s %s %s %d", db_host[1], db_user[1], db_pwd[1], db_db[1], mysql_db_port[1]);
|
||||
isPlayerSQL = true;
|
||||
ParseSqlConfigOrExit("player_sql", value_string, dbConfig[static_cast<size_t>(ESqlConfigIndex::Player)]);
|
||||
continue;
|
||||
}
|
||||
|
||||
TOKEN("common_sql")
|
||||
{
|
||||
const char* line = two_arguments(value_string, db_host[2], sizeof(db_host[2]), db_user[2], sizeof(db_user[2]));
|
||||
line = two_arguments(line, db_pwd[2], sizeof(db_pwd[2]), db_db[2], sizeof(db_db[2]));
|
||||
|
||||
if (line[0])
|
||||
{
|
||||
char buf[256];
|
||||
one_argument(line, buf, sizeof(buf));
|
||||
str_to_number(mysql_db_port[2], buf);
|
||||
}
|
||||
|
||||
if (!*db_host[2] || !*db_user[2] || !*db_pwd[2] || !*db_db[2])
|
||||
{
|
||||
fprintf(stderr, "COMMON_SQL syntax: logsql <host user password db>\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
char buf[1024];
|
||||
snprintf(buf, sizeof(buf), "COMMON_SQL: %s %s %s %s %d", db_host[2], db_user[2], db_pwd[2], db_db[2], mysql_db_port[2]);
|
||||
isCommonSQL = true;
|
||||
ParseSqlConfigOrExit("common_sql", value_string, dbConfig[static_cast<size_t>(ESqlConfigIndex::Common)]);
|
||||
continue;
|
||||
}
|
||||
|
||||
TOKEN("log_sql")
|
||||
{
|
||||
const char* line = two_arguments(value_string, log_host, sizeof(log_host), log_user, sizeof(log_user));
|
||||
line = two_arguments(line, log_pwd, sizeof(log_pwd), log_db, sizeof(log_db));
|
||||
|
||||
if (line[0])
|
||||
{
|
||||
char buf[256];
|
||||
one_argument(line, buf, sizeof(buf));
|
||||
str_to_number(log_port, buf);
|
||||
}
|
||||
|
||||
if (!*log_host || !*log_user || !*log_pwd || !*log_db)
|
||||
{
|
||||
fprintf(stderr, "LOG_SQL syntax: logsql <host user password db>\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
char buf[1024];
|
||||
snprintf(buf, sizeof(buf), "LOG_SQL: %s %s %s %s %d", log_host, log_user, log_pwd, log_db, log_port);
|
||||
ParseSqlConfigOrExit("log_sql", value_string, logConfig);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -753,6 +825,12 @@ void config_init(const string& st_localeServiceName)
|
||||
}
|
||||
fclose(fp_common);
|
||||
|
||||
ApplySqlEnvOverrides("ACCOUNT_SQL", dbConfig[static_cast<size_t>(ESqlConfigIndex::Account)]);
|
||||
ApplySqlEnvOverrides("PLAYER_SQL", dbConfig[static_cast<size_t>(ESqlConfigIndex::Player)]);
|
||||
ApplySqlEnvOverrides("COMMON_SQL", dbConfig[static_cast<size_t>(ESqlConfigIndex::Common)]);
|
||||
ApplySqlEnvOverrides("LOG_SQL", logConfig);
|
||||
ApplyAdminPagePasswordEnvOverride();
|
||||
|
||||
FILE* fpOnlyForDB;
|
||||
|
||||
if (!(fpOnlyForDB = fopen(st_configFileName.c_str(), "r")))
|
||||
@@ -814,31 +892,15 @@ void config_init(const string& st_localeServiceName)
|
||||
//처리가 끝났으니 파일을 닫자.
|
||||
fclose(fpOnlyForDB);
|
||||
|
||||
// CONFIG_SQL_INFO_ERROR
|
||||
if (!isCommonSQL)
|
||||
{
|
||||
puts("LOAD_COMMON_SQL_INFO_FAILURE:");
|
||||
puts("");
|
||||
puts("CONFIG:");
|
||||
puts("------------------------------------------------");
|
||||
puts("COMMON_SQL: HOST USER PASSWORD DATABASE");
|
||||
puts("");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (!isPlayerSQL)
|
||||
{
|
||||
puts("LOAD_PLAYER_SQL_INFO_FAILURE:");
|
||||
puts("");
|
||||
puts("CONFIG:");
|
||||
puts("------------------------------------------------");
|
||||
puts("PLAYER_SQL: HOST USER PASSWORD DATABASE");
|
||||
puts("");
|
||||
exit(1);
|
||||
}
|
||||
ValidateSqlConfigOrExit("COMMON_SQL", dbConfig[static_cast<size_t>(ESqlConfigIndex::Common)]);
|
||||
ValidateSqlConfigOrExit(g_bAuthServer ? "ACCOUNT_SQL" : "PLAYER_SQL", dbConfig[static_cast<size_t>(g_bAuthServer ? ESqlConfigIndex::Account : ESqlConfigIndex::Player)]);
|
||||
if (!g_bAuthServer)
|
||||
ValidateSqlConfigOrExit("LOG_SQL", logConfig);
|
||||
ValidateAdminPageConfigOrExit();
|
||||
|
||||
// Common DB 가 Locale 정보를 가지고 있기 때문에 가장 먼저 접속해야 한다.
|
||||
AccountDB::instance().Connect(db_host[2], mysql_db_port[2], db_user[2], db_pwd[2], db_db[2]);
|
||||
const SqlConnectionConfig& commonDb = dbConfig[static_cast<size_t>(ESqlConfigIndex::Common)];
|
||||
AccountDB::instance().Connect(commonDb.host.c_str(), commonDb.port, commonDb.user.c_str(), commonDb.password.c_str(), commonDb.database.c_str());
|
||||
|
||||
if (false == AccountDB::instance().IsConnected())
|
||||
{
|
||||
@@ -884,13 +946,14 @@ void config_init(const string& st_localeServiceName)
|
||||
|
||||
AccountDB::instance().SetLocale(g_stLocale);
|
||||
|
||||
AccountDB::instance().ConnectAsync(db_host[2], mysql_db_port[2], db_user[2], db_pwd[2], db_db[2], g_stLocale.c_str());
|
||||
AccountDB::instance().ConnectAsync(commonDb.host.c_str(), commonDb.port, commonDb.user.c_str(), commonDb.password.c_str(), commonDb.database.c_str(), g_stLocale.c_str());
|
||||
|
||||
// Player DB 접속
|
||||
const SqlConnectionConfig& playerDb = dbConfig[static_cast<size_t>(g_bAuthServer ? ESqlConfigIndex::Account : ESqlConfigIndex::Player)];
|
||||
if (g_bAuthServer)
|
||||
DBManager::instance().Connect(db_host[0], mysql_db_port[0], db_user[0], db_pwd[0], db_db[0]);
|
||||
DBManager::instance().Connect(playerDb.host.c_str(), playerDb.port, playerDb.user.c_str(), playerDb.password.c_str(), playerDb.database.c_str());
|
||||
else
|
||||
DBManager::instance().Connect(db_host[1], mysql_db_port[1], db_user[1], db_pwd[1], db_db[1]);
|
||||
DBManager::instance().Connect(playerDb.host.c_str(), playerDb.port, playerDb.user.c_str(), playerDb.password.c_str(), playerDb.database.c_str());
|
||||
|
||||
if (!DBManager::instance().IsConnected())
|
||||
{
|
||||
@@ -903,7 +966,7 @@ void config_init(const string& st_localeServiceName)
|
||||
if (false == g_bAuthServer) // 인증 서버가 아닐 경우
|
||||
{
|
||||
// Log DB 접속
|
||||
LogManager::instance().Connect(log_host, log_port, log_user, log_pwd, log_db);
|
||||
LogManager::instance().Connect(logConfig.host.c_str(), logConfig.port, logConfig.user.c_str(), logConfig.password.c_str(), logConfig.database.c_str());
|
||||
|
||||
if (!LogManager::instance().IsConnected())
|
||||
{
|
||||
@@ -1238,4 +1301,3 @@ bool IsValidFileCRC(DWORD dwCRC)
|
||||
return s_set_dwFileCRC.find(dwCRC) != s_set_dwFileCRC.end();
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ enum
|
||||
void config_init(const std::string& st_localeServiceName); // default "" is CONFIG
|
||||
|
||||
extern char sql_addr[256];
|
||||
bool IsAdminPageEnabled();
|
||||
|
||||
extern WORD mother_port;
|
||||
extern WORD p2p_port;
|
||||
@@ -106,4 +107,3 @@ extern int gPlayerMaxLevel;
|
||||
extern bool g_BlockCharCreation;
|
||||
|
||||
#endif /* __INC_METIN_II_GAME_CONFIG_H__ */
|
||||
|
||||
|
||||
440
src/game/db.cpp
440
src/game/db.cpp
@@ -1,4 +1,5 @@
|
||||
#include "stdafx.h"
|
||||
#include <array>
|
||||
#include <sstream>
|
||||
#include "common/length.h"
|
||||
|
||||
@@ -17,9 +18,267 @@
|
||||
#include "login_data.h"
|
||||
#include "locale_service.h"
|
||||
#include "spam.h"
|
||||
#include "libsql/Statement.h"
|
||||
|
||||
extern std::string g_stBlockDate;
|
||||
|
||||
namespace
|
||||
{
|
||||
struct AuthLoginData
|
||||
{
|
||||
char encryptedPassword[45 + 1] = {};
|
||||
char password[45 + 1] = {};
|
||||
char socialId[SOCIAL_ID_MAX_LEN + 1] = {};
|
||||
char status[ACCOUNT_STATUS_MAX_LEN + 1] = {};
|
||||
uint32_t accountId = 0;
|
||||
uint8_t notAvailable = 0;
|
||||
std::array<int, PREMIUM_MAX_NUM> premiumTimes = {};
|
||||
long long createTime = 0;
|
||||
};
|
||||
|
||||
bool IsChannelServiceLogin(const char* login)
|
||||
{
|
||||
return login && login[0] == '[';
|
||||
}
|
||||
|
||||
bool PrepareGameStmt(CStmt& stmt, const std::string& query)
|
||||
{
|
||||
CAsyncSQL* sql = DBManager::instance().GetDirectSQL();
|
||||
|
||||
if (!sql)
|
||||
{
|
||||
sys_err("game direct SQL handle is not initialized");
|
||||
return false;
|
||||
}
|
||||
|
||||
return stmt.Prepare(sql, query.c_str());
|
||||
}
|
||||
|
||||
bool CopyRequiredAuthString(const MYSQL_ROW row, int column, char* dst, size_t dstSize)
|
||||
{
|
||||
if (!row[column])
|
||||
{
|
||||
sys_err("error column %d", column);
|
||||
return false;
|
||||
}
|
||||
|
||||
strlcpy(dst, row[column], dstSize);
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void CopyOptionalAuthNumber(const char* src, T& value)
|
||||
{
|
||||
if (!src)
|
||||
return;
|
||||
|
||||
str_to_number(value, src);
|
||||
}
|
||||
|
||||
bool ParseAuthLoginRow(MYSQL_ROW row, AuthLoginData& auth)
|
||||
{
|
||||
int col = 0;
|
||||
|
||||
if (!CopyRequiredAuthString(row, col++, auth.encryptedPassword, sizeof(auth.encryptedPassword))
|
||||
|| !CopyRequiredAuthString(row, col++, auth.password, sizeof(auth.password))
|
||||
|| !CopyRequiredAuthString(row, col++, auth.socialId, sizeof(auth.socialId)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!row[col])
|
||||
{
|
||||
sys_err("error column %d", col);
|
||||
return false;
|
||||
}
|
||||
str_to_number(auth.accountId, row[col++]);
|
||||
|
||||
if (!CopyRequiredAuthString(row, col++, auth.status, sizeof(auth.status)))
|
||||
return false;
|
||||
|
||||
CopyOptionalAuthNumber(row[col++], auth.notAvailable);
|
||||
CopyOptionalAuthNumber(row[col++], auth.premiumTimes[PREMIUM_EXP]);
|
||||
CopyOptionalAuthNumber(row[col++], auth.premiumTimes[PREMIUM_ITEM]);
|
||||
CopyOptionalAuthNumber(row[col++], auth.premiumTimes[PREMIUM_SAFEBOX]);
|
||||
CopyOptionalAuthNumber(row[col++], auth.premiumTimes[PREMIUM_AUTOLOOT]);
|
||||
CopyOptionalAuthNumber(row[col++], auth.premiumTimes[PREMIUM_FISH_MIND]);
|
||||
CopyOptionalAuthNumber(row[col++], auth.premiumTimes[PREMIUM_MARRIAGE_FAST]);
|
||||
CopyOptionalAuthNumber(row[col++], auth.premiumTimes[PREMIUM_GOLD]);
|
||||
CopyOptionalAuthNumber(row[col], auth.createTime);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LoadAuthLoginData(const char* login, const char* passwd, AuthLoginData& auth, bool& found)
|
||||
{
|
||||
CStmt stmt;
|
||||
const bool channelServiceLogin = IsChannelServiceLogin(login);
|
||||
const std::string query = channelServiceLogin
|
||||
? "SELECT ?, password, social_id, id, status, availDt - NOW() > 0,"
|
||||
"UNIX_TIMESTAMP(silver_expire),"
|
||||
"UNIX_TIMESTAMP(gold_expire),"
|
||||
"UNIX_TIMESTAMP(safebox_expire),"
|
||||
"UNIX_TIMESTAMP(autoloot_expire),"
|
||||
"UNIX_TIMESTAMP(fish_mind_expire),"
|
||||
"UNIX_TIMESTAMP(marriage_fast_expire),"
|
||||
"UNIX_TIMESTAMP(money_drop_rate_expire),"
|
||||
"UNIX_TIMESTAMP(create_time)"
|
||||
" FROM account WHERE login=?"
|
||||
: "SELECT PASSWORD(?), password, social_id, id, status, availDt - NOW() > 0,"
|
||||
"UNIX_TIMESTAMP(silver_expire),"
|
||||
"UNIX_TIMESTAMP(gold_expire),"
|
||||
"UNIX_TIMESTAMP(safebox_expire),"
|
||||
"UNIX_TIMESTAMP(autoloot_expire),"
|
||||
"UNIX_TIMESTAMP(fish_mind_expire),"
|
||||
"UNIX_TIMESTAMP(marriage_fast_expire),"
|
||||
"UNIX_TIMESTAMP(money_drop_rate_expire),"
|
||||
"UNIX_TIMESTAMP(create_time)"
|
||||
" FROM account WHERE login=?";
|
||||
|
||||
found = false;
|
||||
|
||||
if (!PrepareGameStmt(stmt, query))
|
||||
return false;
|
||||
|
||||
if (!stmt.BindParam(MYSQL_TYPE_STRING, const_cast<char*>(passwd))
|
||||
|| !stmt.BindParam(MYSQL_TYPE_STRING, const_cast<char*>(login)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!stmt.BindResult(MYSQL_TYPE_STRING, auth.encryptedPassword, sizeof(auth.encryptedPassword))
|
||||
|| !stmt.BindResult(MYSQL_TYPE_STRING, auth.password, sizeof(auth.password))
|
||||
|| !stmt.BindResult(MYSQL_TYPE_STRING, auth.socialId, sizeof(auth.socialId))
|
||||
|| !stmt.BindResult(MYSQL_TYPE_LONG, &auth.accountId)
|
||||
|| !stmt.BindResult(MYSQL_TYPE_STRING, auth.status, sizeof(auth.status))
|
||||
|| !stmt.BindResult(MYSQL_TYPE_TINY, &auth.notAvailable)
|
||||
|| !stmt.BindResult(MYSQL_TYPE_LONG, &auth.premiumTimes[PREMIUM_EXP])
|
||||
|| !stmt.BindResult(MYSQL_TYPE_LONG, &auth.premiumTimes[PREMIUM_ITEM])
|
||||
|| !stmt.BindResult(MYSQL_TYPE_LONG, &auth.premiumTimes[PREMIUM_SAFEBOX])
|
||||
|| !stmt.BindResult(MYSQL_TYPE_LONG, &auth.premiumTimes[PREMIUM_AUTOLOOT])
|
||||
|| !stmt.BindResult(MYSQL_TYPE_LONG, &auth.premiumTimes[PREMIUM_FISH_MIND])
|
||||
|| !stmt.BindResult(MYSQL_TYPE_LONG, &auth.premiumTimes[PREMIUM_MARRIAGE_FAST])
|
||||
|| !stmt.BindResult(MYSQL_TYPE_LONG, &auth.premiumTimes[PREMIUM_GOLD])
|
||||
|| !stmt.BindResult(MYSQL_TYPE_LONGLONG, &auth.createTime))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!stmt.Execute())
|
||||
return false;
|
||||
|
||||
if (stmt.iRows == 0)
|
||||
return true;
|
||||
|
||||
if (!stmt.Fetch())
|
||||
return false;
|
||||
|
||||
found = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void FormatCreateDate(const AuthLoginData& auth, char* dst, size_t dstSize)
|
||||
{
|
||||
strlcpy(dst, "00000000", dstSize);
|
||||
|
||||
if (auth.createTime <= 0)
|
||||
return;
|
||||
|
||||
time_t createTime = static_cast<time_t>(auth.createTime);
|
||||
struct tm tmBuf;
|
||||
|
||||
if (!localtime_r(&createTime, &tmBuf))
|
||||
return;
|
||||
|
||||
strftime(dst, dstSize, "%Y%m%d", &tmBuf);
|
||||
}
|
||||
|
||||
void UpdateAccountLastPlay(uint32_t accountId)
|
||||
{
|
||||
CStmt stmt;
|
||||
const std::string query = "UPDATE account SET last_play=NOW() WHERE id=?";
|
||||
|
||||
if (!PrepareGameStmt(stmt, query))
|
||||
return;
|
||||
|
||||
if (!stmt.BindParam(MYSQL_TYPE_LONG, &accountId))
|
||||
return;
|
||||
|
||||
if (!stmt.Execute())
|
||||
{
|
||||
sys_err("failed to update last_play for account %u", accountId);
|
||||
}
|
||||
}
|
||||
|
||||
void FinalizeAuthLogin(LPDESC d, const char* login, const char* passwd, const AuthLoginData& auth, const char* logPrefix)
|
||||
{
|
||||
char createDate[256] = "00000000";
|
||||
|
||||
if (LC_IsEurope() || test_server)
|
||||
{
|
||||
FormatCreateDate(auth, createDate, sizeof(createDate));
|
||||
sys_log(0, "Create_Time %lld %s", auth.createTime, createDate);
|
||||
sys_log(0, "Block Time %d ", strncmp(createDate, g_stBlockDate.c_str(), 8));
|
||||
}
|
||||
|
||||
const int passwordDiff = strcmp(auth.encryptedPassword, auth.password);
|
||||
|
||||
if (passwordDiff)
|
||||
{
|
||||
RecordLoginFailure(d->GetHostName());
|
||||
LoginFailure(d, "WRONGPWD");
|
||||
sys_log(0, " WRONGPWD");
|
||||
return;
|
||||
}
|
||||
|
||||
if (auth.notAvailable)
|
||||
{
|
||||
LoginFailure(d, "NOTAVAIL");
|
||||
sys_log(0, " NOTAVAIL");
|
||||
return;
|
||||
}
|
||||
|
||||
if (DESC_MANAGER::instance().FindByLoginName(login))
|
||||
{
|
||||
LoginFailure(d, "ALREADY");
|
||||
sys_log(0, " ALREADY");
|
||||
return;
|
||||
}
|
||||
|
||||
if (strcmp(auth.status, "OK"))
|
||||
{
|
||||
LoginFailure(d, auth.status);
|
||||
sys_log(0, " STATUS: %s", auth.status);
|
||||
return;
|
||||
}
|
||||
|
||||
if (LC_IsEurope())
|
||||
{
|
||||
if (strncmp(createDate, g_stBlockDate.c_str(), 8) >= 0)
|
||||
{
|
||||
LoginFailure(d, "BLKLOGIN");
|
||||
sys_log(0, " BLKLOGIN");
|
||||
return;
|
||||
}
|
||||
|
||||
UpdateAccountLastPlay(auth.accountId);
|
||||
}
|
||||
|
||||
TAccountTable& r = d->GetAccountTable();
|
||||
int premiumTimes[PREMIUM_MAX_NUM];
|
||||
|
||||
r.id = auth.accountId;
|
||||
trim_and_lower(login, r.login, sizeof(r.login));
|
||||
strlcpy(r.passwd, passwd, sizeof(r.passwd));
|
||||
strlcpy(r.social_id, auth.socialId, sizeof(r.social_id));
|
||||
DESC_MANAGER::instance().ConnectAccount(r.login, d);
|
||||
ClearLoginFailure(d->GetHostName());
|
||||
|
||||
thecore_memcpy(premiumTimes, auth.premiumTimes.data(), sizeof(premiumTimes));
|
||||
DBManager::instance().LoginPrepare(d, premiumTimes);
|
||||
sys_log(0, "%s: SUCCESS %s", logPrefix, login);
|
||||
}
|
||||
}
|
||||
|
||||
DBManager::DBManager() : m_bIsConnect(false)
|
||||
{
|
||||
}
|
||||
@@ -232,6 +491,29 @@ void DBManager::LoginPrepare(LPDESC d, int * paiPremiumTimes)
|
||||
SendAuthLogin(d);
|
||||
}
|
||||
|
||||
void DBManager::AuthenticateLogin(LPDESC d, const char* login, const char* passwd)
|
||||
{
|
||||
AuthLoginData auth;
|
||||
bool found = false;
|
||||
const bool channelServiceLogin = IsChannelServiceLogin(login);
|
||||
|
||||
d->SetLogin(login);
|
||||
sys_log(0, "AUTH_LOGIN_DIRECT: START %u %p", d->GetLoginKey(), get_pointer(d));
|
||||
|
||||
if (channelServiceLogin)
|
||||
sys_log(0, "ChannelServiceLogin [%s]", login);
|
||||
|
||||
if (!LoadAuthLoginData(login, passwd, auth, found) || !found)
|
||||
{
|
||||
sys_log(0, " NOID");
|
||||
RecordLoginFailure(d->GetHostName());
|
||||
LoginFailure(d, "NOID");
|
||||
return;
|
||||
}
|
||||
|
||||
FinalizeAuthLogin(d, login, passwd, auth, "AUTH_LOGIN_DIRECT");
|
||||
}
|
||||
|
||||
void DBManager::AnalyzeReturnQuery(SQLMsg * pMsg)
|
||||
{
|
||||
CReturnQueryInfo * qi = (CReturnQueryInfo *) pMsg->pvUserData;
|
||||
@@ -263,162 +545,12 @@ void DBManager::AnalyzeReturnQuery(SQLMsg * pMsg)
|
||||
else
|
||||
{
|
||||
MYSQL_ROW row = mysql_fetch_row(pMsg->Get()->pSQLResult);
|
||||
int col = 0;
|
||||
AuthLoginData auth;
|
||||
|
||||
// PASSWORD('%s'), password, securitycode, social_id, id, status
|
||||
char szEncrytPassword[45 + 1];
|
||||
char szPassword[45 + 1];
|
||||
char szSocialID[SOCIAL_ID_MAX_LEN + 1];
|
||||
char szStatus[ACCOUNT_STATUS_MAX_LEN + 1];
|
||||
DWORD dwID = 0;
|
||||
if (ParseAuthLoginRow(row, auth))
|
||||
FinalizeAuthLogin(d, pinfo->login, pinfo->passwd, auth, "QID_AUTH_LOGIN");
|
||||
|
||||
if (!row[col])
|
||||
{
|
||||
sys_err("error column %d", col);
|
||||
M2_DELETE(pinfo);
|
||||
break;
|
||||
}
|
||||
|
||||
strlcpy(szEncrytPassword, row[col++], sizeof(szEncrytPassword));
|
||||
|
||||
if (!row[col])
|
||||
{
|
||||
sys_err("error column %d", col);
|
||||
M2_DELETE(pinfo);
|
||||
break;
|
||||
}
|
||||
|
||||
strlcpy(szPassword, row[col++], sizeof(szPassword));
|
||||
|
||||
if (!row[col])
|
||||
{
|
||||
sys_err("error column %d", col);
|
||||
M2_DELETE(pinfo);
|
||||
break;
|
||||
}
|
||||
|
||||
strlcpy(szSocialID, row[col++], sizeof(szSocialID));
|
||||
|
||||
if (!row[col])
|
||||
{
|
||||
sys_err("error column %d", col);
|
||||
M2_DELETE(pinfo);
|
||||
break;
|
||||
}
|
||||
|
||||
str_to_number(dwID, row[col++]);
|
||||
|
||||
if (!row[col])
|
||||
{
|
||||
sys_err("error column %d", col);
|
||||
M2_DELETE(pinfo);
|
||||
break;
|
||||
}
|
||||
|
||||
strlcpy(szStatus, row[col++], sizeof(szStatus));
|
||||
|
||||
BYTE bNotAvail = 0;
|
||||
str_to_number(bNotAvail, row[col++]);
|
||||
|
||||
int aiPremiumTimes[PREMIUM_MAX_NUM];
|
||||
memset(&aiPremiumTimes, 0, sizeof(aiPremiumTimes));
|
||||
|
||||
char szCreateDate[256] = "00000000";
|
||||
|
||||
if (!g_iUseLocale)
|
||||
{
|
||||
str_to_number(aiPremiumTimes[PREMIUM_EXP], row[col++]);
|
||||
str_to_number(aiPremiumTimes[PREMIUM_ITEM], row[col++]);
|
||||
str_to_number(aiPremiumTimes[PREMIUM_SAFEBOX], row[col++]);
|
||||
str_to_number(aiPremiumTimes[PREMIUM_AUTOLOOT], row[col++]);
|
||||
str_to_number(aiPremiumTimes[PREMIUM_FISH_MIND], row[col++]);
|
||||
str_to_number(aiPremiumTimes[PREMIUM_MARRIAGE_FAST], row[col++]);
|
||||
str_to_number(aiPremiumTimes[PREMIUM_GOLD], row[col++]);
|
||||
}
|
||||
else
|
||||
{
|
||||
str_to_number(aiPremiumTimes[PREMIUM_EXP], row[col++]);
|
||||
str_to_number(aiPremiumTimes[PREMIUM_ITEM], row[col++]);
|
||||
str_to_number(aiPremiumTimes[PREMIUM_SAFEBOX], row[col++]);
|
||||
str_to_number(aiPremiumTimes[PREMIUM_AUTOLOOT], row[col++]);
|
||||
str_to_number(aiPremiumTimes[PREMIUM_FISH_MIND], row[col++]);
|
||||
str_to_number(aiPremiumTimes[PREMIUM_MARRIAGE_FAST], row[col++]);
|
||||
str_to_number(aiPremiumTimes[PREMIUM_GOLD], row[col++]);
|
||||
|
||||
if (LC_IsEurope() || test_server)
|
||||
{
|
||||
long retValue = 0;
|
||||
str_to_number(retValue, row[col]);
|
||||
|
||||
time_t create_time = retValue;
|
||||
struct tm * tm1;
|
||||
tm1 = localtime(&create_time);
|
||||
strftime(szCreateDate, 255, "%Y%m%d", tm1);
|
||||
|
||||
sys_log(0, "Create_Time %d %s", retValue, szCreateDate);
|
||||
sys_log(0, "Block Time %d ", strncmp(szCreateDate, g_stBlockDate.c_str(), 8));
|
||||
}
|
||||
}
|
||||
|
||||
int nPasswordDiff = strcmp(szEncrytPassword, szPassword);
|
||||
|
||||
if (nPasswordDiff)
|
||||
{
|
||||
RecordLoginFailure(d->GetHostName());
|
||||
LoginFailure(d, "WRONGPWD");
|
||||
sys_log(0, " WRONGPWD");
|
||||
M2_DELETE(pinfo);
|
||||
}
|
||||
else if (bNotAvail)
|
||||
{
|
||||
LoginFailure(d, "NOTAVAIL");
|
||||
sys_log(0, " NOTAVAIL");
|
||||
M2_DELETE(pinfo);
|
||||
}
|
||||
else if (DESC_MANAGER::instance().FindByLoginName(pinfo->login))
|
||||
{
|
||||
LoginFailure(d, "ALREADY");
|
||||
sys_log(0, " ALREADY");
|
||||
M2_DELETE(pinfo);
|
||||
}
|
||||
else if (strcmp(szStatus, "OK"))
|
||||
{
|
||||
LoginFailure(d, szStatus);
|
||||
sys_log(0, " STATUS: %s", szStatus);
|
||||
M2_DELETE(pinfo);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (LC_IsEurope())
|
||||
{
|
||||
//stBlockData >= 0 == 날짜가 BlockDate 보다 미래
|
||||
if (strncmp(szCreateDate, g_stBlockDate.c_str(), 8) >= 0)
|
||||
{
|
||||
LoginFailure(d, "BLKLOGIN");
|
||||
sys_log(0, " BLKLOGIN");
|
||||
M2_DELETE(pinfo);
|
||||
break;
|
||||
}
|
||||
|
||||
char szQuery[1024];
|
||||
snprintf(szQuery, sizeof(szQuery), "UPDATE account SET last_play=NOW() WHERE id=%u", dwID);
|
||||
DBManager::instance().DirectQuery(szQuery);
|
||||
}
|
||||
|
||||
TAccountTable & r = d->GetAccountTable();
|
||||
|
||||
r.id = dwID;
|
||||
trim_and_lower(pinfo->login, r.login, sizeof(r.login));
|
||||
strlcpy(r.passwd, pinfo->passwd, sizeof(r.passwd));
|
||||
strlcpy(r.social_id, szSocialID, sizeof(r.social_id));
|
||||
DESC_MANAGER::instance().ConnectAccount(r.login, d);
|
||||
ClearLoginFailure(d->GetHostName());
|
||||
|
||||
LoginPrepare(d, aiPremiumTimes);
|
||||
M2_DELETE(pinfo);
|
||||
|
||||
sys_log(0, "QID_AUTH_LOGIN: SUCCESS %s", pinfo->login);
|
||||
}
|
||||
M2_DELETE(pinfo);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -81,8 +81,10 @@ class DBManager : public singleton<DBManager>
|
||||
void SendMoneyLog(BYTE type, DWORD vnum, int gold);
|
||||
|
||||
void LoginPrepare(LPDESC d, int * paiPremiumTimes = NULL);
|
||||
void AuthenticateLogin(LPDESC d, const char* login, const char* passwd);
|
||||
void SendAuthLogin(LPDESC d);
|
||||
void SendLoginPing(const char * c_pszLogin);
|
||||
CAsyncSQL* GetDirectSQL() { return &m_sql_direct; }
|
||||
|
||||
void InsertLoginData(CLoginData * pkLD);
|
||||
void DeleteLoginData(CLoginData * pkLD);
|
||||
|
||||
@@ -18,6 +18,9 @@
|
||||
#include "questmanager.h"
|
||||
#include "MarkManager.h"
|
||||
#include "MarkImage.h"
|
||||
#include "libsql/Statement.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
SGuildMember::SGuildMember(LPCHARACTER ch, BYTE grade, DWORD offer_exp)
|
||||
: pid(ch->GetPlayerID()), grade(grade), is_general(0), job(ch->GetJob()), level(ch->GetLevel()), offer_exp(offer_exp), name(ch->GetName())
|
||||
@@ -28,6 +31,148 @@
|
||||
|
||||
namespace
|
||||
{
|
||||
bool PrepareGameStmt(CStmt& stmt, const std::string& query)
|
||||
{
|
||||
CAsyncSQL* sql = DBManager::instance().GetDirectSQL();
|
||||
|
||||
if (!sql)
|
||||
{
|
||||
sys_err("game direct SQL handle is not initialized");
|
||||
return false;
|
||||
}
|
||||
|
||||
return stmt.Prepare(sql, query.c_str());
|
||||
}
|
||||
|
||||
bool InsertGuildComment(uint32_t guildId, const char* name, uint8_t notice, const char* content)
|
||||
{
|
||||
CStmt stmt;
|
||||
const std::string query = std::string("INSERT INTO guild_comment") + get_table_postfix()
|
||||
+ "(guild_id, name, notice, content, time) VALUES(?, ?, ?, ?, NOW())";
|
||||
|
||||
if (!PrepareGameStmt(stmt, query))
|
||||
return false;
|
||||
|
||||
if (!stmt.BindParam(MYSQL_TYPE_LONG, &guildId)
|
||||
|| !stmt.BindParam(MYSQL_TYPE_STRING, const_cast<char*>(name))
|
||||
|| !stmt.BindParam(MYSQL_TYPE_TINY, ¬ice)
|
||||
|| !stmt.BindParam(MYSQL_TYPE_STRING, const_cast<char*>(content)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return stmt.Execute();
|
||||
}
|
||||
|
||||
bool DeleteGuildComment(uint32_t guildId, uint32_t commentId, const char* nameFilter, unsigned long long& affectedRows)
|
||||
{
|
||||
CStmt stmt;
|
||||
const std::string query = nameFilter
|
||||
? std::string("DELETE FROM guild_comment") + get_table_postfix() + " WHERE id=? AND guild_id=? AND name=?"
|
||||
: std::string("DELETE FROM guild_comment") + get_table_postfix() + " WHERE id=? AND guild_id=?";
|
||||
|
||||
affectedRows = 0;
|
||||
|
||||
if (!PrepareGameStmt(stmt, query))
|
||||
return false;
|
||||
|
||||
if (!stmt.BindParam(MYSQL_TYPE_LONG, &commentId)
|
||||
|| !stmt.BindParam(MYSQL_TYPE_LONG, &guildId))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (nameFilter && !stmt.BindParam(MYSQL_TYPE_STRING, const_cast<char*>(nameFilter)))
|
||||
return false;
|
||||
|
||||
if (!stmt.Execute())
|
||||
return false;
|
||||
|
||||
affectedRows = stmt.GetAffectedRows();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PrepareGuildCommentList(CStmt& stmt, uint32_t guildId)
|
||||
{
|
||||
const std::string query = std::string("SELECT id, name, content FROM guild_comment") + get_table_postfix()
|
||||
+ " WHERE guild_id=? ORDER BY notice DESC, id DESC LIMIT " + std::to_string(GUILD_COMMENT_MAX_COUNT);
|
||||
|
||||
if (!PrepareGameStmt(stmt, query))
|
||||
return false;
|
||||
|
||||
return stmt.BindParam(MYSQL_TYPE_LONG, &guildId);
|
||||
}
|
||||
|
||||
bool InsertGuildRecord(const char* name, uint32_t masterPid, uint32_t& guildId)
|
||||
{
|
||||
CStmt stmt;
|
||||
const std::string query = std::string("INSERT INTO guild") + get_table_postfix()
|
||||
+ "(name, master, sp, level, exp, skill_point, skill) "
|
||||
+ "VALUES(?, ?, 1000, 1, 0, 0, '\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0')";
|
||||
|
||||
guildId = 0;
|
||||
|
||||
if (!PrepareGameStmt(stmt, query))
|
||||
return false;
|
||||
|
||||
if (!stmt.BindParam(MYSQL_TYPE_STRING, const_cast<char*>(name))
|
||||
|| !stmt.BindParam(MYSQL_TYPE_LONG, &masterPid)
|
||||
|| !stmt.Execute())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
guildId = static_cast<uint32_t>(stmt.GetInsertId());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool InsertGuildGrade(uint32_t guildId, int grade, const char* gradeName, int authFlag)
|
||||
{
|
||||
CStmt stmt;
|
||||
const std::string query = std::string("INSERT INTO guild_grade") + get_table_postfix() + " VALUES(?, ?, ?, ?)";
|
||||
|
||||
if (!PrepareGameStmt(stmt, query))
|
||||
return false;
|
||||
|
||||
if (!stmt.BindParam(MYSQL_TYPE_LONG, &guildId)
|
||||
|| !stmt.BindParam(MYSQL_TYPE_LONG, &grade)
|
||||
|| !stmt.BindParam(MYSQL_TYPE_STRING, const_cast<char*>(gradeName))
|
||||
|| !stmt.BindParam(MYSQL_TYPE_LONG, &authFlag))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return stmt.Execute();
|
||||
}
|
||||
|
||||
bool LoadGuildInviteLimit(uint32_t guildId, uint32_t& inviteLimit, bool& found)
|
||||
{
|
||||
CStmt stmt;
|
||||
const std::string query = "SELECT value FROM guild_invite_limit WHERE id=?";
|
||||
|
||||
inviteLimit = 0;
|
||||
found = false;
|
||||
|
||||
if (!PrepareGameStmt(stmt, query))
|
||||
return false;
|
||||
|
||||
if (!stmt.BindParam(MYSQL_TYPE_LONG, &guildId)
|
||||
|| !stmt.BindResult(MYSQL_TYPE_LONG, &inviteLimit)
|
||||
|| !stmt.Execute())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (stmt.iRows == 0)
|
||||
return true;
|
||||
|
||||
if (!stmt.Fetch())
|
||||
return false;
|
||||
|
||||
found = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
struct FGuildNameSender
|
||||
{
|
||||
FGuildNameSender(uint32_t id, const char* guild_name) : id(id), name(guild_name)
|
||||
@@ -74,22 +219,15 @@ CGuild::CGuild(TGuildCreateParameter & cp)
|
||||
m_data.grade_array[i].auth_flag = 0;
|
||||
}
|
||||
|
||||
auto pmsg = DBManager::instance().DirectQuery(
|
||||
"INSERT INTO guild%s(name, master, sp, level, exp, skill_point, skill) "
|
||||
"VALUES('%s', %u, 1000, 1, 0, 0, '\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0')",
|
||||
get_table_postfix(), m_data.name, m_data.master_pid);
|
||||
|
||||
// TODO if error occur?
|
||||
m_data.guild_id = pmsg->Get()->uiInsertID;
|
||||
InsertGuildRecord(m_data.name, m_data.master_pid, m_data.guild_id);
|
||||
|
||||
for (int i = 0; i < GUILD_GRADE_COUNT; ++i)
|
||||
{
|
||||
DBManager::instance().Query("INSERT INTO guild_grade%s VALUES(%u, %d, '%s', %d)",
|
||||
get_table_postfix(),
|
||||
m_data.guild_id,
|
||||
i + 1,
|
||||
m_data.grade_array[i].grade_name,
|
||||
m_data.grade_array[i].auth_flag);
|
||||
InsertGuildGrade(
|
||||
m_data.guild_id,
|
||||
i + 1,
|
||||
m_data.grade_array[i].grade_name,
|
||||
m_data.grade_array[i].auth_flag);
|
||||
}
|
||||
|
||||
ComputeGuildPoints();
|
||||
@@ -1042,25 +1180,18 @@ void CGuild::AddComment(LPCHARACTER ch, const std::string& str)
|
||||
return;
|
||||
}
|
||||
|
||||
char text[GUILD_COMMENT_MAX_LEN * 2 + 1];
|
||||
DBManager::instance().EscapeString(text, sizeof(text), str.c_str(), str.length());
|
||||
const uint8_t notice = (str[0] == '!') ? 1 : 0;
|
||||
|
||||
DBManager::instance().FuncAfterQuery(std::bind(&CGuild::RefreshCommentForce,this, ch->GetPlayerID()),
|
||||
"INSERT INTO guild_comment%s(guild_id, name, notice, content, time) VALUES(%u, '%s', %d, '%s', NOW())",
|
||||
get_table_postfix(), m_data.guild_id, ch->GetName(), (str[0] == '!') ? 1 : 0, text);
|
||||
if (InsertGuildComment(m_data.guild_id, ch->GetName(), notice, str.c_str()))
|
||||
RefreshCommentForce(ch->GetPlayerID());
|
||||
}
|
||||
|
||||
void CGuild::DeleteComment(LPCHARACTER ch, DWORD comment_id)
|
||||
{
|
||||
std::unique_ptr<SQLMsg> pmsg{};
|
||||
unsigned long long affectedRows = 0;
|
||||
const char* nameFilter = GetMember(ch->GetPlayerID())->grade == GUILD_LEADER_GRADE ? NULL : ch->GetName();
|
||||
|
||||
if (GetMember(ch->GetPlayerID())->grade == GUILD_LEADER_GRADE)
|
||||
pmsg = DBManager::instance().DirectQuery("DELETE FROM guild_comment%s WHERE id = %u AND guild_id = %u",get_table_postfix(), comment_id, m_data.guild_id);
|
||||
else
|
||||
pmsg = DBManager::instance().DirectQuery("DELETE FROM guild_comment%s WHERE id = %u AND guild_id = %u AND name = '%s'",get_table_postfix(), comment_id, m_data.guild_id, ch->GetName());
|
||||
|
||||
auto* res = pmsg ? pmsg->Get() : nullptr;
|
||||
if (!res || res->uiAffectedRows == 0 || res->uiAffectedRows == (uint32_t)-1)
|
||||
if (!DeleteGuildComment(m_data.guild_id, comment_id, nameFilter, affectedRows) || affectedRows == 0)
|
||||
ch->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<길드> 삭제할 수 없는 글입니다."));
|
||||
else
|
||||
RefreshCommentForce(ch->GetPlayerID());
|
||||
@@ -1074,45 +1205,55 @@ void CGuild::RefreshComment(LPCHARACTER ch)
|
||||
void CGuild::RefreshCommentForce(DWORD player_id)
|
||||
{
|
||||
LPCHARACTER ch = CHARACTER_MANAGER::instance().FindByPID(player_id);
|
||||
if (ch == NULL) {
|
||||
if (ch == NULL)
|
||||
return;
|
||||
}
|
||||
|
||||
auto pmsg = DBManager::instance().DirectQuery("SELECT id, name, content FROM guild_comment%s WHERE guild_id = %u ORDER BY notice DESC, id DESC LIMIT %d", get_table_postfix(), m_data.guild_id, GUILD_COMMENT_MAX_COUNT);
|
||||
|
||||
TPacketGCGuild pack;
|
||||
pack.header = GC::GUILD;
|
||||
pack.length = sizeof(pack)+1;
|
||||
pack.length = sizeof(pack) + 1;
|
||||
pack.subheader = GuildSub::GC::COMMENTS;
|
||||
|
||||
BYTE count = pmsg->Get()->uiNumRows;
|
||||
|
||||
LPDESC d = ch->GetDesc();
|
||||
|
||||
if (!d)
|
||||
return;
|
||||
|
||||
pack.length += (sizeof(DWORD)+CHARACTER_NAME_MAX_LEN+1+GUILD_COMMENT_MAX_LEN+1)*(WORD)count;
|
||||
d->BufferedPacket(&pack,sizeof(pack));
|
||||
d->BufferedPacket(&count, 1);
|
||||
CStmt stmt;
|
||||
uint32_t id = 0;
|
||||
char szName[CHARACTER_NAME_MAX_LEN + 1];
|
||||
char szContent[GUILD_COMMENT_MAX_LEN + 1];
|
||||
|
||||
memset(szName, 0, sizeof(szName));
|
||||
memset(szContent, 0, sizeof(szContent));
|
||||
|
||||
for (uint i = 0; i < pmsg->Get()->uiNumRows; i++)
|
||||
if (!PrepareGuildCommentList(stmt, m_data.guild_id)
|
||||
|| !stmt.BindResult(MYSQL_TYPE_LONG, &id)
|
||||
|| !stmt.BindResult(MYSQL_TYPE_STRING, szName, sizeof(szName))
|
||||
|| !stmt.BindResult(MYSQL_TYPE_STRING, szContent, sizeof(szContent))
|
||||
|| !stmt.Execute())
|
||||
{
|
||||
MYSQL_ROW row = mysql_fetch_row(pmsg->Get()->pSQLResult);
|
||||
DWORD id = strtoul(row[0], NULL, 10);
|
||||
return;
|
||||
}
|
||||
|
||||
strlcpy(szName, row[1], sizeof(szName));
|
||||
strlcpy(szContent, row[2], sizeof(szContent));
|
||||
BYTE count = static_cast<BYTE>(stmt.iRows);
|
||||
|
||||
pack.length += (sizeof(DWORD) + CHARACTER_NAME_MAX_LEN + 1 + GUILD_COMMENT_MAX_LEN + 1) * static_cast<WORD>(count);
|
||||
d->BufferedPacket(&pack, sizeof(pack));
|
||||
d->BufferedPacket(&count, 1);
|
||||
|
||||
for (BYTE i = 0; i < count; ++i)
|
||||
{
|
||||
memset(szName, 0, sizeof(szName));
|
||||
memset(szContent, 0, sizeof(szContent));
|
||||
|
||||
if (!stmt.Fetch())
|
||||
break;
|
||||
|
||||
d->BufferedPacket(&id, sizeof(id));
|
||||
d->BufferedPacket(szName, sizeof(szName));
|
||||
|
||||
if (i == pmsg->Get()->uiNumRows - 1)
|
||||
d->Packet(szContent, sizeof(szContent)); // 마지막 줄이면 보내기
|
||||
if (i == count - 1)
|
||||
d->Packet(szContent, sizeof(szContent));
|
||||
else
|
||||
d->BufferedPacket(szContent, sizeof(szContent));
|
||||
}
|
||||
@@ -2122,13 +2263,13 @@ CGuild::GuildJoinErrCode CGuild::VerifyGuildJoinableCondition( const LPCHARACTER
|
||||
}
|
||||
else if ( LC_IsBrazil() == true )
|
||||
{
|
||||
auto pMsg = DBManager::instance().DirectQuery("SELECT value FROM guild_invite_limit WHERE id=%d", GetID());
|
||||
uint32_t inviteLimit = 0;
|
||||
bool hasInviteLimit = false;
|
||||
|
||||
if ( pMsg->Get()->uiNumRows > 0 )
|
||||
if (LoadGuildInviteLimit(GetID(), inviteLimit, hasInviteLimit) && hasInviteLimit)
|
||||
{
|
||||
MYSQL_ROW row = mysql_fetch_row(pMsg->Get()->pSQLResult);
|
||||
time_t limit_time=0;
|
||||
str_to_number( limit_time, row[0] );
|
||||
limit_time = inviteLimit;
|
||||
|
||||
if (test_server)
|
||||
{
|
||||
@@ -2171,4 +2312,3 @@ void CGuild::SendGuildDataUpdateToAllMember(SQLMsg* pmsg)
|
||||
SendAllGradePacket(*iter);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,9 +15,55 @@
|
||||
#include "locale_service.h"
|
||||
#include "guild_manager.h"
|
||||
#include "MarkManager.h"
|
||||
#include "libsql/Statement.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace
|
||||
{
|
||||
bool PrepareGameStmt(CStmt& stmt, const std::string& query)
|
||||
{
|
||||
CAsyncSQL* sql = DBManager::instance().GetDirectSQL();
|
||||
|
||||
if (!sql)
|
||||
{
|
||||
sys_err("game direct SQL handle is not initialized");
|
||||
return false;
|
||||
}
|
||||
|
||||
return stmt.Prepare(sql, query.c_str());
|
||||
}
|
||||
|
||||
bool GuildNameExists(const char* guildName, bool& exists)
|
||||
{
|
||||
CStmt stmt;
|
||||
const std::string query = std::string("SELECT COUNT(*) FROM guild") + get_table_postfix() + " WHERE name=?";
|
||||
uint32_t count = 0;
|
||||
|
||||
exists = false;
|
||||
|
||||
if (!PrepareGameStmt(stmt, query))
|
||||
return false;
|
||||
|
||||
if (!stmt.BindParam(MYSQL_TYPE_STRING, const_cast<char*>(guildName))
|
||||
|| !stmt.BindResult(MYSQL_TYPE_LONG, &count))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!stmt.Execute())
|
||||
return false;
|
||||
|
||||
if (stmt.iRows == 0)
|
||||
return true;
|
||||
|
||||
if (!stmt.Fetch())
|
||||
return false;
|
||||
|
||||
exists = count != 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
struct FGuildNameSender
|
||||
{
|
||||
@@ -81,25 +127,20 @@ DWORD CGuildManager::CreateGuild(TGuildCreateParameter& gcp)
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto pmsg = DBManager::instance().DirectQuery("SELECT COUNT(*) FROM guild%s WHERE name = '%s'",
|
||||
get_table_postfix(), gcp.name);
|
||||
bool guildNameExists = false;
|
||||
|
||||
if (pmsg->Get()->uiNumRows > 0)
|
||||
{
|
||||
MYSQL_ROW row = mysql_fetch_row(pmsg->Get()->pSQLResult);
|
||||
|
||||
if (!(row[0] && row[0][0] == '0'))
|
||||
{
|
||||
gcp.master->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<길드> 이미 같은 이름의 길드가 있습니다."));
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
if (!GuildNameExists(gcp.name, guildNameExists))
|
||||
{
|
||||
gcp.master->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<길드> 길드를 생성할 수 없습니다."));
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (guildNameExists)
|
||||
{
|
||||
gcp.master->ChatPacket(CHAT_TYPE_INFO, LC_TEXT("<길드> 이미 같은 이름의 길드가 있습니다."));
|
||||
return 0;
|
||||
}
|
||||
|
||||
// new CGuild(gcp) queries guild tables and tell dbcache to notice other game servers.
|
||||
// other game server calls CGuildManager::LoadGuild to load guild.
|
||||
CGuild * pg = M2_NEW CGuild(gcp);
|
||||
@@ -956,4 +997,3 @@ void CGuildManager::ChangeMaster(DWORD dwGID)
|
||||
"SELECT 1");
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -18,19 +18,35 @@
|
||||
|
||||
extern time_t get_global_time();
|
||||
|
||||
bool IsEmptyAdminPage()
|
||||
namespace
|
||||
{
|
||||
return g_stAdminPageIP.empty();
|
||||
}
|
||||
|
||||
bool IsAdminPage(const char * ip)
|
||||
{
|
||||
for (size_t n = 0; n < g_stAdminPageIP.size(); ++n)
|
||||
bool IsEmptyAdminPage()
|
||||
{
|
||||
if (g_stAdminPageIP[n] == ip)
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
return g_stAdminPageIP.empty();
|
||||
}
|
||||
|
||||
bool IsAdminPage(const char * ip)
|
||||
{
|
||||
for (size_t n = 0; n < g_stAdminPageIP.size(); ++n)
|
||||
{
|
||||
if (g_stAdminPageIP[n] == ip)
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool HasAdminPageIpAccess(const char* ip)
|
||||
{
|
||||
if (!IsAdminPageEnabled())
|
||||
return false;
|
||||
|
||||
return IsEmptyAdminPage() || IsAdminPage(ip);
|
||||
}
|
||||
|
||||
bool IsAdminCommandAuthorized(LPDESC d)
|
||||
{
|
||||
return d->IsAdminMode();
|
||||
}
|
||||
}
|
||||
|
||||
CInputProcessor::CInputProcessor() : m_pPacketInfo(NULL), m_iBufferLeft(0)
|
||||
@@ -245,14 +261,15 @@ int CInputHandshake::HandleText(LPDESC d, const char * c_pData)
|
||||
stResult = "YES";
|
||||
}
|
||||
//else if (!stBuf.compare("SHOWMETHEMONEY"))
|
||||
else if (stBuf == g_stAdminPagePassword)
|
||||
else if (IsAdminPageEnabled() && stBuf == g_stAdminPagePassword)
|
||||
{
|
||||
const char* hostIp = inet_ntoa(d->GetAddr().sin_addr);
|
||||
if (!IsEmptyAdminPage())
|
||||
{
|
||||
if (!IsAdminPage(inet_ntoa(d->GetAddr().sin_addr)))
|
||||
if (!IsAdminPage(hostIp))
|
||||
{
|
||||
char szTmp[64];
|
||||
snprintf(szTmp, sizeof(szTmp), "WEBADMIN : Wrong Connector : %s", inet_ntoa(d->GetAddr().sin_addr));
|
||||
snprintf(szTmp, sizeof(szTmp), "WEBADMIN : Wrong Connector : %s", hostIp);
|
||||
stResult += szTmp;
|
||||
}
|
||||
else
|
||||
@@ -270,21 +287,14 @@ int CInputHandshake::HandleText(LPDESC d, const char * c_pData)
|
||||
else if (!stBuf.compare("USER_COUNT"))
|
||||
{
|
||||
char szTmp[64];
|
||||
const char* hostIp = inet_ntoa(d->GetAddr().sin_addr);
|
||||
|
||||
if (!IsEmptyAdminPage())
|
||||
if (!HasAdminPageIpAccess(hostIp))
|
||||
{
|
||||
if (!IsAdminPage(inet_ntoa(d->GetAddr().sin_addr)))
|
||||
{
|
||||
snprintf(szTmp, sizeof(szTmp), "WEBADMIN : Wrong Connector : %s", inet_ntoa(d->GetAddr().sin_addr));
|
||||
}
|
||||
if (IsAdminPageEnabled())
|
||||
snprintf(szTmp, sizeof(szTmp), "WEBADMIN : Wrong Connector : %s", hostIp);
|
||||
else
|
||||
{
|
||||
int iTotal;
|
||||
int * paiEmpireUserCount;
|
||||
int iLocal;
|
||||
DESC_MANAGER::instance().GetUserCount(iTotal, &paiEmpireUserCount, iLocal);
|
||||
snprintf(szTmp, sizeof(szTmp), "%d %d %d %d %d", iTotal, paiEmpireUserCount[1], paiEmpireUserCount[2], paiEmpireUserCount[3], iLocal);
|
||||
}
|
||||
strlcpy(szTmp, "WEBADMIN : Disabled", sizeof(szTmp));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -298,48 +308,68 @@ int CInputHandshake::HandleText(LPDESC d, const char * c_pData)
|
||||
}
|
||||
else if (!stBuf.compare("CHECK_P2P_CONNECTIONS"))
|
||||
{
|
||||
std::ostringstream oss(std::ostringstream::out);
|
||||
|
||||
oss << "P2P CONNECTION NUMBER : " << P2P_MANAGER::instance().GetDescCount() << "\n";
|
||||
std::string hostNames;
|
||||
P2P_MANAGER::Instance().GetP2PHostNames(hostNames);
|
||||
oss << hostNames;
|
||||
stResult = oss.str();
|
||||
TPacketGGCheckAwakeness packet;
|
||||
packet.header = GG::CHECK_AWAKENESS;
|
||||
packet.length = sizeof(packet);
|
||||
if (!IsAdminCommandAuthorized(d))
|
||||
stResult = "UNKNOWN";
|
||||
else
|
||||
{
|
||||
std::ostringstream oss(std::ostringstream::out);
|
||||
|
||||
P2P_MANAGER::instance().Send(&packet, sizeof(packet));
|
||||
oss << "P2P CONNECTION NUMBER : " << P2P_MANAGER::instance().GetDescCount() << "\n";
|
||||
std::string hostNames;
|
||||
P2P_MANAGER::Instance().GetP2PHostNames(hostNames);
|
||||
oss << hostNames;
|
||||
stResult = oss.str();
|
||||
TPacketGGCheckAwakeness packet;
|
||||
packet.header = GG::CHECK_AWAKENESS;
|
||||
packet.length = sizeof(packet);
|
||||
|
||||
P2P_MANAGER::instance().Send(&packet, sizeof(packet));
|
||||
}
|
||||
}
|
||||
else if (!stBuf.compare("PACKET_INFO"))
|
||||
{
|
||||
m_pMainPacketInfo->Log("packet_info.txt");
|
||||
stResult = "OK";
|
||||
if (!IsAdminCommandAuthorized(d))
|
||||
stResult = "UNKNOWN";
|
||||
else
|
||||
{
|
||||
m_pMainPacketInfo->Log("packet_info.txt");
|
||||
stResult = "OK";
|
||||
}
|
||||
}
|
||||
else if (!stBuf.compare("PROFILE"))
|
||||
{
|
||||
CProfiler::instance().Log("profile.txt");
|
||||
stResult = "OK";
|
||||
if (!IsAdminCommandAuthorized(d))
|
||||
stResult = "UNKNOWN";
|
||||
else
|
||||
{
|
||||
CProfiler::instance().Log("profile.txt");
|
||||
stResult = "OK";
|
||||
}
|
||||
}
|
||||
//gift notify delete command
|
||||
else if (!stBuf.compare(0,15,"DELETE_AWARDID "))
|
||||
{
|
||||
char szTmp[64];
|
||||
std::string msg = stBuf.substr(15,26); // item_award의 id범위?
|
||||
|
||||
TPacketDeleteAwardID p;
|
||||
p.dwID = (DWORD)(atoi(msg.c_str()));
|
||||
snprintf(szTmp,sizeof(szTmp),"Sent to DB cache to delete ItemAward, id: %d",p.dwID);
|
||||
//sys_log(0,"%d",p.dwID);
|
||||
// strlcpy(p.login, msg.c_str(), sizeof(p.login));
|
||||
db_clientdesc->DBPacket(GD::DELETE_AWARDID, 0, &p, sizeof(p));
|
||||
stResult += szTmp;
|
||||
if (!IsAdminCommandAuthorized(d))
|
||||
stResult = "UNKNOWN";
|
||||
else
|
||||
{
|
||||
char szTmp[64];
|
||||
std::string msg = stBuf.substr(15,26); // item_award의 id범위?
|
||||
|
||||
TPacketDeleteAwardID p;
|
||||
p.dwID = (DWORD)(atoi(msg.c_str()));
|
||||
snprintf(szTmp,sizeof(szTmp),"Sent to DB cache to delete ItemAward, id: %d",p.dwID);
|
||||
//sys_log(0,"%d",p.dwID);
|
||||
// strlcpy(p.login, msg.c_str(), sizeof(p.login));
|
||||
db_clientdesc->DBPacket(GD::DELETE_AWARDID, 0, &p, sizeof(p));
|
||||
stResult += szTmp;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
stResult = "UNKNOWN";
|
||||
|
||||
if (d->IsAdminMode())
|
||||
else
|
||||
{
|
||||
stResult = "UNKNOWN";
|
||||
|
||||
if (d->IsAdminMode())
|
||||
{
|
||||
// 어드민 명령들
|
||||
if (!stBuf.compare(0, 7, "NOTICE "))
|
||||
|
||||
@@ -233,50 +233,7 @@ void CInputAuth::Login(LPDESC d, const char * c_pData)
|
||||
|
||||
sys_log(0, "InputAuth::Login : key %u login %s", dwKey, login);
|
||||
|
||||
TPacketCGLogin3 * p = M2_NEW TPacketCGLogin3;
|
||||
thecore_memcpy(p, pinfo, sizeof(TPacketCGLogin3));
|
||||
|
||||
char szPasswd[PASSWD_MAX_LEN * 2 + 1];
|
||||
DBManager::instance().EscapeString(szPasswd, sizeof(szPasswd), passwd, strlen(passwd));
|
||||
|
||||
char szLogin[LOGIN_MAX_LEN * 2 + 1];
|
||||
DBManager::instance().EscapeString(szLogin, sizeof(szLogin), login, strlen(login));
|
||||
|
||||
// CHANNEL_SERVICE_LOGIN
|
||||
if (Login_IsInChannelService(szLogin))
|
||||
{
|
||||
sys_log(0, "ChannelServiceLogin [%s]", szLogin);
|
||||
|
||||
DBManager::instance().ReturnQuery(QID_AUTH_LOGIN, dwKey, p,
|
||||
"SELECT '%s',password,social_id,id,status,availDt - NOW() > 0,"
|
||||
"UNIX_TIMESTAMP(silver_expire),"
|
||||
"UNIX_TIMESTAMP(gold_expire),"
|
||||
"UNIX_TIMESTAMP(safebox_expire),"
|
||||
"UNIX_TIMESTAMP(autoloot_expire),"
|
||||
"UNIX_TIMESTAMP(fish_mind_expire),"
|
||||
"UNIX_TIMESTAMP(marriage_fast_expire),"
|
||||
"UNIX_TIMESTAMP(money_drop_rate_expire),"
|
||||
"UNIX_TIMESTAMP(create_time)"
|
||||
" FROM account WHERE login='%s'",
|
||||
|
||||
szPasswd, szLogin);
|
||||
}
|
||||
// END_OF_CHANNEL_SERVICE_LOGIN
|
||||
else
|
||||
{
|
||||
DBManager::instance().ReturnQuery(QID_AUTH_LOGIN, dwKey, p,
|
||||
"SELECT PASSWORD('%s'),password,social_id,id,status,availDt - NOW() > 0,"
|
||||
"UNIX_TIMESTAMP(silver_expire),"
|
||||
"UNIX_TIMESTAMP(gold_expire),"
|
||||
"UNIX_TIMESTAMP(safebox_expire),"
|
||||
"UNIX_TIMESTAMP(autoloot_expire),"
|
||||
"UNIX_TIMESTAMP(fish_mind_expire),"
|
||||
"UNIX_TIMESTAMP(marriage_fast_expire),"
|
||||
"UNIX_TIMESTAMP(money_drop_rate_expire),"
|
||||
"UNIX_TIMESTAMP(create_time)"
|
||||
" FROM account WHERE login='%s'",
|
||||
szPasswd, szLogin);
|
||||
}
|
||||
DBManager::instance().AuthenticateLogin(d, login, passwd);
|
||||
}
|
||||
|
||||
int CInputAuth::Analyze(LPDESC d, uint16_t wHeader, const char * c_pData)
|
||||
|
||||
@@ -11,10 +11,113 @@
|
||||
#include "char.h"
|
||||
#include "char_manager.h"
|
||||
#include "questmanager.h"
|
||||
#include "libsql/Statement.h"
|
||||
|
||||
static char __account[CHARACTER_NAME_MAX_LEN * 2 + 1];
|
||||
static char __companion[CHARACTER_NAME_MAX_LEN * 2 + 1];
|
||||
|
||||
namespace
|
||||
{
|
||||
bool PrepareMessengerStmt(CStmt& stmt, const std::string& query)
|
||||
{
|
||||
CAsyncSQL* sql = DBManager::instance().GetDirectSQL();
|
||||
|
||||
if (!sql)
|
||||
{
|
||||
sys_err("messenger direct SQL handle is not initialized");
|
||||
return false;
|
||||
}
|
||||
|
||||
return stmt.Prepare(sql, query.c_str());
|
||||
}
|
||||
|
||||
bool LoadMessengerCompanions(MessengerManager::keyA account, std::vector<std::string>& companions)
|
||||
{
|
||||
CStmt stmt;
|
||||
char companion[CHARACTER_NAME_MAX_LEN + 1];
|
||||
const std::string query = std::string("SELECT companion FROM messenger_list") + get_table_postfix() + " WHERE account=?";
|
||||
|
||||
memset(companion, 0, sizeof(companion));
|
||||
companions.clear();
|
||||
|
||||
if (!PrepareMessengerStmt(stmt, query))
|
||||
return false;
|
||||
|
||||
if (!stmt.BindParam(MYSQL_TYPE_STRING, const_cast<char*>(account.c_str()))
|
||||
|| !stmt.BindResult(MYSQL_TYPE_STRING, companion, sizeof(companion))
|
||||
|| !stmt.Execute())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < stmt.iRows; ++i)
|
||||
{
|
||||
memset(companion, 0, sizeof(companion));
|
||||
|
||||
if (!stmt.Fetch())
|
||||
return false;
|
||||
|
||||
companions.emplace_back(companion);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool InsertMessengerRelation(MessengerManager::keyA account, MessengerManager::keyA companion)
|
||||
{
|
||||
CStmt stmt;
|
||||
const std::string query = std::string("INSERT INTO messenger_list") + get_table_postfix() + " VALUES (?, ?)";
|
||||
|
||||
if (!PrepareMessengerStmt(stmt, query))
|
||||
return false;
|
||||
|
||||
if (!stmt.BindParam(MYSQL_TYPE_STRING, const_cast<char*>(account.c_str()))
|
||||
|| !stmt.BindParam(MYSQL_TYPE_STRING, const_cast<char*>(companion.c_str())))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return stmt.Execute();
|
||||
}
|
||||
|
||||
bool DeleteMessengerRelation(MessengerManager::keyA account, MessengerManager::keyA companion)
|
||||
{
|
||||
CStmt stmt;
|
||||
const std::string query = std::string("DELETE FROM messenger_list") + get_table_postfix()
|
||||
+ " WHERE (account=? AND companion=?) OR (account=? AND companion=?)";
|
||||
|
||||
if (!PrepareMessengerStmt(stmt, query))
|
||||
return false;
|
||||
|
||||
if (!stmt.BindParam(MYSQL_TYPE_STRING, const_cast<char*>(account.c_str()))
|
||||
|| !stmt.BindParam(MYSQL_TYPE_STRING, const_cast<char*>(companion.c_str()))
|
||||
|| !stmt.BindParam(MYSQL_TYPE_STRING, const_cast<char*>(companion.c_str()))
|
||||
|| !stmt.BindParam(MYSQL_TYPE_STRING, const_cast<char*>(account.c_str())))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return stmt.Execute();
|
||||
}
|
||||
|
||||
bool DeleteAllMessengerRelations(MessengerManager::keyA account)
|
||||
{
|
||||
CStmt stmt;
|
||||
const std::string query = std::string("DELETE FROM messenger_list") + get_table_postfix() + " WHERE account=? OR companion=?";
|
||||
|
||||
if (!PrepareMessengerStmt(stmt, query))
|
||||
return false;
|
||||
|
||||
if (!stmt.BindParam(MYSQL_TYPE_STRING, const_cast<char*>(account.c_str()))
|
||||
|| !stmt.BindParam(MYSQL_TYPE_STRING, const_cast<char*>(account.c_str())))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return stmt.Execute();
|
||||
}
|
||||
}
|
||||
|
||||
MessengerManager::MessengerManager()
|
||||
{
|
||||
}
|
||||
@@ -51,39 +154,26 @@ void MessengerManager::Login(MessengerManager::keyA account)
|
||||
if (account.compare(__account))
|
||||
return;
|
||||
|
||||
DBManager::instance().FuncQuery(std::bind(&MessengerManager::LoadList, this, std::placeholders::_1),
|
||||
"SELECT account, companion FROM messenger_list%s WHERE account='%s'", get_table_postfix(), account.c_str());
|
||||
|
||||
m_set_loginAccount.insert(account);
|
||||
LoadList(account);
|
||||
}
|
||||
|
||||
void MessengerManager::LoadList(SQLMsg * msg)
|
||||
void MessengerManager::LoadList(MessengerManager::keyA account)
|
||||
{
|
||||
if (NULL == msg)
|
||||
std::vector<std::string> companions;
|
||||
|
||||
if (!LoadMessengerCompanions(account, companions))
|
||||
return;
|
||||
|
||||
if (NULL == msg->Get())
|
||||
if (companions.empty())
|
||||
return;
|
||||
|
||||
if (msg->Get()->uiNumRows == 0)
|
||||
return;
|
||||
|
||||
std::string account;
|
||||
|
||||
sys_log(1, "Messenger::LoadList");
|
||||
|
||||
for (uint i = 0; i < msg->Get()->uiNumRows; ++i)
|
||||
for (const auto& companion : companions)
|
||||
{
|
||||
MYSQL_ROW row = mysql_fetch_row(msg->Get()->pSQLResult);
|
||||
|
||||
if (row[0] && row[1])
|
||||
{
|
||||
if (account.length() == 0)
|
||||
account = row[0];
|
||||
|
||||
m_Relation[row[0]].insert(row[1]);
|
||||
m_InverseRelation[row[1]].insert(row[0]);
|
||||
}
|
||||
m_Relation[account].insert(companion);
|
||||
m_InverseRelation[companion].insert(account);
|
||||
}
|
||||
|
||||
SendList(account);
|
||||
@@ -501,8 +591,7 @@ void MessengerManager::AddToList(MessengerManager::keyA account, MessengerManage
|
||||
|
||||
sys_log(0, "Messenger Add %s %s", account.c_str(), companion.c_str());
|
||||
|
||||
DBManager::instance().Query("INSERT INTO messenger_list%s VALUES ('%s', '%s')",
|
||||
get_table_postfix(), account.c_str(), companion.c_str());
|
||||
InsertMessengerRelation(account, companion);
|
||||
|
||||
__AddToList(account, companion);
|
||||
|
||||
@@ -568,9 +657,7 @@ void MessengerManager::RemoveFromList(MessengerManager::keyA account, MessengerM
|
||||
|
||||
sys_log(1, "Messenger Remove %s %s", account.c_str(), companion.c_str());
|
||||
|
||||
// Fix
|
||||
DBManager::instance().Query("DELETE FROM messenger_list%s WHERE (account='%s' AND companion = '%s') OR (account = '%s' AND companion = '%s')",
|
||||
get_table_postfix(), account.c_str(), companion.c_str(), companion.c_str(), account.c_str());
|
||||
DeleteMessengerRelation(account, companion);
|
||||
|
||||
// MR-3: Remove from messenger Fix
|
||||
LPCHARACTER ch = CHARACTER_MANAGER::instance().FindPC(account.c_str());
|
||||
@@ -601,8 +688,7 @@ void MessengerManager::RemoveAllList(keyA account)
|
||||
return;
|
||||
|
||||
/* SQL Data 삭제 */
|
||||
DBManager::instance().Query("DELETE FROM messenger_list%s WHERE account='%s' OR companion='%s'",
|
||||
get_table_postfix(), account.c_str(), account.c_str());
|
||||
DeleteAllMessengerRelations(account);
|
||||
|
||||
/* 내가 가지고있는 리스트 삭제 */
|
||||
for (std::set<keyT>::iterator iter = company.begin();
|
||||
@@ -739,4 +825,3 @@ void MessengerManager::SendLogout(MessengerManager::keyA account, MessengerManag
|
||||
d->BufferedPacket(&bLen, sizeof(BYTE));
|
||||
d->Packet(companion.c_str(), companion.size());
|
||||
}
|
||||
|
||||
|
||||
@@ -50,7 +50,7 @@ class MessengerManager : public singleton<MessengerManager>
|
||||
void SendLogin(keyA account, keyA companion);
|
||||
void SendLogout(keyA account, keyA companion);
|
||||
|
||||
void LoadList(SQLMsg * pmsg);
|
||||
void LoadList(keyA account);
|
||||
|
||||
void Destroy();
|
||||
|
||||
|
||||
@@ -19,9 +19,11 @@
|
||||
#include "desc_client.h"
|
||||
#include "messenger_manager.h"
|
||||
#include "log.h"
|
||||
#include "db.h"
|
||||
#include "utils.h"
|
||||
#include "unique_item.h"
|
||||
#include "mob_manager.h"
|
||||
#include "libsql/Statement.h"
|
||||
#include <cctype>
|
||||
|
||||
#undef sys_err
|
||||
@@ -31,6 +33,69 @@ extern int g_nPortalLimitTime;
|
||||
extern LPCLIENT_DESC db_clientdesc;
|
||||
const int ITEM_BROKEN_METIN_VNUM = 28960;
|
||||
|
||||
namespace
|
||||
{
|
||||
bool PrepareGameStmt(CStmt& stmt, const std::string& query)
|
||||
{
|
||||
CAsyncSQL* sql = DBManager::instance().GetDirectSQL();
|
||||
|
||||
if (!sql)
|
||||
{
|
||||
sys_err("game direct SQL handle is not initialized");
|
||||
return false;
|
||||
}
|
||||
|
||||
return stmt.Prepare(sql, query.c_str());
|
||||
}
|
||||
|
||||
bool CharacterNameExists(const char* name, bool& exists)
|
||||
{
|
||||
CStmt stmt;
|
||||
const std::string query = std::string("SELECT COUNT(*) FROM player") + get_table_postfix() + " WHERE name=?";
|
||||
uint32_t count = 0;
|
||||
|
||||
exists = false;
|
||||
|
||||
if (!PrepareGameStmt(stmt, query))
|
||||
return false;
|
||||
|
||||
if (!stmt.BindParam(MYSQL_TYPE_STRING, const_cast<char*>(name))
|
||||
|| !stmt.BindResult(MYSQL_TYPE_LONG, &count))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!stmt.Execute())
|
||||
return false;
|
||||
|
||||
if (stmt.iRows == 0)
|
||||
return true;
|
||||
|
||||
if (!stmt.Fetch())
|
||||
return false;
|
||||
|
||||
exists = count != 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UpdateCharacterName(uint32_t playerId, const char* name)
|
||||
{
|
||||
CStmt stmt;
|
||||
const std::string query = std::string("UPDATE player") + get_table_postfix() + " SET name=? WHERE id=?";
|
||||
|
||||
if (!PrepareGameStmt(stmt, query))
|
||||
return false;
|
||||
|
||||
if (!stmt.BindParam(MYSQL_TYPE_STRING, const_cast<char*>(name))
|
||||
|| !stmt.BindParam(MYSQL_TYPE_LONG, &playerId))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return stmt.Execute();
|
||||
}
|
||||
}
|
||||
|
||||
namespace quest
|
||||
{
|
||||
//
|
||||
@@ -2114,23 +2179,19 @@ teleport_area:
|
||||
return 1;
|
||||
}
|
||||
|
||||
char szQuery[1024];
|
||||
snprintf(szQuery, sizeof(szQuery), "SELECT COUNT(*) FROM player%s WHERE name='%s'", get_table_postfix(), szName);
|
||||
auto pmsg = DBManager::instance().DirectQuery(szQuery);
|
||||
bool characterNameExists = false;
|
||||
|
||||
if ( pmsg->Get()->uiNumRows > 0 )
|
||||
if (!CharacterNameExists(szName, characterNameExists))
|
||||
{
|
||||
MYSQL_ROW row = mysql_fetch_row(pmsg->Get()->pSQLResult);
|
||||
lua_pushnumber(L, 0);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int count = 0;
|
||||
str_to_number(count, row[0]);
|
||||
|
||||
// 이미 해당 이름을 가진 캐릭터가 있음
|
||||
if ( count != 0 )
|
||||
{
|
||||
lua_pushnumber(L, 3);
|
||||
return 1;
|
||||
}
|
||||
// 이미 해당 이름을 가진 캐릭터가 있음
|
||||
if (characterNameExists)
|
||||
{
|
||||
lua_pushnumber(L, 3);
|
||||
return 1;
|
||||
}
|
||||
|
||||
DWORD pid = ch->GetPlayerID();
|
||||
@@ -2144,8 +2205,11 @@ teleport_area:
|
||||
/* change_name_log */
|
||||
LogManager::instance().ChangeNameLog(pid, ch->GetName(), szName, ch->GetDesc()->GetHostName());
|
||||
|
||||
snprintf(szQuery, sizeof(szQuery), "UPDATE player%s SET name='%s' WHERE id=%u", get_table_postfix(), szName, pid);
|
||||
DBManager::instance().DirectQuery(szQuery);
|
||||
if (!UpdateCharacterName(pid, szName))
|
||||
{
|
||||
lua_pushnumber(L, 0);
|
||||
return 1;
|
||||
}
|
||||
|
||||
ch->SetNewName(szName);
|
||||
lua_pushnumber(L, 4);
|
||||
|
||||
@@ -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,26 @@ 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];
|
||||
}
|
||||
|
||||
unsigned long long CStmt::GetAffectedRows() const
|
||||
{
|
||||
if (!m_pkStmt)
|
||||
return 0;
|
||||
|
||||
return mysql_stmt_affected_rows(m_pkStmt);
|
||||
}
|
||||
|
||||
unsigned long long CStmt::GetInsertId() const
|
||||
{
|
||||
if (!m_pkStmt)
|
||||
return 0;
|
||||
|
||||
return mysql_stmt_insert_id(m_pkStmt);
|
||||
}
|
||||
|
||||
@@ -17,6 +17,9 @@ class CStmt
|
||||
bool BindResult(enum_field_types type, void * p, int iMaxLen=0);
|
||||
int Execute();
|
||||
bool Fetch();
|
||||
unsigned long GetResultLength(unsigned int index) const;
|
||||
unsigned long long GetAffectedRows() const;
|
||||
unsigned long long GetInsertId() const;
|
||||
|
||||
void Error(const char * c_pszMsg);
|
||||
|
||||
@@ -35,6 +38,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