mirror of
https://gitlab.com/Scribble/gdlenhanced.git
synced 2026-08-17 14:23:04 -04:00
Supports custom ban message (config option) on login with expiration time. Bans default to permanent, but can be set by number of seconds. Requires "banned" field in accounts table in db.
797 lines
21 KiB
C++
797 lines
21 KiB
C++
|
|
#include "StdAfx.h"
|
|
#include "Database2.h"
|
|
#include "DatabaseIO.h"
|
|
#include "SHA512.h"
|
|
|
|
DWORD64 g_RandomAdminPassword = 0; // this is used for the admin login
|
|
|
|
CDatabaseIO::CDatabaseIO()
|
|
{
|
|
blob_query_buffer = new BYTE[BLOB_QUERY_BUFFER_LENGTH];
|
|
}
|
|
|
|
CDatabaseIO::~CDatabaseIO()
|
|
{
|
|
SafeDeleteArray (blob_query_buffer);
|
|
}
|
|
|
|
bool CDatabaseIO::VerifyAccount(const char *username, const char *password, AccountInformation_t *pAccountInfo, int *pError)
|
|
{
|
|
*pError = DBIO_ERROR_NONE;
|
|
|
|
std::string usernameEscaped = g_pDB2->EscapeString(username);
|
|
|
|
std::string passwordSalt;
|
|
if (g_pDB2->Query("SELECT password_salt FROM accounts WHERE username='%s' LIMIT 1", usernameEscaped.c_str()))
|
|
{
|
|
CSQLResult *pResult = g_pDB2->GetResult();
|
|
if (pResult)
|
|
{
|
|
SQLResultRow_t ResultRow = pResult->FetchRow();
|
|
|
|
if (ResultRow)
|
|
{
|
|
passwordSalt = ResultRow[0];
|
|
delete pResult;
|
|
}
|
|
else
|
|
{
|
|
delete pResult;
|
|
|
|
*pError = VERIFYACCOUNT_ERROR_DOESNT_EXIST;
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
*pError = VERIFYACCOUNT_ERROR_QUERY_FAILED;
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
*pError = VERIFYACCOUNT_ERROR_QUERY_FAILED;
|
|
return false;
|
|
}
|
|
|
|
bool bIsAdmin = false;
|
|
if (!_stricmp(username, "admin"))
|
|
{
|
|
if (!strcmp(password, csprintf("%I64u", g_RandomAdminPassword)))
|
|
{
|
|
bIsAdmin = true;
|
|
//*pError = VERIFYACCOUNT_ERROR_BAD_LOGIN;
|
|
//return false;
|
|
}
|
|
|
|
//bIsAdmin = true;
|
|
}
|
|
|
|
std::string saltedPassword = std::string(password) + passwordSalt;
|
|
std::string hashedSaltedPassword = SHA512(saltedPassword.c_str(), saltedPassword.length()).substr(0, 64);
|
|
if (g_pDB2->Query("SELECT id, username, date_created, access, banned FROM accounts WHERE (username!='admin' AND username='%s' AND password='%s') OR (username='admin' AND %d) LIMIT 1", usernameEscaped.c_str(), hashedSaltedPassword.c_str(), bIsAdmin))
|
|
{
|
|
CSQLResult *pResult = g_pDB2->GetResult();
|
|
if (pResult)
|
|
{
|
|
SQLResultRow_t ResultRow = pResult->FetchRow();
|
|
|
|
if (ResultRow)
|
|
{
|
|
pAccountInfo->id = CSQLResult::SafeUInt(ResultRow[0]);
|
|
pAccountInfo->username = CSQLResult::SafeString(ResultRow[1]);
|
|
pAccountInfo->dateCreated = CSQLResult::SafeUInt(ResultRow[2]);
|
|
pAccountInfo->access = CSQLResult::SafeUInt(ResultRow[3]);
|
|
pAccountInfo->banned = CSQLResult::SafeInt(ResultRow[4]);
|
|
delete pResult;
|
|
}
|
|
else
|
|
{
|
|
delete pResult;
|
|
|
|
*pError = VERIFYACCOUNT_ERROR_BAD_LOGIN;
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
*pError = VERIFYACCOUNT_ERROR_QUERY_FAILED;
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
*pError = VERIFYACCOUNT_ERROR_QUERY_FAILED;
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CDatabaseIO::CreateAccount(const char *username, const char *password, int *pError, const char *ipaddress)
|
|
{
|
|
unsigned int usernameLength = (unsigned int)strlen(username);
|
|
if (usernameLength < MIN_USERNAME_LENGTH || usernameLength > MAX_USERNAME_LENGTH)
|
|
{
|
|
*pError = CREATEACCOUNT_ERROR_BAD_USERNAME;
|
|
return false;
|
|
}
|
|
|
|
// Check if username is valid characters.
|
|
for (char &c : std::string(username))
|
|
{
|
|
if (c >= 'a' && c <= 'z')
|
|
continue;
|
|
if (c >= 'A' && c <= 'Z')
|
|
continue;
|
|
if (c >= '0' && c <= '9')
|
|
continue;
|
|
if (c == '_')
|
|
continue;
|
|
if (c == '-')
|
|
continue;
|
|
|
|
*pError = CREATEACCOUNT_ERROR_BAD_USERNAME;
|
|
return false;
|
|
}
|
|
|
|
unsigned int passwordLength = (unsigned int)strlen(password);
|
|
if (passwordLength < MIN_PASSWORD_LENGTH || passwordLength > MAX_PASSWORD_LENGTH)
|
|
{
|
|
*pError = CREATEACCOUNT_ERROR_BAD_PASSWORD;
|
|
return false;
|
|
}
|
|
|
|
*pError = DBIO_ERROR_NONE;
|
|
|
|
std::string usernameEscaped = g_pDB2->EscapeString(username);
|
|
std::string ipaddressEscaped = g_pDB2->EscapeString(ipaddress);
|
|
|
|
// Create random salt
|
|
DWORD randomValue = Random::GenUInt(0, 100000);
|
|
std::string passwordSalt = SHA512(&randomValue, sizeof(randomValue)).substr(0, 16);
|
|
|
|
// Apply salt
|
|
std::string saltedPassword = password + passwordSalt;
|
|
std::string hashedSaltedPassword = SHA512(saltedPassword.c_str(), saltedPassword.length()).substr(0, 64);
|
|
|
|
uint64_t accountID = 0;
|
|
if (g_pDB2->Query("INSERT INTO accounts (username, password, password_salt, date_created, access, created_ip_address) VALUES ('%s', '%s', '%s', UNIX_TIMESTAMP(), 1, '%s')", usernameEscaped.c_str(), hashedSaltedPassword.c_str(), passwordSalt.c_str(), ipaddressEscaped.c_str()))
|
|
{
|
|
accountID = g_pDB2->GetInsertID();
|
|
|
|
if (!accountID)
|
|
{
|
|
*pError = CREATEACCOUNT_ERROR_QUERY_FAILED;
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
*pError = CREATEACCOUNT_ERROR_QUERY_FAILED;
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CDatabaseIO::UpdateBan(unsigned int account_id, bool ban)
|
|
{
|
|
return g_pDB2->Query("UPDATE accounts SET banned = %u WHERE id = %u", ban, account_id);
|
|
}
|
|
|
|
|
|
std::list<unsigned int> CDatabaseIO::GetWeeniesAt(unsigned int block_id)
|
|
{
|
|
std::list<unsigned int> results;
|
|
|
|
if (g_pDB2->Query("SELECT weenie_id FROM blocks WHERE block_id = %u", block_id))
|
|
{
|
|
CSQLResult *pQueryResult = g_pDB2->GetResult();
|
|
if (pQueryResult)
|
|
{
|
|
while (SQLResultRow_t Row = pQueryResult->FetchRow())
|
|
{
|
|
results.push_back(strtoul(Row[0], NULL, 10));
|
|
}
|
|
|
|
delete pQueryResult;
|
|
}
|
|
}
|
|
|
|
return results;
|
|
}
|
|
|
|
bool CDatabaseIO::AddOrUpdateWeenieToBlock(unsigned int weenie_id, unsigned int block_id)
|
|
{
|
|
return g_pDB2->Query("INSERT INTO blocks (weenie_id, block_id) VALUES (%u, %u) ON DUPLICATE KEY UPDATE block_id = %u", weenie_id, block_id, block_id);
|
|
}
|
|
|
|
bool CDatabaseIO::RemoveWeenieFromBlock(unsigned int weenie_id)
|
|
{
|
|
return g_pDB2->Query("DELETE FROM blocks WHERE weenie_id = %u", weenie_id);
|
|
}
|
|
|
|
std::list<CharacterDesc_t> CDatabaseIO::GetCharacterList(unsigned int account_id)
|
|
{
|
|
std::list<CharacterDesc_t> results;
|
|
|
|
if (g_pDB2->Query("SELECT account_id, weenie_id, name, date_created, instance_ts FROM characters WHERE account_id = %u", account_id))
|
|
{
|
|
CSQLResult *pQueryResult = g_pDB2->GetResult();
|
|
if (pQueryResult)
|
|
{
|
|
while (SQLResultRow_t Row = pQueryResult->FetchRow())
|
|
{
|
|
CharacterDesc_t entry;
|
|
entry.account_id = strtoul(Row[0], NULL, 10);
|
|
entry.weenie_id = strtoul(Row[1], NULL, 10);
|
|
entry.name = Row[2];
|
|
entry.date_created = strtoul(Row[3], NULL, 10);
|
|
entry.instance_ts = (WORD)strtoul(Row[4], NULL, 10);
|
|
results.push_back(entry);
|
|
}
|
|
|
|
delete pQueryResult;
|
|
}
|
|
}
|
|
|
|
return results;
|
|
}
|
|
|
|
std::list<CharacterSquelch_t> CDatabaseIO::GetCharacterSquelch(unsigned int character_id)
|
|
{
|
|
std::list<CharacterSquelch_t> results;
|
|
|
|
if (g_pDB2->Query("SELECT squelched_id, account_id, isip, isspeech, istell, iscombat, ismagic, isemote, isadvancement, isappraisal, isspellcasting, isallegiance, isfellowhip, iscombatenemy, isrecall, iscrafting FROM character_squelch WHERE character_id = %u", character_id))
|
|
{
|
|
CSQLResult *pQueryResult = g_pDB2->GetResult();
|
|
if (pQueryResult)
|
|
{
|
|
while (SQLResultRow_t Row = pQueryResult->FetchRow())
|
|
{
|
|
CharacterSquelch_t entry;
|
|
entry.squelched_id = strtoul(Row[0], NULL, 10);
|
|
entry.account_id = strtoul(Row[1], NULL, 10);
|
|
entry.isIp = Row[2];
|
|
entry.isSpeech = Row[3];
|
|
entry.isTell = Row[4];
|
|
entry.isCombat = Row[5];
|
|
entry.isMagic = Row[6];
|
|
entry.isEmote = Row[7];
|
|
entry.isAdvancement = Row[8];
|
|
entry.isAppraisal = Row[9];
|
|
entry.isSpellcasting = Row[10];
|
|
entry.isAllegiance = Row[11];
|
|
entry.isFellowship = Row[12];
|
|
entry.isCombatEnemy = Row[13];
|
|
entry.isRecall = Row[14];
|
|
entry.isCrafting = Row[15];
|
|
results.push_back(entry);
|
|
}
|
|
delete pQueryResult;
|
|
}
|
|
}
|
|
|
|
return results;
|
|
}
|
|
|
|
bool CDatabaseIO::SaveCharacterSquelch(unsigned int character_id, CharacterSquelch_t data)
|
|
{
|
|
return g_pDB2->Query("INSERT INTO character_squelch(character_id, squelched_id, account_id, isip, isspeech, istell, iscombat, ismagic, isemote, isadvancement, isappraisal, isspellcasting, isallegiance, isfellowhip, iscombatenemy, isrecall, iscrafting) VALUES"
|
|
"(%u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u) ON DUPLICATE KEY UPDATE "
|
|
"isip = %u, isspeech = %u, istell = %u, iscombat = %u, ismagic = %u, isemote = %u, isadvancement = %u, isappraisal = %u, isspellcasting = %u, isallegiance = %u, isfellowhip = %u, iscombatenemy = %u, isrecall = %u, iscrafting = %u;",
|
|
character_id, data.squelched_id, data.account_id, data.isIp, data.isSpeech, data.isTell, data.isCombat, data.isMagic, data.isEmote, data.isAdvancement,
|
|
data.isAppraisal, data.isSpellcasting, data.isAllegiance, data.isFellowship, data.isCombatEnemy, data.isRecall, data.isCrafting,
|
|
data.isIp, data.isSpeech, data.isTell, data.isCombat, data.isMagic, data.isEmote, data.isAdvancement,
|
|
data.isAppraisal, data.isSpellcasting, data.isAllegiance, data.isFellowship, data.isCombatEnemy, data.isRecall, data.isCrafting);
|
|
}
|
|
|
|
bool CDatabaseIO::RemoveCharacterSquelch(unsigned int character_id, CharacterSquelch_t data)
|
|
{
|
|
return g_pDB2->Query("DELETE FROM character_squelch WHERE character_id = %u AND squelched_id = %u;", character_id, data.squelched_id);
|
|
}
|
|
|
|
DWORD CDatabaseIO::GetPlayerAccountId(unsigned int character_id)
|
|
{
|
|
DWORD accountToSquelch = 0;
|
|
|
|
if (g_pDB2->Query("SELECT account_id FROM characters WHERE weenie_id = %u;", character_id))
|
|
{
|
|
CSQLResult *pQueryResult = g_pDB2->GetResult();
|
|
if (pQueryResult)
|
|
{
|
|
SQLResultRow_t Row = pQueryResult->FetchRow();
|
|
if (Row)
|
|
{
|
|
accountToSquelch = strtoul(Row[0], NULL, 10);
|
|
}
|
|
delete pQueryResult;
|
|
}
|
|
}
|
|
|
|
return accountToSquelch;
|
|
}
|
|
|
|
|
|
CharacterDesc_t CDatabaseIO::GetCharacterInfo(unsigned int weenie_id)
|
|
{
|
|
CharacterDesc_t result = { 0,0,"",0,0 };
|
|
|
|
if (g_pDB2->Query("SELECT account_id, weenie_id, name, date_created, instance_ts FROM characters WHERE weenie_id = %u;", weenie_id))
|
|
{
|
|
CSQLResult *pQueryResult = g_pDB2->GetResult();
|
|
if (pQueryResult)
|
|
{
|
|
SQLResultRow_t Row = pQueryResult->FetchRow();
|
|
if (Row)
|
|
{
|
|
result.account_id = strtoul(Row[0], NULL, 10);
|
|
result.weenie_id = strtoul(Row[1], NULL, 10);
|
|
result.name = Row[2];
|
|
result.date_created = strtoul(Row[3], NULL, 10);
|
|
result.instance_ts = (WORD)strtoul(Row[4], NULL, 10);
|
|
}
|
|
|
|
delete pQueryResult;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
bool CDatabaseIO::CreateCharacter(unsigned int account_id, unsigned int weenie_id, const char *name)
|
|
{
|
|
return g_pDB2->Query("INSERT INTO characters (account_id, weenie_id, name, date_created, instance_ts) VALUES (%u, %u, '%s', UNIX_TIMESTAMP(), 1)",
|
|
account_id, weenie_id, g_pDB2->EscapeString(name).c_str());
|
|
}
|
|
|
|
bool CDatabaseIO::DeleteCharacter(unsigned int weenie_id)
|
|
{
|
|
return g_pDB2->Query("DELETE FROM characters WHERE weenie_id = %u", weenie_id);
|
|
}
|
|
|
|
bool CDatabaseIO::SetCharacterInstanceTS(unsigned int weenie_id, unsigned int instance_ts)
|
|
{
|
|
return g_pDB2->Query("UPDATE characters SET instance_ts = %u WHERE weenie_id = %u", instance_ts, weenie_id);
|
|
}
|
|
|
|
std::list< std::pair<unsigned int, unsigned int> >
|
|
CDatabaseIO::GetUnusedIdRanges(unsigned int min_range, unsigned int max_range)
|
|
{
|
|
std::list< std::pair<unsigned int, unsigned int> > result;
|
|
|
|
const char *szQuery =
|
|
"SELECT w.id + 1 AS l, (SELECT id FROM weenies WHERE id > w.id ORDER BY id LIMIT 1) AS u "
|
|
"FROM weenies w "
|
|
"LEFT JOIN weenies l ON l.id = w.id + 1 "
|
|
"WHERE w.id > %u AND w.id < %u AND l.id IS NULL "
|
|
"ORDER BY w.id "
|
|
"LIMIT 1000;";
|
|
|
|
if (g_pDBDynamicIDs->Query(szQuery, min_range, max_range))
|
|
{
|
|
CSQLResult *pRes = g_pDBDynamicIDs->GetResult();
|
|
if (pRes)
|
|
{
|
|
while (SQLResultRow_t row = pRes->FetchRow())
|
|
{
|
|
unsigned int lower = strtoul(row[0], NULL, 10);
|
|
unsigned int upper = max_range;
|
|
if (row[1])
|
|
upper = strtoul(row[1], NULL, 10);
|
|
|
|
result.push_back(std::pair<unsigned int, unsigned int>(lower, upper));
|
|
}
|
|
|
|
delete pRes;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
bool CDatabaseIO::IDRangeTableExistsAndValid()
|
|
{
|
|
bool retval = false;
|
|
|
|
if (g_pDB2->Query("SELECT unused FROM idranges WHERE unused > 2147999999 LIMIT 1"))
|
|
{
|
|
CSQLResult *pQueryResult = g_pDB2->GetResult();
|
|
if (pQueryResult)
|
|
{
|
|
delete pQueryResult;
|
|
retval = true;
|
|
}
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
|
|
|
|
std::list<unsigned int> CDatabaseIO::GetNextIDRange(unsigned int rangeStart, unsigned int count)
|
|
{
|
|
std::list< unsigned int> result;
|
|
|
|
const char *szQuery = "SELECT unused FROM idranges WHERE unused > %u limit %u;";
|
|
|
|
if (g_pDB2->Query(szQuery, rangeStart, count))
|
|
{
|
|
CSQLResult *pRes = g_pDB2->GetResult();
|
|
if (pRes)
|
|
{
|
|
while (SQLResultRow_t row = pRes->FetchRow())
|
|
{
|
|
unsigned int newId = strtoul(row[0], NULL, 10);
|
|
|
|
result.push_back(newId);
|
|
}
|
|
|
|
delete pRes;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
unsigned int CDatabaseIO::GetHighestWeenieID(unsigned int min_range, unsigned int max_range)
|
|
{
|
|
unsigned int result = min_range;
|
|
|
|
if (g_pDB2->Query(csprintf("SELECT id FROM weenies WHERE id >= %u AND id < %u ORDER BY id DESC LIMIT 1", min_range, max_range)))
|
|
{
|
|
CSQLResult *pQueryResult = g_pDB2->GetResult();
|
|
if (pQueryResult)
|
|
{
|
|
if (SQLResultRow_t Row = pQueryResult->FetchRow())
|
|
result = strtoul(Row[0], NULL, 10);
|
|
|
|
delete pQueryResult;
|
|
return result;
|
|
}
|
|
}
|
|
|
|
SERVER_ERROR << "Could not get highest weenie ID for range" << min_range << "-" << max_range << ", returning" << min_range;
|
|
return result;
|
|
}
|
|
|
|
bool CDatabaseIO::IsCharacterNameOpen(const char *name)
|
|
{
|
|
bool bIsOpen = false;
|
|
|
|
if (g_pDB2->Query("SELECT weenie_id FROM characters WHERE name='%s'", g_pDB2->EscapeString(name).c_str()))
|
|
{
|
|
CSQLResult *pQueryResult = g_pDB2->GetResult();
|
|
if (pQueryResult)
|
|
{
|
|
bIsOpen = (pQueryResult->ResultRows() == 0);
|
|
delete pQueryResult;
|
|
}
|
|
}
|
|
|
|
return bIsOpen;
|
|
}
|
|
|
|
bool CDatabaseIO::IsPlayerCharacter(unsigned int weenie_id)
|
|
{
|
|
bool isPlayer = false;
|
|
if (g_pDB2->Query("SELECT weenie_id FROM characters WHERE weenie_id='%u'", weenie_id))
|
|
{
|
|
CSQLResult *pQueryResult = g_pDB2->GetResult();
|
|
if (pQueryResult)
|
|
{
|
|
isPlayer = (pQueryResult->ResultRows() > 0);
|
|
delete pQueryResult;
|
|
}
|
|
}
|
|
|
|
return isPlayer;
|
|
}
|
|
|
|
DWORD CDatabaseIO::GetPlayerCharacterId(const char *name)
|
|
{
|
|
DWORD result = 0;
|
|
if (g_pDB2->Query("SELECT weenie_id FROM characters WHERE name='%s'", g_pDB2->EscapeString(name).c_str()))
|
|
{
|
|
CSQLResult *pQueryResult = g_pDB2->GetResult();
|
|
if (pQueryResult)
|
|
{
|
|
if (SQLResultRow_t Row = pQueryResult->FetchRow())
|
|
result = strtoul(Row[0], NULL, 10);
|
|
|
|
delete pQueryResult;
|
|
return result;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
std::string CDatabaseIO::GetPlayerCharacterName(DWORD weenie_id)
|
|
{
|
|
std::string result;
|
|
if (g_pDB2->Query("SELECT name FROM characters WHERE weenie_id='%u'", weenie_id))
|
|
{
|
|
CSQLResult *pQueryResult = g_pDB2->GetResult();
|
|
if (pQueryResult)
|
|
{
|
|
if (SQLResultRow_t Row = pQueryResult->FetchRow())
|
|
result = Row[0];
|
|
|
|
delete pQueryResult;
|
|
return result;
|
|
}
|
|
}
|
|
return "";
|
|
}
|
|
|
|
/*
|
|
bool CDatabaseIO::CreateOrUpdateWeenie(unsigned int weenie_id, unsigned int top_level_object_id, unsigned int block_id, void *data, unsigned int data_length)
|
|
{
|
|
// synchronous, deprecated
|
|
return CreateOrUpdateWeenie((MYSQL *)g_pDB2->GetInternalConnection(), weenie_id, top_level_object_id, block_id, data, data_length);
|
|
}
|
|
*/
|
|
|
|
bool CDatabaseIO::CreateOrUpdateWeenie(unsigned int weenie_id, unsigned int top_level_object_id, unsigned int block_id, void *data, unsigned int data_length)
|
|
{
|
|
IncrementPendingSave(weenie_id);
|
|
g_pDB2->QueueAsyncQuery(new CMYSQLSaveWeenieQuery(weenie_id, top_level_object_id, block_id, data, data_length));
|
|
return true;
|
|
}
|
|
|
|
bool CDatabaseIO::GetWeenie(unsigned int weenie_id, unsigned int *top_level_object_id, unsigned int *block_id, void **data, unsigned long *data_length)
|
|
{
|
|
// this is so bad...
|
|
while (GetNumPendingSaves(weenie_id))
|
|
Sleep(0);
|
|
|
|
MYSQL *sql = (MYSQL *)g_pDB2->GetInternalConnection();
|
|
if (!sql)
|
|
return false;
|
|
|
|
MYSQL_STMT *statement = mysql_stmt_init(sql);
|
|
|
|
const char *query_string = csprintf("SELECT top_level_object_id, block_id, data FROM weenies WHERE id = %u", weenie_id);
|
|
mysql_stmt_prepare(statement, query_string, (unsigned long) strlen(query_string));
|
|
|
|
MYSQL_BIND binding[3];
|
|
memset(binding, 0, sizeof(binding));
|
|
|
|
binding[0].buffer = top_level_object_id;
|
|
binding[0].buffer_length = sizeof(unsigned int);
|
|
binding[0].buffer_type = MYSQL_TYPE_LONG;
|
|
binding[0].is_unsigned = true;
|
|
|
|
binding[1].buffer = block_id;
|
|
binding[1].buffer_length = sizeof(unsigned int);
|
|
binding[1].buffer_type = MYSQL_TYPE_LONG;
|
|
binding[1].is_unsigned = true;
|
|
|
|
binding[2].buffer = blob_query_buffer;
|
|
binding[2].buffer_length = BLOB_QUERY_BUFFER_LENGTH;
|
|
binding[2].buffer_type = MYSQL_TYPE_BLOB;
|
|
binding[2].length = data_length;
|
|
|
|
mysql_stmt_bind_result(statement, binding);
|
|
|
|
if (mysql_stmt_execute(statement) || mysql_stmt_store_result(statement))
|
|
{
|
|
SERVER_ERROR << "Error on GetWeenie:" << mysql_error(sql);
|
|
mysql_stmt_close(statement);
|
|
return false;
|
|
}
|
|
|
|
if (mysql_stmt_fetch(statement))
|
|
{
|
|
SERVER_WARN << "GetWeenie failed because weenie" << weenie_id << "doesn't exist!\n%s\n";
|
|
|
|
mysql_stmt_free_result(statement);
|
|
mysql_stmt_close(statement);
|
|
return false;
|
|
}
|
|
|
|
*data = blob_query_buffer;
|
|
|
|
mysql_stmt_free_result(statement);
|
|
mysql_stmt_close(statement);
|
|
return true;
|
|
}
|
|
|
|
bool CDatabaseIO::DeleteWeenie(unsigned int weenie_id)
|
|
{
|
|
return g_pDB2->Query("DELETE FROM weenies WHERE id = %u OR top_level_object_id = %u", weenie_id, weenie_id);
|
|
}
|
|
|
|
bool CDatabaseIO::IsWeenieInDatabase(unsigned int weenie_id)
|
|
{
|
|
bool isWeenieInDatabase = false;
|
|
if (g_pDB2->Query("SELECT id FROM weenies WHERE id ='%u'", weenie_id))
|
|
{
|
|
CSQLResult *pQueryResult = g_pDB2->GetResult();
|
|
if (pQueryResult)
|
|
{
|
|
isWeenieInDatabase = (pQueryResult->ResultRows() > 0);
|
|
delete pQueryResult;
|
|
}
|
|
}
|
|
|
|
return isWeenieInDatabase;
|
|
}
|
|
|
|
bool CDatabaseIO::CreateOrUpdateGlobalData(DBIOGlobalDataID id, void *data, unsigned long data_length)
|
|
{
|
|
MYSQL *sql = (MYSQL *)g_pDB2->GetInternalConnection();
|
|
if (!sql)
|
|
return false;
|
|
|
|
MYSQL_STMT *statement = mysql_stmt_init(sql);
|
|
|
|
const char *query_string = csprintf("REPLACE INTO globals (id, data) VALUES (%u, ?)", id);
|
|
mysql_stmt_prepare(statement, query_string, (unsigned long)strlen(query_string));
|
|
|
|
MYSQL_BIND data_param;
|
|
memset(&data_param, 0, sizeof(data_param));
|
|
data_param.buffer = data;
|
|
data_param.buffer_length = data_length;
|
|
data_param.buffer_type = MYSQL_TYPE_BLOB;
|
|
mysql_stmt_bind_param(statement, &data_param);
|
|
|
|
bool bErrored = false;
|
|
if (mysql_stmt_execute(statement))
|
|
{
|
|
SERVER_ERROR << "Error on CreateOrUpdateGlobalData for" << id << ":" << mysql_error(sql);
|
|
bErrored = true;
|
|
}
|
|
|
|
mysql_stmt_close(statement);
|
|
return !bErrored;
|
|
}
|
|
|
|
bool CDatabaseIO::GetGlobalData(DBIOGlobalDataID id, void **data, unsigned long *data_length)
|
|
{
|
|
MYSQL *sql = (MYSQL *)g_pDB2->GetInternalConnection();
|
|
if (!sql)
|
|
return false;
|
|
|
|
MYSQL_STMT *statement = mysql_stmt_init(sql);
|
|
|
|
const char *query_string = csprintf("SELECT data FROM globals WHERE id = %u", id);
|
|
mysql_stmt_prepare(statement, query_string, (unsigned long)strlen(query_string));
|
|
|
|
MYSQL_BIND binding[1];
|
|
memset(binding, 0, sizeof(binding));
|
|
|
|
binding[0].buffer = blob_query_buffer;
|
|
binding[0].buffer_length = BLOB_QUERY_BUFFER_LENGTH;
|
|
binding[0].buffer_type = MYSQL_TYPE_BLOB;
|
|
binding[0].length = data_length;
|
|
|
|
mysql_stmt_bind_result(statement, binding);
|
|
|
|
if (mysql_stmt_execute(statement) || mysql_stmt_store_result(statement))
|
|
{
|
|
SERVER_ERROR << "Error on GetGlobalData:" << mysql_error(sql);
|
|
mysql_stmt_close(statement);
|
|
return false;
|
|
}
|
|
|
|
if (mysql_stmt_fetch(statement))
|
|
{
|
|
DEBUG_DATA << "GetGlobalData failed because id" << id << "doesn't exist!\n" << query_string;
|
|
|
|
mysql_stmt_free_result(statement);
|
|
mysql_stmt_close(statement);
|
|
return false;
|
|
}
|
|
|
|
*data = blob_query_buffer;
|
|
|
|
mysql_stmt_free_result(statement);
|
|
mysql_stmt_close(statement);
|
|
return true;
|
|
}
|
|
|
|
bool CDatabaseIO::CreateOrUpdateHouseData(unsigned int house_id, void *data, unsigned int data_length)
|
|
{
|
|
IncrementPendingSave(house_id);
|
|
g_pDB2->QueueAsyncQuery(new CMYSQLSaveHouseQuery(house_id, data, data_length));
|
|
return true;
|
|
}
|
|
|
|
bool CDatabaseIO::GetHouseData(unsigned int house_id, void **data, unsigned long *data_length)
|
|
{
|
|
// this is so bad...
|
|
while (GetNumPendingSaves(house_id))
|
|
Sleep(0);
|
|
|
|
MYSQL *sql = (MYSQL *)g_pDB2->GetInternalConnection();
|
|
if (!sql)
|
|
return false;
|
|
|
|
MYSQL_STMT *statement = mysql_stmt_init(sql);
|
|
|
|
const char *query_string = csprintf("SELECT data FROM houses WHERE house_id = %u", house_id);
|
|
mysql_stmt_prepare(statement, query_string, (unsigned long)strlen(query_string));
|
|
|
|
MYSQL_BIND binding[1];
|
|
memset(binding, 0, sizeof(binding));
|
|
|
|
binding[0].buffer = blob_query_buffer;
|
|
binding[0].buffer_length = BLOB_QUERY_BUFFER_LENGTH;
|
|
binding[0].buffer_type = MYSQL_TYPE_BLOB;
|
|
binding[0].length = data_length;
|
|
|
|
mysql_stmt_bind_result(statement, binding);
|
|
|
|
if (mysql_stmt_execute(statement) || mysql_stmt_store_result(statement))
|
|
{
|
|
SERVER_ERROR << "Error on GetHouseData:" << mysql_error(sql);
|
|
mysql_stmt_close(statement);
|
|
return false;
|
|
}
|
|
|
|
if (mysql_stmt_fetch(statement))
|
|
{
|
|
mysql_stmt_free_result(statement);
|
|
mysql_stmt_close(statement);
|
|
return false;
|
|
}
|
|
|
|
*data = blob_query_buffer;
|
|
|
|
mysql_stmt_free_result(statement);
|
|
mysql_stmt_close(statement);
|
|
return true;
|
|
}
|
|
|
|
// CLockable _pendingSavesLock;
|
|
// std::unordered_map<DWORD, DWORD> _pendingSaves;
|
|
|
|
void CDatabaseIO::IncrementPendingSave(DWORD weenie_id)
|
|
{
|
|
_pendingSavesLock.Lock();
|
|
|
|
std::unordered_map<DWORD, DWORD>::iterator i = _pendingSaves.find(weenie_id);
|
|
if (i != _pendingSaves.end())
|
|
{
|
|
i->second++;
|
|
}
|
|
else
|
|
{
|
|
_pendingSaves.insert(std::pair<DWORD, DWORD>(weenie_id, 1));
|
|
}
|
|
|
|
_pendingSavesLock.Unlock();
|
|
}
|
|
|
|
void CDatabaseIO::DecrementPendingSave(DWORD weenie_id)
|
|
{
|
|
_pendingSavesLock.Lock();
|
|
|
|
std::unordered_map<DWORD, DWORD>::iterator i = _pendingSaves.find(weenie_id);
|
|
if (i != _pendingSaves.end())
|
|
{
|
|
if (i->second <= 1)
|
|
_pendingSaves.erase(i);
|
|
else
|
|
i->second--;
|
|
}
|
|
|
|
_pendingSavesLock.Unlock();
|
|
}
|
|
|
|
DWORD CDatabaseIO::GetNumPendingSaves(DWORD weenie_id)
|
|
{
|
|
_pendingSavesLock.Lock();
|
|
std::unordered_map<DWORD, DWORD>::iterator i = _pendingSaves.find(weenie_id);
|
|
|
|
DWORD numSaves = 0;
|
|
if (i != _pendingSaves.end())
|
|
{
|
|
numSaves = i->second;
|
|
}
|
|
|
|
_pendingSavesLock.Unlock();
|
|
return numSaves;
|
|
}
|
|
|